├── .gitignore ├── EveDoesntFrontRun.cfg ├── ForceMove.pdf ├── ForceMove.tla ├── README.md ├── Success.cfg ├── Utils.tla ├── Version1.tla ├── Version1NoCounter.tla ├── Version2.tla ├── Version2NoGrief.tla ├── Version3.tla ├── v1-no-counter.txt ├── v1-problems.txt └── v2-no-grief.txt /.gitignore: -------------------------------------------------------------------------------- 1 | # The tla+ toolbox creates a .toolbox folder for each specification you create 2 | # While this folder does contain model details, which we potentially want to check 3 | # in, it also contains a lot of other files, so we ignore it for the moment 4 | *.toolbox/ 5 | 6 | # The tlatex utility produces auxilliary files when compiling the spec 7 | *.aux 8 | *.dvi 9 | *.log 10 | *.old 11 | *.tex 12 | 13 | # Running TLC generates a states folder 14 | states/* 15 | */states/* -------------------------------------------------------------------------------- /EveDoesntFrontRun.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | NULL = NULL 3 | StartingTurnNumber <- const_StartingTurnNumber 4 | NumParticipants <- const_NumParticipants 5 | MaxActions <- const_MaxActions 6 | CountActions <- const_CountActions 7 | ForceMoveOverwrites <- const_ForceMoveOverwrites 8 | AliceRefutes <- const_AliceRefutes 9 | EveCheckpoints <- const_EveCheckpoints 10 | EveRefutes <- const_EveRefutes 11 | Nat <- def_ov_Nat 12 | 13 | 14 | SPECIFICATION 15 | Spec 16 | 17 | INVARIANT 18 | TypeOK 19 | 20 | PROPERTY 21 | EveDoesntFrontRun 22 | -------------------------------------------------------------------------------- /ForceMove.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/statechannels/tla-specs/6d7227e2d18331c7a415b27cada2639557e99d48/ForceMove.pdf -------------------------------------------------------------------------------- /ForceMove.tla: -------------------------------------------------------------------------------- 1 | ----------------------------- MODULE ForceMove ----------------------------- 2 | EXTENDS Integers, TLC, Utils 3 | CONSTANTS 4 | StartingTurnNumber, 5 | NumParticipants, 6 | MaxActions, 7 | CountActions, 8 | EveRefutes, 9 | EveCheckpoints, 10 | ForceMoveOverwrites, 11 | AliceRefutes, 12 | NULL 13 | (***************************************************************************) 14 | (* The purpose of this specification is to outline an algorithm that *) 15 | (* guarantees that a challenge is registered on chain with turnNumber *) 16 | (* equal to LatestTurnNumber. It is guaranteed even with an antagonist *) 17 | (* who can do anything (including front-run Alice an arbitrary number of *) 18 | (* times) except *) 19 | (* - signing data with Alice's private key *) 20 | (* - corrupting the blockchain *) 21 | (* *) 22 | (* This guarantee has a key assumption, namely: *) 23 | (* 1. When a challenge is recorded on the adjudicator, Alice is *) 24 | (* always able to *) 25 | (* a) notice the event *) 26 | (* b) submit a transaction *) 27 | (* c) receive confirmation that that transaction was mined *) 28 | (* all before the challenge times out. *) 29 | (* *) 30 | (* If guarantee is met, then either *) 31 | (* A. the channel concludes at this state; or *) 32 | (* B. someone responds with a move that progresses the channel *) 33 | (* C. someone checkpoints with a move that progresses the *) 34 | (* channel *) 35 | (* *) 36 | (* Alice must accept A. She must also accept C -- indeed, she must accept *) 37 | (* any supported state that is recorded on chain, since she must have *) 38 | (* signed at least one "recent" state in its support, and has no control *) 39 | (* over what the other participants does after that state. She would be *) 40 | (* most satisfied with B. *) 41 | (* *) 42 | (* In reality, it is possible that Alice receives a state with turnNumber *) 43 | (* LatestTurnNumber+1, and in this case Alice could (gracefully) abort her *) 44 | (* algorithm and continue the channel. A future version of this *) 45 | (* specification could consider this possibility. *) 46 | (* *) 47 | (* By inductively applying her algorithm, Alice can therefore guarantee *) 48 | (* that either the channel progresses as long as she wishes, or it *) 49 | (* concludes on the latest state that she has. *) 50 | (***************************************************************************) 51 | 52 | 53 | ASSUME 54 | /\ StartingTurnNumber \in Nat 55 | /\ /\ NumParticipants \in Nat 56 | /\ NumParticipants > 1 57 | 58 | 59 | (* --algorithm forceMove 60 | (***************************************************************************) 61 | (* Alice calls adjudicator functions by submitting a pending transaction *) 62 | (* with the function type and arguments. The TransactionProcessor *) 63 | (* processes this transaction and modifies the adjudicator state on her *) 64 | (* behalf. However, when Eve calls functions, she directly modifies the *) 65 | (* adjudicator state. This emulates a reality where Eve can consistently *) 66 | (* front-run Alice's transactions, when desired. *) 67 | (***************************************************************************) 68 | 69 | variables 70 | adjudicator = [turnNumber |-> 0, mode |-> ChannelMode.OPEN ], 71 | TransactionPool = NULL, 72 | Alice \in ParticipantIDXs \ { ParticipantIDX(LatestTurnNumber + 1) }, 73 | alicesActionCount = 0 74 | \* We can't specify any properties that require any memory of the 75 | \* behaviour up to the certain point (ie. the behaviour has passed through state X seven times in a row) 76 | \* we thus have to embed the "memory" of the behaviour in the state itself, 77 | \* if we want to check some property the depends on the history of the behaviour 78 | 79 | define 80 | Number == Nat \cup { 0 } 81 | LatestTurnNumber == StartingTurnNumber + NumParticipants - 1 82 | ParticipantIDXs == 1..NumParticipants 83 | ParticipantIDX(turnNumber) == 1 + ((turnNumber - 1) % NumParticipants) 84 | Signer(state) == ParticipantIDX(state.turnNumber) 85 | 86 | KnownTurnNumbers == 0..(StartingTurnNumber + NumParticipants) 87 | ValidStates == [ turnNumber: Nat ] 88 | AlicesStates == { c \in ValidStates : 89 | /\ c.turnNumber \in KnownTurnNumbers 90 | } 91 | StoredStates == { c \in AlicesStates : c.turnNumber >= StartingTurnNumber } 92 | 93 | AlicesNextTurnNumber == CHOOSE n \in (LatestTurnNumber+1)..(LatestTurnNumber+NumParticipants) : ParticipantIDX(n) = Alice 94 | TargetTurnNumbers == (LatestTurnNumber + 1)..(AlicesNextTurnNumber - 1) 95 | 96 | \* The spec makes an assumption about what supported states Eve has: 97 | \* Since Eve cannot forge Alice's signature, Eve cannot have a supported state with turn number 98 | \* greater than or equal to AlicesNextTurnNumber 99 | EvesSupportedStates == { c \in ValidStates : c.turnNumber < AlicesNextTurnNumber} 100 | EvesStates == EvesSupportedStates \union { c \in ValidStates : ParticipantIDX(c.turnNumber) # Alice } 101 | 102 | challengeOngoing == adjudicator.mode = ChannelMode.CHALLENGE 103 | channelOpen == adjudicator.mode = ChannelMode.OPEN 104 | 105 | increasesTurnNumber(state) == state.turnNumber > adjudicator.turnNumber 106 | 107 | validState(c) == c \in ValidStates 108 | 109 | validTransition(c) == 110 | /\ challengeOngoing 111 | /\ c.turnNumber = adjudicator.turnNumber + 1 112 | 113 | AlicesGoalMet == adjudicator.turnNumber \in TargetTurnNumbers 114 | end define; 115 | 116 | macro validateState(c, type) 117 | begin 118 | if ~validState(c) then 119 | print(<>); 120 | assert FALSE; 121 | end if; 122 | end macro; 123 | 124 | macro clearChallenge(turnNumber) 125 | begin 126 | assert turnNumber \in Nat; 127 | adjudicator := [ 128 | mode |-> ChannelMode.OPEN, 129 | turnNumber |-> turnNumber 130 | ]; 131 | end macro; 132 | 133 | 134 | macro respondWithMove(state) 135 | begin 136 | validateState(state, "respond"); 137 | if validTransition(state) 138 | then clearChallenge(state.turnNumber); 139 | end if; 140 | end macro; 141 | 142 | macro checkpoint(state) 143 | begin 144 | validateState(state, "checkpoint"); 145 | if increasesTurnNumber(state) 146 | then clearChallenge(state.turnNumber); 147 | end if; 148 | end macro; 149 | 150 | macro forceMove(state) 151 | begin 152 | validateState(state, "forceMove"); 153 | if 154 | \/ /\ channelOpen 155 | /\ state.turnNumber >= adjudicator.turnNumber 156 | \/ /\ challengeOngoing 157 | /\ ForceMoveOverwrites 158 | /\ state.turnNumber > adjudicator.turnNumber 159 | then 160 | adjudicator := [ mode |-> ChannelMode.CHALLENGE, turnNumber |-> state.turnNumber ]; 161 | end if; 162 | end macro; 163 | 164 | macro refute(state) 165 | begin 166 | validateState(state, "refute"); 167 | if 168 | /\ challengeOngoing 169 | /\ ParticipantIDX(state.turnNumber) = ParticipantIDX(adjudicator.turnNumber) 170 | /\ state.turnNumber > adjudicator.turnNumber 171 | then clearChallenge(adjudicator.turnNumber) 172 | end if; 173 | end macro; 174 | 175 | macro submitTX(transaction) 176 | begin 177 | if TransactionPool # NULL 178 | then 179 | print(<>); 180 | assert FALSE; 181 | end if; 182 | TransactionPool := transaction; 183 | if CountActions then alicesActionCount := alicesActionCount + 1; end if; 184 | end macro; 185 | 186 | fair process TransactionProcessor = "TransactionProcessor" 187 | begin 188 | (***************************************************************************) 189 | (* This process records submitted transactions. *) 190 | (***************************************************************************) 191 | TransactionProcessor: 192 | while ~AlicesGoalMet \/ TransactionPool # NULL do 193 | if TransactionPool # NULL then 194 | if TransactionPool.type = ForceMoveAPI.FORCE_MOVE then forceMove(TransactionPool.state); 195 | elsif TransactionPool.type = ForceMoveAPI.RESPOND then respondWithMove(TransactionPool.state); 196 | elsif TransactionPool.type = ForceMoveAPI.REFUTE then refute(TransactionPool.state); 197 | elsif TransactionPool.type = ForceMoveAPI.CHECKPOINT then checkpoint(TransactionPool.state); 198 | else assert FALSE; 199 | end if; 200 | TransactionPool := NULL; 201 | end if; 202 | end while; 203 | end process; 204 | 205 | fair process alice = "Alice" 206 | begin 207 | (***************************************************************************) 208 | (* Alice has states (n - numParticipants)..(n-1). She wants to end up *) 209 | (* with states (n - numParticipants + 1)..n. *) 210 | (* *) 211 | (* She is allowed to: *) 212 | (* A. Call submitForceMove with any states that she currently has *) 213 | (* B. Call respondWithMove whenever there's an active challenge where *) 214 | (* it's her turn to move *) 215 | (* C. Call checkpoint at any time. *) 216 | (* D. Call refute at any time. *) 217 | (***************************************************************************) 218 | A: 219 | while ~AlicesGoalMet do 220 | await TransactionPool = NULL; 221 | if challengeOngoing then with turnNumber = adjudicator.turnNumber do 222 | if turnNumber < LatestTurnNumber then 223 | if AliceRefutes then with state = CHOOSE s \in StoredStates : 224 | /\ s.turnNumber > adjudicator.turnNumber 225 | /\ ParticipantIDX(s.turnNumber) = ParticipantIDX(adjudicator.turnNumber) 226 | do submitTX([ type |-> ForceMoveAPI.REFUTE, state |-> state]); 227 | end with; 228 | end if; 229 | end if; 230 | end with; else 231 | submitTX([state |-> [turnNumber |-> LatestTurnNumber ], type |-> ForceMoveAPI.FORCE_MOVE]); 232 | end if; 233 | end while; 234 | end process; 235 | 236 | fair process eve = "Eve" 237 | begin 238 | (***************************************************************************) 239 | (* Eve can do almost anything. She can sign any data with any private *) 240 | (* key, except she cannot sign a state with Alice's private key when the *) 241 | (* turn number is greater than or equal to StartingTurnNumber *) 242 | (* *) 243 | (* She can front-run any transaction an arbitrary number of times: if *) 244 | (* Alice else calls an adjudicator function by submitting a transaction, *) 245 | (* Eve can then instantly mine a transaction before Alice's is mined *) 246 | (***************************************************************************) 247 | E: 248 | while ~AlicesGoalMet do 249 | either 250 | \* TODO: challenge with more states than this 251 | with state \in EvesSupportedStates 252 | do forceMove(state); 253 | end with; 254 | or if challengeOngoing 255 | then either 256 | with state \in EvesSupportedStates do respondWithMove(state); end with; 257 | or if EveCheckpoints then with state \in EvesSupportedStates do checkpoint(state); end with; end if; 258 | or if EveRefutes then with state \in EvesStates do refute(state); end with; end if; 259 | end either; 260 | end if; end either; 261 | end while; 262 | end process; 263 | 264 | end algorithm; 265 | *) 266 | 267 | 268 | \* BEGIN TRANSLATION - the hash of the PCal code: PCal-7553a0e9503658653aa4bda6917162f2 269 | \* Label TransactionProcessor of process TransactionProcessor at line 192 col 1 changed to TransactionProcessor_ 270 | VARIABLES adjudicator, TransactionPool, Alice, alicesActionCount, pc 271 | 272 | (* define statement *) 273 | Number == Nat \cup { 0 } 274 | LatestTurnNumber == StartingTurnNumber + NumParticipants - 1 275 | ParticipantIDXs == 1..NumParticipants 276 | ParticipantIDX(turnNumber) == 1 + ((turnNumber - 1) % NumParticipants) 277 | Signer(state) == ParticipantIDX(state.turnNumber) 278 | 279 | KnownTurnNumbers == 0..(StartingTurnNumber + NumParticipants) 280 | ValidStates == [ turnNumber: Nat ] 281 | AlicesStates == { c \in ValidStates : 282 | /\ c.turnNumber \in KnownTurnNumbers 283 | } 284 | StoredStates == { c \in AlicesStates : c.turnNumber >= StartingTurnNumber } 285 | 286 | AlicesNextTurnNumber == CHOOSE n \in (LatestTurnNumber+1)..(LatestTurnNumber+NumParticipants) : ParticipantIDX(n) = Alice 287 | TargetTurnNumbers == (LatestTurnNumber + 1)..(AlicesNextTurnNumber - 1) 288 | 289 | 290 | 291 | 292 | EvesSupportedStates == { c \in ValidStates : c.turnNumber < AlicesNextTurnNumber} 293 | EvesStates == EvesSupportedStates \union { c \in ValidStates : ParticipantIDX(c.turnNumber) # Alice } 294 | 295 | challengeOngoing == adjudicator.mode = ChannelMode.CHALLENGE 296 | channelOpen == adjudicator.mode = ChannelMode.OPEN 297 | 298 | increasesTurnNumber(state) == state.turnNumber > adjudicator.turnNumber 299 | 300 | validState(c) == c \in ValidStates 301 | 302 | validTransition(c) == 303 | /\ challengeOngoing 304 | /\ c.turnNumber = adjudicator.turnNumber + 1 305 | 306 | AlicesGoalMet == adjudicator.turnNumber \in TargetTurnNumbers 307 | 308 | 309 | vars == << adjudicator, TransactionPool, Alice, alicesActionCount, pc >> 310 | 311 | ProcSet == {"TransactionProcessor"} \cup {"Alice"} \cup {"Eve"} 312 | 313 | Init == (* Global variables *) 314 | /\ adjudicator = [turnNumber |-> 0, mode |-> ChannelMode.OPEN ] 315 | /\ TransactionPool = NULL 316 | /\ Alice \in ParticipantIDXs \ { ParticipantIDX(LatestTurnNumber + 1) } 317 | /\ alicesActionCount = 0 318 | /\ pc = [self \in ProcSet |-> CASE self = "TransactionProcessor" -> "TransactionProcessor_" 319 | [] self = "Alice" -> "A" 320 | [] self = "Eve" -> "E"] 321 | 322 | TransactionProcessor_ == /\ pc["TransactionProcessor"] = "TransactionProcessor_" 323 | /\ IF ~AlicesGoalMet \/ TransactionPool # NULL 324 | THEN /\ IF TransactionPool # NULL 325 | THEN /\ IF TransactionPool.type = ForceMoveAPI.FORCE_MOVE 326 | THEN /\ IF ~validState((TransactionPool.state)) 327 | THEN /\ PrintT((<<"forceMove", (TransactionPool.state)>>)) 328 | /\ Assert(FALSE, 329 | "Failure of assertion at line 120, column 5 of macro called at line 194, column 66.") 330 | ELSE /\ TRUE 331 | /\ IF \/ /\ channelOpen 332 | /\ (TransactionPool.state).turnNumber >= adjudicator.turnNumber 333 | \/ /\ challengeOngoing 334 | /\ ForceMoveOverwrites 335 | /\ (TransactionPool.state).turnNumber > adjudicator.turnNumber 336 | THEN /\ adjudicator' = [ mode |-> ChannelMode.CHALLENGE, turnNumber |-> (TransactionPool.state).turnNumber ] 337 | ELSE /\ TRUE 338 | /\ UNCHANGED adjudicator 339 | ELSE /\ IF TransactionPool.type = ForceMoveAPI.RESPOND 340 | THEN /\ IF ~validState((TransactionPool.state)) 341 | THEN /\ PrintT((<<"respond", (TransactionPool.state)>>)) 342 | /\ Assert(FALSE, 343 | "Failure of assertion at line 120, column 5 of macro called at line 195, column 66.") 344 | ELSE /\ TRUE 345 | /\ IF validTransition((TransactionPool.state)) 346 | THEN /\ Assert(((TransactionPool.state).turnNumber) \in Nat, 347 | "Failure of assertion at line 126, column 1 of macro called at line 195, column 66.") 348 | /\ adjudicator' = [ 349 | mode |-> ChannelMode.OPEN, 350 | turnNumber |-> ((TransactionPool.state).turnNumber) 351 | ] 352 | ELSE /\ TRUE 353 | /\ UNCHANGED adjudicator 354 | ELSE /\ IF TransactionPool.type = ForceMoveAPI.REFUTE 355 | THEN /\ IF ~validState((TransactionPool.state)) 356 | THEN /\ PrintT((<<"refute", (TransactionPool.state)>>)) 357 | /\ Assert(FALSE, 358 | "Failure of assertion at line 120, column 5 of macro called at line 196, column 66.") 359 | ELSE /\ TRUE 360 | /\ IF /\ challengeOngoing 361 | /\ ParticipantIDX((TransactionPool.state).turnNumber) = ParticipantIDX(adjudicator.turnNumber) 362 | /\ (TransactionPool.state).turnNumber > adjudicator.turnNumber 363 | THEN /\ Assert((adjudicator.turnNumber) \in Nat, 364 | "Failure of assertion at line 126, column 1 of macro called at line 196, column 66.") 365 | /\ adjudicator' = [ 366 | mode |-> ChannelMode.OPEN, 367 | turnNumber |-> (adjudicator.turnNumber) 368 | ] 369 | ELSE /\ TRUE 370 | /\ UNCHANGED adjudicator 371 | ELSE /\ IF TransactionPool.type = ForceMoveAPI.CHECKPOINT 372 | THEN /\ IF ~validState((TransactionPool.state)) 373 | THEN /\ PrintT((<<"checkpoint", (TransactionPool.state)>>)) 374 | /\ Assert(FALSE, 375 | "Failure of assertion at line 120, column 5 of macro called at line 197, column 66.") 376 | ELSE /\ TRUE 377 | /\ IF increasesTurnNumber((TransactionPool.state)) 378 | THEN /\ Assert(((TransactionPool.state).turnNumber) \in Nat, 379 | "Failure of assertion at line 126, column 1 of macro called at line 197, column 66.") 380 | /\ adjudicator' = [ 381 | mode |-> ChannelMode.OPEN, 382 | turnNumber |-> ((TransactionPool.state).turnNumber) 383 | ] 384 | ELSE /\ TRUE 385 | /\ UNCHANGED adjudicator 386 | ELSE /\ Assert(FALSE, 387 | "Failure of assertion at line 198, column 14.") 388 | /\ UNCHANGED adjudicator 389 | /\ TransactionPool' = NULL 390 | ELSE /\ TRUE 391 | /\ UNCHANGED << adjudicator, 392 | TransactionPool >> 393 | /\ pc' = [pc EXCEPT !["TransactionProcessor"] = "TransactionProcessor_"] 394 | ELSE /\ pc' = [pc EXCEPT !["TransactionProcessor"] = "Done"] 395 | /\ UNCHANGED << adjudicator, 396 | TransactionPool >> 397 | /\ UNCHANGED << Alice, alicesActionCount >> 398 | 399 | TransactionProcessor == TransactionProcessor_ 400 | 401 | A == /\ pc["Alice"] = "A" 402 | /\ IF ~AlicesGoalMet 403 | THEN /\ TransactionPool = NULL 404 | /\ IF challengeOngoing 405 | THEN /\ LET turnNumber == adjudicator.turnNumber IN 406 | IF turnNumber < LatestTurnNumber 407 | THEN /\ IF AliceRefutes 408 | THEN /\ LET state == CHOOSE s \in StoredStates : 409 | /\ s.turnNumber > adjudicator.turnNumber 410 | /\ ParticipantIDX(s.turnNumber) = ParticipantIDX(adjudicator.turnNumber) IN 411 | /\ IF TransactionPool # NULL 412 | THEN /\ PrintT((< ForceMoveAPI.REFUTE, state |-> state])>>)) 413 | /\ Assert(FALSE, 414 | "Failure of assertion at line 180, column 5 of macro called at line 226, column 17.") 415 | ELSE /\ TRUE 416 | /\ TransactionPool' = [ type |-> ForceMoveAPI.REFUTE, state |-> state] 417 | /\ IF CountActions 418 | THEN /\ alicesActionCount' = alicesActionCount + 1 419 | ELSE /\ TRUE 420 | /\ UNCHANGED alicesActionCount 421 | ELSE /\ TRUE 422 | /\ UNCHANGED << TransactionPool, 423 | alicesActionCount >> 424 | ELSE /\ TRUE 425 | /\ UNCHANGED << TransactionPool, 426 | alicesActionCount >> 427 | ELSE /\ IF TransactionPool # NULL 428 | THEN /\ PrintT((< [turnNumber |-> LatestTurnNumber ], type |-> ForceMoveAPI.FORCE_MOVE])>>)) 429 | /\ Assert(FALSE, 430 | "Failure of assertion at line 180, column 5 of macro called at line 231, column 9.") 431 | ELSE /\ TRUE 432 | /\ TransactionPool' = [state |-> [turnNumber |-> LatestTurnNumber ], type |-> ForceMoveAPI.FORCE_MOVE] 433 | /\ IF CountActions 434 | THEN /\ alicesActionCount' = alicesActionCount + 1 435 | ELSE /\ TRUE 436 | /\ UNCHANGED alicesActionCount 437 | /\ pc' = [pc EXCEPT !["Alice"] = "A"] 438 | ELSE /\ pc' = [pc EXCEPT !["Alice"] = "Done"] 439 | /\ UNCHANGED << TransactionPool, alicesActionCount >> 440 | /\ UNCHANGED << adjudicator, Alice >> 441 | 442 | alice == A 443 | 444 | E == /\ pc["Eve"] = "E" 445 | /\ IF ~AlicesGoalMet 446 | THEN /\ \/ /\ \E state \in EvesSupportedStates: 447 | /\ IF ~validState(state) 448 | THEN /\ PrintT((<<"forceMove", state>>)) 449 | /\ Assert(FALSE, 450 | "Failure of assertion at line 120, column 5 of macro called at line 252, column 12.") 451 | ELSE /\ TRUE 452 | /\ IF \/ /\ channelOpen 453 | /\ state.turnNumber >= adjudicator.turnNumber 454 | \/ /\ challengeOngoing 455 | /\ ForceMoveOverwrites 456 | /\ state.turnNumber > adjudicator.turnNumber 457 | THEN /\ adjudicator' = [ mode |-> ChannelMode.CHALLENGE, turnNumber |-> state.turnNumber ] 458 | ELSE /\ TRUE 459 | /\ UNCHANGED adjudicator 460 | \/ /\ IF challengeOngoing 461 | THEN /\ \/ /\ \E state \in EvesSupportedStates: 462 | /\ IF ~validState(state) 463 | THEN /\ PrintT((<<"respond", state>>)) 464 | /\ Assert(FALSE, 465 | "Failure of assertion at line 120, column 5 of macro called at line 256, column 47.") 466 | ELSE /\ TRUE 467 | /\ IF validTransition(state) 468 | THEN /\ Assert((state.turnNumber) \in Nat, 469 | "Failure of assertion at line 126, column 1 of macro called at line 256, column 47.") 470 | /\ adjudicator' = [ 471 | mode |-> ChannelMode.OPEN, 472 | turnNumber |-> (state.turnNumber) 473 | ] 474 | ELSE /\ TRUE 475 | /\ UNCHANGED adjudicator 476 | \/ /\ IF EveCheckpoints 477 | THEN /\ \E state \in EvesSupportedStates: 478 | /\ IF ~validState(state) 479 | THEN /\ PrintT((<<"checkpoint", state>>)) 480 | /\ Assert(FALSE, 481 | "Failure of assertion at line 120, column 5 of macro called at line 257, column 73.") 482 | ELSE /\ TRUE 483 | /\ IF increasesTurnNumber(state) 484 | THEN /\ Assert((state.turnNumber) \in Nat, 485 | "Failure of assertion at line 126, column 1 of macro called at line 257, column 73.") 486 | /\ adjudicator' = [ 487 | mode |-> ChannelMode.OPEN, 488 | turnNumber |-> (state.turnNumber) 489 | ] 490 | ELSE /\ TRUE 491 | /\ UNCHANGED adjudicator 492 | ELSE /\ TRUE 493 | /\ UNCHANGED adjudicator 494 | \/ /\ IF EveRefutes 495 | THEN /\ \E state \in EvesStates: 496 | /\ IF ~validState(state) 497 | THEN /\ PrintT((<<"refute", state>>)) 498 | /\ Assert(FALSE, 499 | "Failure of assertion at line 120, column 5 of macro called at line 258, column 60.") 500 | ELSE /\ TRUE 501 | /\ IF /\ challengeOngoing 502 | /\ ParticipantIDX(state.turnNumber) = ParticipantIDX(adjudicator.turnNumber) 503 | /\ state.turnNumber > adjudicator.turnNumber 504 | THEN /\ Assert((adjudicator.turnNumber) \in Nat, 505 | "Failure of assertion at line 126, column 1 of macro called at line 258, column 60.") 506 | /\ adjudicator' = [ 507 | mode |-> ChannelMode.OPEN, 508 | turnNumber |-> (adjudicator.turnNumber) 509 | ] 510 | ELSE /\ TRUE 511 | /\ UNCHANGED adjudicator 512 | ELSE /\ TRUE 513 | /\ UNCHANGED adjudicator 514 | ELSE /\ TRUE 515 | /\ UNCHANGED adjudicator 516 | /\ pc' = [pc EXCEPT !["Eve"] = "E"] 517 | ELSE /\ pc' = [pc EXCEPT !["Eve"] = "Done"] 518 | /\ UNCHANGED adjudicator 519 | /\ UNCHANGED << TransactionPool, Alice, alicesActionCount >> 520 | 521 | eve == E 522 | 523 | (* Allow infinite stuttering to prevent deadlock on termination. *) 524 | Terminating == /\ \A self \in ProcSet: pc[self] = "Done" 525 | /\ UNCHANGED vars 526 | 527 | Next == TransactionProcessor \/ alice \/ eve 528 | \/ Terminating 529 | 530 | Spec == /\ Init /\ [][Next]_vars 531 | /\ WF_vars(TransactionProcessor) 532 | /\ WF_vars(alice) 533 | /\ WF_vars(eve) 534 | 535 | Termination == <>(\A self \in ProcSet: pc[self] = "Done") 536 | 537 | \* END TRANSLATION - the hash of the generated TLA code (remove to silence divergence warnings): TLA-909bec0176088e127165e58e0167acee 538 | 539 | AllowedTransactions == [ type: Range(ForceMoveAPI), state: ValidStates ] 540 | AllowedChannels == [ mode: Range(ChannelMode), turnNumber: Number ] 541 | 542 | \* Safety & liveness properties 543 | 544 | TypeOK == 545 | /\ adjudicator \in AllowedChannels 546 | /\ \/ TransactionPool \in AllowedTransactions 547 | \/ TransactionPool = NULL 548 | 549 | AliceCanProgressChannel == <>[](adjudicator.turnNumber \in TargetTurnNumbers) 550 | 551 | \* We can verify that Alice can never directly modify the adjudicator with this property, with 552 | \* the exception that she can finalize the channel. 553 | AliceMustSubmitTransactions == [][ 554 | /\ TransactionPool = NULL 555 | /\ TransactionPool' # NULL 556 | => UNCHANGED adjudicator 557 | ]_<> 558 | 559 | TurnNumberIncrements == [][ 560 | adjudicator'.turnNumber >= adjudicator.turnNumber 561 | ]_<> 562 | 563 | 564 | \* Alice should be able to accomplish her goal by submitting a single transaction. 565 | AliceCannotBeGriefed == alicesActionCount <= MaxActions 566 | 567 | \* Eve front runs if she changes the adjudicator after Alice submitted a transaction, but before 568 | \* the transaction is processed 569 | \* Violations of this property are therefore _examples_ of Eve's ability to front-run 570 | \* Alice's transactions 571 | EveDoesntFrontRun == [][~( 572 | /\ TransactionPool # NULL \* transaction has been submitted 573 | /\ TransactionPool' = TransactionPool \* transaction is not processed 574 | /\ adjudicator' # adjudicator \* adjudicator is changed 575 | )]_<> 576 | 577 | 578 | ============================================================================= 579 | \* Modification History 580 | \* Last modified Fri Jun 12 08:38:05 MDT 2020 by andrewstewart 581 | \* Created Tue Aug 06 14:38:11 MDT 2019 by andrewstewart 582 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This repository contains TLA+ specifications of various protocols used by wallets in the nitro protocol. 4 | 5 | ## Getting started 6 | 7 | 1. First, you'll need to [grab the TLA+ toolbox](https://lamport.azurewebsites.net/tla/toolbox.html). 8 | 2. The learning curve is pretty tough. These [videos](http://lamport.azurewebsites.net/video/videos.html) are a good, but dense intro. You're on your own. 9 | 10 | ## Getting started quickly 11 | 12 | 1. You'll still need to [grab the TLA+ toolbox](https://lamport.azurewebsites.net/tla/toolbox.html). 13 | 2. Read [this article](https://medium.com/@bellmar/introduction-to-tla-model-checking-in-the-command-line-c6871700a6a2). 14 | 3. If you didn't read the article, follow [these instructions](https://github.com/pmer/tla-bin#installation). 15 | - The TLA+ Toolkit isn't _that_ bad. It makes your specs look nice! 16 | 4. Try out a model: `tlc Version1 -config Success.cfg`. 17 | 5. Try out a model that "fails": `tlc Version1 -config EveDoesntFrontRun.cfg` 18 | 19 | - This actually produces an error trace that exhibits Eve's ability to "front-run", according to the spec's design. 20 | 21 | ## Interpretation of `EveDoesntFrontRun` 22 | 23 | First, here's the definition 24 | 25 | ``` 26 | EveDoesntFrontRun == [][~( 27 | /\ TransactionPool # NULL \* transaction has been submitted 28 | /\ TransactionPool' = TransactionPool \* transaction is not processed 29 | /\ adjudicator' # adjudicator \* adjudicator is changed 30 | )]_<> 31 | ``` 32 | 33 | This is a temporal property, which specifies how variables can change: 34 | `adjudicator` is the value of the `adjudicator` variable before the action, and 35 | `adjudicator'` is the value of the `adjudicator` variable after the action. 36 | 37 | In plain English, the property states: 38 | 39 | > It is never true that 40 | > 1. the submitted transaction stored in `TransactionPool` is not null AND 41 | > 2. the submitted transaction stored in `TransactionPool` does not change AND 42 | > 3. the channel value stored in `adjudicator` does change 43 | 44 | Of course, if Eve takes an action after Alice has submitted a transaction, but before 45 | that transaction is recorded, then 1-3 will all hold. 46 | 47 | Therefore, violations of this property are examples of Eve front-running Alice: 48 | 49 | ``` 50 | Error: Action property EveDoesntFrontRun is violated. 51 | Error: The behavior up to this point is: 52 | State 1: 53 | /\ TransactionPool = NULL 54 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 55 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 56 | /\ Alice = 2 57 | /\ alicesActionCount = 0 58 | 59 | # In this state, Alice "submits a transaction", with turn number 6 60 | State 2: 61 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 62 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 63 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 64 | /\ Alice = 2 65 | /\ alicesActionCount = 1 66 | 67 | # In this state, the transaction is still submitted, but the (on-chain) adjudicator 68 | # state has been updated before the TransactionProcessor processed the transaction. 69 | # Eve has mined a ForceMove transaction before Alice's transaction is mined, 70 | # updating the `adjudicatorl` variable to a challenge mode. 71 | State 3: 72 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 73 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 74 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 75 | /\ Alice = 2 76 | /\ alicesActionCount = 1 77 | ``` 78 | 79 | This gives us confidence that our spec is accurately emulating Eve's ability to front-run transactions. 80 | 81 | If we wish, we can observe more interesting traces, where we force some specific on-chain state while Alice's transaction is pending: 82 | 83 | ``` 84 | EveDoesntFrontRun == [][~( 85 | /\ TransactionPool # NULL \* transaction has been submitted 86 | /\ TransactionPool' = TransactionPool \* transaction is not processed 87 | /\ adjudicator' # adjudicator \* adjudicator is changed 88 | /\ adjudicator'.turnNumber \in { 3,4 } 89 | /\ adjudicator'.mode = adjudicatorMode.OPEN 90 | )]_<> 91 | ``` 92 | 93 | This resulted in 94 | 95 | ``` 96 | Error: Action property EveDoesntFrontRun is violated. 97 | Error: The behavior up to this point is: 98 | State 1: 99 | /\ TransactionPool = NULL 100 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 101 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 102 | /\ Alice = 2 103 | /\ alicesActionCount = 0 104 | 105 | # Alice submitted a ForceMove transaction 106 | State 2: 107 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 108 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 109 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 110 | /\ Alice = 2 111 | /\ alicesActionCount = 1 112 | 113 | # Eve mined a ForceMove transaction 114 | State 3: 115 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 116 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 117 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 118 | /\ Alice = 2 119 | /\ alicesActionCount = 1 120 | 121 | # Eve mined a Checkpoint transaction 122 | State 4: 123 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 124 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 125 | /\ adjudicator = [turnNumber |-> 3, mode |-> "OPEN"] 126 | /\ Alice = 2 127 | /\ alicesActionCount = 1 128 | ``` 129 | 130 | # Protocol versions 131 | 132 | ## V1 133 | 134 | | State | Action | NextState | Requirements | 135 | | ----------- | -------------------- | ----------- | ------------------ | 136 | | Open(n) | forceMove(m, s\*, p) | Chal(m,s,p) | m >= n | 137 | | Chal(n,s,p) | respond(n+1,s, s') | Open(n+1) | s->s' | 138 | | Chal(n,s,p) | refute(m, s, s') | Open(n) | m > n, p signed s' | 139 | | Chal(n,s,p) | altRespond(n+1) | Open(n+1) | | 140 | 141 | In this version of the spec, we ignore responding with alternative moves. 142 | Alice employs the strategy of calling `forceMove` when she can, and otherwise 143 | calling `refute` if Eve calls `forceMove` with a stale state. 144 | 145 | Running `❯ tlc Version1.tla -config Success.cfg > v1-problems.txt`, and inspecting the [error trace](v1-problems.txt), we see that Eve was able to enter an infinite cycle, since she is able to cycle between `[turnNumber |-> 0, mode |-> "OPEN"]` and `[turnNumber |-> 0, mode |-> "CHALLENGE"]`. 146 | 147 | TLC can detect the infinite loop if we didn't increment a counter whenever Alice submits transactions. 148 | We can see that by running `❯ tlc Version1NoCounter.tla -config Success.cfg > v1-no-counter.txt`, and inspecting its [error trace](v1-no-counter.txt) 149 | 150 | ## V2 151 | 152 | | State | Action | NextState | Requirements | 153 | | ----------- | -------------------- | ----------- | ------------ | 154 | | Open(n) | forceMove(m, s\*, p) | Chal(m,s,p) | m >= n | 155 | | Chal(n,s,p) | respond(n+1,s, s') | Open(n+1) | s->s' | 156 | | Chal(n,s,p) | altRespond(n+1) | Open(n+1) | | 157 | 158 | Since Eve can force an infinite loop if she can reliably front-run, we have no choice but to remove `refute` from the ForceMove API. 159 | 160 | This yields a successful result: Alice is guaranteed to be able to progress the channel: 161 | 162 | ``` 163 | ❯ tlc Version2 -config Success.cfg 164 | Starting... (2020-06-09 21:16:32) 165 | Implied-temporal checking--satisfiability problem has 2 branches. 166 | Computing initial states... 167 | Finished computing initial states: 1 distinct state generated at 2020-06-09 21:16:32. 168 | Progress(6) at 2020-06-09 21:16:32: 561 states generated, 52 distinct states found, 0 states left on queue. 169 | Checking 2 branches of temporal properties for the complete state space with 104 total distinct states at (2020-06-09 21:16:32) 170 | Finished checking temporal properties in 00s at 2020-06-09 21:16:32 171 | Model checking completed. No error has been found. 172 | Estimates of the probability that TLC did not check all reachable states 173 | because two distinct states had the same fingerprint: 174 | calculated (optimistic): val = 1.4E-15 175 | 561 states generated, 52 distinct states found, 0 states left on queue. 176 | The depth of the complete state graph search is 6. 177 | The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 9 and the 95th percentile is 7). 178 | Finished in 01s at (2020-06-09 21:16:32) 179 | ``` 180 | 181 | However, this is not satisfactory. Eve can grief Alice by front-running `forceMove(s10^*)` with `forceMove(s0^*)`, then `forceMove(s1^*)`, etc. Run 182 | ``` 183 | ❯ tlc Version2NoGrief.tla -config Success.cfg > v2-no-grief.txt 184 | ``` 185 | We see in the [output](v2-no-grief.txt) that Alice needs to submit as many transactions as there are states to force the channel to a certain turn number. 186 | 187 | ## V3 188 | 189 | | State | Action | NextState | Requirements | 190 | | ----------- | ----------------- | ------------ | ------------ | 191 | | Open(n) | forceMove(m,s,p) | Chal(m,s,p) | m >= n | 192 | | Chal(n,s,p) | forceMove(m,s',p) | Chal(m,s',p) | m > n | 193 | | Open(n,s) | checkpoint(m) | Open(m) | m > n | 194 | | Chal(n,s,p) | checkpoint(m) | Open(m) | m > n | 195 | | Chal(n,s,p) | respond(s,s') | Open(n+1) | s->s' | 196 | 197 | Thus, we change the semantics of `forceMove` to overwrite existing challenges, if it increases the turn number. 198 | This maximally simplifies Alice's strategy -- she can submit a `forceMove` transaction with her latest supported state, and no amount of front-running will prevent her from progressing the channel in a constant number of actions: 199 | 200 | ``` 201 | ❯ tlc Version3.tla -config Success.cfg 202 | Starting... (2020-06-09 22:05:03) 203 | Implied-temporal checking--satisfiability problem has 2 branches. 204 | Computing initial states... 205 | Finished computing initial states: 1 distinct state generated at 2020-06-09 22:05:03. 206 | Progress(7) at 2020-06-09 22:05:03: 614 states generated, 69 distinct states found, 0 states left on queue. 207 | Checking 2 branches of temporal properties for the complete state space with 138 total distinct states at (2020-06-09 22:05:03) 208 | Finished checking temporal properties in 00s at 2020-06-09 22:05:03 209 | Model checking completed. No error has been found. 210 | Estimates of the probability that TLC did not check all reachable states 211 | because two distinct states had the same fingerprint: 212 | calculated (optimistic): val = 2.0E-15 213 | 614 states generated, 69 distinct states found, 0 states left on queue. 214 | The depth of the complete state graph search is 7. 215 | The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 9 and the 95th percentile is 7). 216 | Finished in 01s at (2020-06-09 22:05:03) 217 | ``` 218 | 219 | In this version, we also introduced a `checkpoint` operation: the original `respondWithAlternativeMove` described in the nitro paper (TODO: LINK) had two limitations: 220 | 221 | - the turn number must increase by exactly 1 222 | - the channel must be in the challenge mode. 223 | 224 | While exploring other strategies for Alice using TLA+, it became clear that these limitations were unnecessary, and lifting them provided many benefits. 225 | -------------------------------------------------------------------------------- /Success.cfg: -------------------------------------------------------------------------------- 1 | CONSTANT 2 | NULL = NULL 3 | StartingTurnNumber <- const_StartingTurnNumber 4 | NumParticipants <- const_NumParticipants 5 | MaxActions <- const_MaxActions 6 | CountActions <- const_CountActions 7 | AliceRefutes <- const_AliceRefutes 8 | ForceMoveOverwrites <- const_ForceMoveOverwrites 9 | EveCheckpoints <- const_EveCheckpoints 10 | EveRefutes <- const_EveRefutes 11 | Nat <- def_ov_Nat 12 | 13 | SPECIFICATION 14 | Spec 15 | 16 | INVARIANT 17 | TypeOK 18 | AliceCannotBeGriefed 19 | 20 | PROPERTY 21 | Termination 22 | AliceCanProgressChannel 23 | AliceMustSubmitTransactions 24 | TurnNumberIncrements -------------------------------------------------------------------------------- /Utils.tla: -------------------------------------------------------------------------------- 1 | ------------------------------- MODULE Utils ------------------------------- 2 | EXTENDS Integers 3 | ChannelMode == [ 4 | OPEN |-> "OPEN", 5 | CHALLENGE |-> "CHALLENGE" 6 | ] 7 | 8 | ForceMoveAPI == [ 9 | FORCE_MOVE |-> "FORCE_MOVE", 10 | RESPOND |-> "RESPOND", 11 | CHECKPOINT |-> "CHECKPOINT", 12 | REFUTE |-> "REFUTE" 13 | ] 14 | 15 | Range(f) == { f[x] : x \in DOMAIN f } 16 | Maximum(a,b) == IF a > b THEN a ELSE b 17 | 18 | ============================================================================= 19 | \* Modification History 20 | \* Last modified Mon Jun 08 14:03:44 PDT 2020 by andrewstewart 21 | \* Created Tue Sep 10 18:35:45 MDT 2019 by andrewstewart 22 | -------------------------------------------------------------------------------- /Version1.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE Version1 ---- 2 | EXTENDS ForceMove, TLC 3 | 4 | const_StartingTurnNumber == 5 5 | const_NumParticipants == 2 6 | const_MaxActions == 3 7 | const_CountActions == TRUE 8 | const_ForceMoveOverwrites == FALSE 9 | const_AliceRefutes == TRUE 10 | const_EveCheckpoints == FALSE 11 | const_EveRefutes == TRUE 12 | def_ov_Nat == 0..20 13 | ============================================================================= -------------------------------------------------------------------------------- /Version1NoCounter.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE Version1NoCounter ---- 2 | EXTENDS ForceMove, TLC 3 | 4 | const_StartingTurnNumber == 5 5 | const_NumParticipants == 2 6 | const_MaxActions == 3 7 | const_CountActions == FALSE 8 | const_ForceMoveOverwrites == FALSE 9 | const_AliceRefutes == TRUE 10 | const_EveCheckpoints == FALSE 11 | const_EveRefutes == TRUE 12 | def_ov_Nat == 0..20 13 | ============================================================================= -------------------------------------------------------------------------------- /Version2.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE Version2 ---- 2 | EXTENDS ForceMove, TLC 3 | 4 | const_StartingTurnNumber == 5 5 | const_NumParticipants == 2 6 | const_MaxActions == 1 7 | const_CountActions == FALSE 8 | const_ForceMoveOverwrites == FALSE 9 | const_AliceRefutes == FALSE 10 | const_EveCheckpoints == TRUE 11 | const_EveRefutes == FALSE 12 | def_ov_Nat == 0..20 13 | ============================================================================= -------------------------------------------------------------------------------- /Version2NoGrief.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE Version2NoGrief ---- 2 | EXTENDS ForceMove, TLC 3 | 4 | const_StartingTurnNumber == 10 5 | const_NumParticipants == 2 6 | const_MaxActions == 10 7 | const_CountActions == TRUE 8 | const_ForceMoveOverwrites == FALSE 9 | const_AliceRefutes == FALSE 10 | const_EveCheckpoints == TRUE 11 | const_EveRefutes == FALSE 12 | def_ov_Nat == 0..20 13 | ============================================================================= -------------------------------------------------------------------------------- /Version3.tla: -------------------------------------------------------------------------------- 1 | ---- MODULE Version3 ---- 2 | EXTENDS ForceMove, TLC 3 | 4 | const_StartingTurnNumber == 5 5 | const_NumParticipants == 2 6 | const_MaxActions == 1 7 | const_CountActions == TRUE 8 | const_ForceMoveOverwrites == TRUE 9 | const_AliceRefutes == FALSE 10 | const_EveCheckpoints == TRUE 11 | const_EveRefutes == FALSE 12 | def_ov_Nat == 0..20 13 | ============================================================================= -------------------------------------------------------------------------------- /v1-no-counter.txt: -------------------------------------------------------------------------------- 1 | TLC2 Version 2.14 of 10 July 2019 (rev: 0cae24f) 2 | Running breadth-first search Model-Checking with fp 12 and seed 978873911999156740 with 1 worker on 12 cores with 3641MB heap and 64MB offheap memory [pid: 2952] (Mac OS X 10.13.6 x86_64, Oracle Corporation 11.0.2 x86_64, MSBDiskFPSet, DiskStateQueue). 3 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Version1NoCounter.tla 4 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/ForceMove.tla 5 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/TLC.tla 6 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Integers.tla 7 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Utils.tla 8 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Naturals.tla 9 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Sequences.tla 10 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/FiniteSets.tla 11 | Semantic processing of module Naturals 12 | Semantic processing of module Integers 13 | Semantic processing of module Sequences 14 | Semantic processing of module FiniteSets 15 | Semantic processing of module TLC 16 | Semantic processing of module Utils 17 | Semantic processing of module ForceMove 18 | Semantic processing of module Version1NoCounter 19 | Starting... (2020-06-09 21:01:05) 20 | Implied-temporal checking--satisfiability problem has 2 branches. 21 | Computing initial states... 22 | Finished computing initial states: 1 distinct state generated at 2020-06-09 21:01:05. 23 | Progress(7) at 2020-06-09 21:01:06: 1,439 states generated, 106 distinct states found, 0 states left on queue. 24 | Checking 2 branches of temporal properties for the complete state space with 212 total distinct states at (2020-06-09 21:01:06) 25 | Error: Temporal properties were violated. 26 | 27 | Error: The following behavior constitutes a counter-example: 28 | 29 | State 1: 30 | /\ TransactionPool = NULL 31 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 32 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 33 | /\ Alice = 2 34 | /\ alicesActionCount = 0 35 | 36 | State 2: 37 | /\ TransactionPool = NULL 38 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 39 | /\ adjudicator = [turnNumber |-> 4, mode |-> "CHALLENGE"] 40 | /\ Alice = 2 41 | /\ alicesActionCount = 0 42 | 43 | State 3: 44 | /\ TransactionPool = NULL 45 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 46 | /\ adjudicator = [turnNumber |-> 5, mode |-> "OPEN"] 47 | /\ Alice = 2 48 | /\ alicesActionCount = 0 49 | 50 | State 4: 51 | /\ TransactionPool = NULL 52 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 53 | /\ adjudicator = [turnNumber |-> 5, mode |-> "CHALLENGE"] 54 | /\ Alice = 2 55 | /\ alicesActionCount = 0 56 | 57 | State 5: 58 | /\ TransactionPool = [state |-> [turnNumber |-> 7], type |-> "REFUTE"] 59 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 60 | /\ adjudicator = [turnNumber |-> 5, mode |-> "CHALLENGE"] 61 | /\ Alice = 2 62 | /\ alicesActionCount = 0 63 | 64 | Back to state 3: 65 | 66 | Finished checking temporal properties in 00s at 2020-06-09 21:01:06 67 | 1439 states generated, 106 distinct states found, 0 states left on queue. 68 | Finished in 00s at (2020-06-09 21:01:06) 69 | -------------------------------------------------------------------------------- /v1-problems.txt: -------------------------------------------------------------------------------- 1 | TLC2 Version 2.14 of 10 July 2019 (rev: 0cae24f) 2 | Running breadth-first search Model-Checking with fp 97 and seed -2707441539784394856 with 1 worker on 12 cores with 3641MB heap and 64MB offheap memory [pid: 82995] (Mac OS X 10.13.6 x86_64, Oracle Corporation 11.0.2 x86_64, MSBDiskFPSet, DiskStateQueue). 3 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Version1.tla 4 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/ForceMove.tla 5 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/TLC.tla 6 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Integers.tla 7 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Utils.tla 8 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Naturals.tla 9 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Sequences.tla 10 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/FiniteSets.tla 11 | Semantic processing of module Naturals 12 | Semantic processing of module Integers 13 | Semantic processing of module Sequences 14 | Semantic processing of module FiniteSets 15 | Semantic processing of module TLC 16 | Semantic processing of module Utils 17 | Semantic processing of module ForceMove 18 | Semantic processing of module Version1 19 | Starting... (2020-06-09 12:21:57) 20 | Implied-temporal checking--satisfiability problem has 2 branches. 21 | Computing initial states... 22 | Finished computing initial states: 1 distinct state generated at 2020-06-09 12:21:57. 23 | Error: Invariant AliceCannotBeGriefed is violated. 24 | Error: The behavior up to this point is: 25 | State 1: 26 | /\ TransactionPool = NULL 27 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 28 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 29 | /\ Alice = 2 30 | /\ alicesActionCount = 0 31 | 32 | State 2: 33 | /\ TransactionPool = NULL 34 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 35 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 36 | /\ Alice = 2 37 | /\ alicesActionCount = 0 38 | 39 | State 3: 40 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "REFUTE"] 41 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 42 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 43 | /\ Alice = 2 44 | /\ alicesActionCount = 1 45 | 46 | State 4: 47 | /\ TransactionPool = NULL 48 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 49 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 50 | /\ Alice = 2 51 | /\ alicesActionCount = 1 52 | 53 | State 5: 54 | /\ TransactionPool = NULL 55 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 56 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 57 | /\ Alice = 2 58 | /\ alicesActionCount = 1 59 | 60 | State 6: 61 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "REFUTE"] 62 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 63 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 64 | /\ Alice = 2 65 | /\ alicesActionCount = 2 66 | 67 | State 7: 68 | /\ TransactionPool = NULL 69 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 70 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 71 | /\ Alice = 2 72 | /\ alicesActionCount = 2 73 | 74 | State 8: 75 | /\ TransactionPool = NULL 76 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 77 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 78 | /\ Alice = 2 79 | /\ alicesActionCount = 2 80 | 81 | State 9: 82 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "REFUTE"] 83 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 84 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 85 | /\ Alice = 2 86 | /\ alicesActionCount = 3 87 | 88 | State 10: 89 | /\ TransactionPool = NULL 90 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 91 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 92 | /\ Alice = 2 93 | /\ alicesActionCount = 3 94 | 95 | State 11: 96 | /\ TransactionPool = [state |-> [turnNumber |-> 6], type |-> "FORCE_MOVE"] 97 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 98 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 99 | /\ Alice = 2 100 | /\ alicesActionCount = 4 101 | 102 | 3829 states generated, 309 distinct states found, 41 states left on queue. 103 | The depth of the complete state graph search is 11. 104 | The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 9 and the 95th percentile is 5). 105 | Finished in 01s at (2020-06-09 12:21:58) 106 | -------------------------------------------------------------------------------- /v2-no-grief.txt: -------------------------------------------------------------------------------- 1 | TLC2 Version 2.14 of 10 July 2019 (rev: 0cae24f) 2 | Running breadth-first search Model-Checking with fp 125 and seed 6349797062896713821 with 1 worker on 12 cores with 3641MB heap and 64MB offheap memory [pid: 20376] (Mac OS X 10.13.6 x86_64, Oracle Corporation 11.0.2 x86_64, MSBDiskFPSet, DiskStateQueue). 3 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Version2NoGrief.tla 4 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/ForceMove.tla 5 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/TLC.tla 6 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Integers.tla 7 | Parsing file /Users/andrewstewart/Code/magmo/tla-specs/Utils.tla 8 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Naturals.tla 9 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/Sequences.tla 10 | Parsing file /private/var/folders/qp/rsls0bwn2p99qffhs69_ffbc0000gn/T/FiniteSets.tla 11 | Semantic processing of module Naturals 12 | Semantic processing of module Integers 13 | Semantic processing of module Sequences 14 | Semantic processing of module FiniteSets 15 | Semantic processing of module TLC 16 | Semantic processing of module Utils 17 | Semantic processing of module ForceMove 18 | Semantic processing of module Version2NoGrief 19 | Starting... (2020-06-09 21:55:27) 20 | Implied-temporal checking--satisfiability problem has 2 branches. 21 | Computing initial states... 22 | Finished computing initial states: 1 distinct state generated at 2020-06-09 21:55:27. 23 | Error: Invariant AliceCannotBeGriefed is violated. 24 | Error: The behavior up to this point is: 25 | State 1: 26 | /\ TransactionPool = NULL 27 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 28 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 29 | /\ Alice = 1 30 | /\ alicesActionCount = 0 31 | 32 | State 2: 33 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 34 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 35 | /\ adjudicator = [turnNumber |-> 0, mode |-> "OPEN"] 36 | /\ Alice = 1 37 | /\ alicesActionCount = 1 38 | 39 | State 3: 40 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 41 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 42 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 43 | /\ Alice = 1 44 | /\ alicesActionCount = 1 45 | 46 | State 4: 47 | /\ TransactionPool = NULL 48 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 49 | /\ adjudicator = [turnNumber |-> 0, mode |-> "CHALLENGE"] 50 | /\ Alice = 1 51 | /\ alicesActionCount = 1 52 | 53 | State 5: 54 | /\ TransactionPool = NULL 55 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 56 | /\ adjudicator = [turnNumber |-> 1, mode |-> "OPEN"] 57 | /\ Alice = 1 58 | /\ alicesActionCount = 1 59 | 60 | State 6: 61 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 62 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 63 | /\ adjudicator = [turnNumber |-> 1, mode |-> "OPEN"] 64 | /\ Alice = 1 65 | /\ alicesActionCount = 2 66 | 67 | State 7: 68 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 69 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 70 | /\ adjudicator = [turnNumber |-> 1, mode |-> "CHALLENGE"] 71 | /\ Alice = 1 72 | /\ alicesActionCount = 2 73 | 74 | State 8: 75 | /\ TransactionPool = NULL 76 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 77 | /\ adjudicator = [turnNumber |-> 1, mode |-> "CHALLENGE"] 78 | /\ Alice = 1 79 | /\ alicesActionCount = 2 80 | 81 | State 9: 82 | /\ TransactionPool = NULL 83 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 84 | /\ adjudicator = [turnNumber |-> 2, mode |-> "OPEN"] 85 | /\ Alice = 1 86 | /\ alicesActionCount = 2 87 | 88 | State 10: 89 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 90 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 91 | /\ adjudicator = [turnNumber |-> 2, mode |-> "OPEN"] 92 | /\ Alice = 1 93 | /\ alicesActionCount = 3 94 | 95 | State 11: 96 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 97 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 98 | /\ adjudicator = [turnNumber |-> 2, mode |-> "CHALLENGE"] 99 | /\ Alice = 1 100 | /\ alicesActionCount = 3 101 | 102 | State 12: 103 | /\ TransactionPool = NULL 104 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 105 | /\ adjudicator = [turnNumber |-> 2, mode |-> "CHALLENGE"] 106 | /\ Alice = 1 107 | /\ alicesActionCount = 3 108 | 109 | State 13: 110 | /\ TransactionPool = NULL 111 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 112 | /\ adjudicator = [turnNumber |-> 3, mode |-> "OPEN"] 113 | /\ Alice = 1 114 | /\ alicesActionCount = 3 115 | 116 | State 14: 117 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 118 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 119 | /\ adjudicator = [turnNumber |-> 3, mode |-> "OPEN"] 120 | /\ Alice = 1 121 | /\ alicesActionCount = 4 122 | 123 | State 15: 124 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 125 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 126 | /\ adjudicator = [turnNumber |-> 3, mode |-> "CHALLENGE"] 127 | /\ Alice = 1 128 | /\ alicesActionCount = 4 129 | 130 | State 16: 131 | /\ TransactionPool = NULL 132 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 133 | /\ adjudicator = [turnNumber |-> 3, mode |-> "CHALLENGE"] 134 | /\ Alice = 1 135 | /\ alicesActionCount = 4 136 | 137 | State 17: 138 | /\ TransactionPool = NULL 139 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 140 | /\ adjudicator = [turnNumber |-> 4, mode |-> "OPEN"] 141 | /\ Alice = 1 142 | /\ alicesActionCount = 4 143 | 144 | State 18: 145 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 146 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 147 | /\ adjudicator = [turnNumber |-> 4, mode |-> "OPEN"] 148 | /\ Alice = 1 149 | /\ alicesActionCount = 5 150 | 151 | State 19: 152 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 153 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 154 | /\ adjudicator = [turnNumber |-> 4, mode |-> "CHALLENGE"] 155 | /\ Alice = 1 156 | /\ alicesActionCount = 5 157 | 158 | State 20: 159 | /\ TransactionPool = NULL 160 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 161 | /\ adjudicator = [turnNumber |-> 4, mode |-> "CHALLENGE"] 162 | /\ Alice = 1 163 | /\ alicesActionCount = 5 164 | 165 | State 21: 166 | /\ TransactionPool = NULL 167 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 168 | /\ adjudicator = [turnNumber |-> 5, mode |-> "OPEN"] 169 | /\ Alice = 1 170 | /\ alicesActionCount = 5 171 | 172 | State 22: 173 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 174 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 175 | /\ adjudicator = [turnNumber |-> 5, mode |-> "OPEN"] 176 | /\ Alice = 1 177 | /\ alicesActionCount = 6 178 | 179 | State 23: 180 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 181 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 182 | /\ adjudicator = [turnNumber |-> 5, mode |-> "CHALLENGE"] 183 | /\ Alice = 1 184 | /\ alicesActionCount = 6 185 | 186 | State 24: 187 | /\ TransactionPool = NULL 188 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 189 | /\ adjudicator = [turnNumber |-> 5, mode |-> "CHALLENGE"] 190 | /\ Alice = 1 191 | /\ alicesActionCount = 6 192 | 193 | State 25: 194 | /\ TransactionPool = NULL 195 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 196 | /\ adjudicator = [turnNumber |-> 6, mode |-> "OPEN"] 197 | /\ Alice = 1 198 | /\ alicesActionCount = 6 199 | 200 | State 26: 201 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 202 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 203 | /\ adjudicator = [turnNumber |-> 6, mode |-> "OPEN"] 204 | /\ Alice = 1 205 | /\ alicesActionCount = 7 206 | 207 | State 27: 208 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 209 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 210 | /\ adjudicator = [turnNumber |-> 6, mode |-> "CHALLENGE"] 211 | /\ Alice = 1 212 | /\ alicesActionCount = 7 213 | 214 | State 28: 215 | /\ TransactionPool = NULL 216 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 217 | /\ adjudicator = [turnNumber |-> 6, mode |-> "CHALLENGE"] 218 | /\ Alice = 1 219 | /\ alicesActionCount = 7 220 | 221 | State 29: 222 | /\ TransactionPool = NULL 223 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 224 | /\ adjudicator = [turnNumber |-> 7, mode |-> "OPEN"] 225 | /\ Alice = 1 226 | /\ alicesActionCount = 7 227 | 228 | State 30: 229 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 230 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 231 | /\ adjudicator = [turnNumber |-> 7, mode |-> "OPEN"] 232 | /\ Alice = 1 233 | /\ alicesActionCount = 8 234 | 235 | State 31: 236 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 237 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 238 | /\ adjudicator = [turnNumber |-> 7, mode |-> "CHALLENGE"] 239 | /\ Alice = 1 240 | /\ alicesActionCount = 8 241 | 242 | State 32: 243 | /\ TransactionPool = NULL 244 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 245 | /\ adjudicator = [turnNumber |-> 7, mode |-> "CHALLENGE"] 246 | /\ Alice = 1 247 | /\ alicesActionCount = 8 248 | 249 | State 33: 250 | /\ TransactionPool = NULL 251 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 252 | /\ adjudicator = [turnNumber |-> 8, mode |-> "OPEN"] 253 | /\ Alice = 1 254 | /\ alicesActionCount = 8 255 | 256 | State 34: 257 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 258 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 259 | /\ adjudicator = [turnNumber |-> 8, mode |-> "OPEN"] 260 | /\ Alice = 1 261 | /\ alicesActionCount = 9 262 | 263 | State 35: 264 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 265 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 266 | /\ adjudicator = [turnNumber |-> 8, mode |-> "CHALLENGE"] 267 | /\ Alice = 1 268 | /\ alicesActionCount = 9 269 | 270 | State 36: 271 | /\ TransactionPool = NULL 272 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 273 | /\ adjudicator = [turnNumber |-> 8, mode |-> "CHALLENGE"] 274 | /\ Alice = 1 275 | /\ alicesActionCount = 9 276 | 277 | State 37: 278 | /\ TransactionPool = NULL 279 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 280 | /\ adjudicator = [turnNumber |-> 9, mode |-> "OPEN"] 281 | /\ Alice = 1 282 | /\ alicesActionCount = 9 283 | 284 | State 38: 285 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 286 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 287 | /\ adjudicator = [turnNumber |-> 9, mode |-> "OPEN"] 288 | /\ Alice = 1 289 | /\ alicesActionCount = 10 290 | 291 | State 39: 292 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 293 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 294 | /\ adjudicator = [turnNumber |-> 9, mode |-> "CHALLENGE"] 295 | /\ Alice = 1 296 | /\ alicesActionCount = 10 297 | 298 | State 40: 299 | /\ TransactionPool = NULL 300 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 301 | /\ adjudicator = [turnNumber |-> 9, mode |-> "CHALLENGE"] 302 | /\ Alice = 1 303 | /\ alicesActionCount = 10 304 | 305 | State 41: 306 | /\ TransactionPool = NULL 307 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 308 | /\ adjudicator = [turnNumber |-> 10, mode |-> "OPEN"] 309 | /\ Alice = 1 310 | /\ alicesActionCount = 10 311 | 312 | State 42: 313 | /\ TransactionPool = [state |-> [turnNumber |-> 11], type |-> "FORCE_MOVE"] 314 | /\ pc = [Alice |-> "A", TransactionProcessor |-> "TransactionProcessor", Eve |-> "E"] 315 | /\ adjudicator = [turnNumber |-> 10, mode |-> "OPEN"] 316 | /\ Alice = 1 317 | /\ alicesActionCount = 11 318 | 319 | 9483 states generated, 565 distinct states found, 10 states left on queue. 320 | The depth of the complete state graph search is 42. 321 | The average outdegree of the complete state graph is 1 (minimum is 0, the maximum 14 and the 95th percentile is 3). 322 | Finished in 01s at (2020-06-09 21:55:29) 323 | --------------------------------------------------------------------------------