|
|
- ------------------------------- MODULE scheduler -------------------------------
- (*
- A specification of the fast sync scheduler that is introduced in blockchain/v2:
-
- https://github.com/tendermint/tendermint/tree/brapse/blockchain-v2-riri-reactor-2
-
- The model includes:
- - a scheduler that maintains the peers and blocks that it receives from the peers, and
- - one environment simulating a correct peer
-
- This specification focuses on the events that are received and produced by the scheduler.
- Communication between the scheduler and the other fastsync components is not specified.
- *)
-
- EXTENDS Integers, FiniteSets
-
- \* the protocol parameters
- CONSTANTS
- PeerIDs, \* potential peer ids, a set of integers, e.g. 0..2
- ultimateHeight, \* the maximum height of the blockchain, an integer, e.g. 3
- numRequests \* the maximum number of requests made when scheduling new blocks, e.g. 2
-
- \* a few definitions
- None == -1 \* an undefined value
- Heights == 0..ultimateHeight \* potential heights
- noErr == "errNone"
- Errors == {
- noErr, "errPeerNotFound", "errDelRemovedPeer", "errAddDuplicatePeer",
- "errUpdateRemovedPeer", "errAfterPeerRemove", "errBadPeer", "errBadPeerState",
- "errProcessedBlockEv", "finished", "timeout", "errAddRemovedPeer", "errPeerNoBlock", "errBadBlockState"}
-
- PeerStates == {"peerStateUnknown", "peerStateNew", "peerStateReady", "peerStateRemoved"}
-
- BlockStates == {
- "blockStateUnknown", "blockStateNew", "blockStatePending",
- "blockStateReceived", "blockStateProcessed"}
-
- \* basic stuff
- Min(a, b) == IF a < b THEN a ELSE b
- Max(a, b) == IF a > b THEN a ELSE b
-
- \* the state of the scheduler:
- VARIABLE turn \* who makes a step: the scheduler or the environment
-
- \* the state of the reactor:
- VARIABLES inEvent, \* an event from the environment to the scheduler
- envRunning \* a Boolean, negation of stopProcessing in the implementation
-
- \* the state of the scheduler:
- VARIABLES outEvent, \* an event from the scheduler to the environment
- scRunning
-
- \* the block pool:
- VARIABLE scheduler
- (*
- scheduler is a record that contains:
- height: Int,
- height of the next block to collect
- peers: PeerIDs,
- the set of peers that have connected in the past, may include removed peers
- peerHeights: [PeerIDs -> Heights],
- a map to collect the peer heights, >0 if peer in ready state, None (-1) otherwise
- peerStates: [PeerIDs -> PeerStates],
- a map to record the peer states
- blockStates: [Heights -> BlockStates]
- a set of heights for which blocks are to be scheduled, pending or received
- pendingBlocks: [Heights -> PeerIDs],
- a set of heights for which blocks are to be scheduled or pending
- receivedBlocks: [Heights -> PeerIDs],
- a set of heights for which blocks were received but not yet processed
- blocks: Heights,
- the full set of blocks requested or downloaded by the scheduler
- *)
-
- vars == <<turn, envRunning, inEvent, scRunning, outEvent, scheduler>>
-
- \* for now just keep the height in the block
- Blocks == [ height: Heights ]
-
- noEvent == [type |-> "NoEvent"]
-
- InEvents ==
- {noEvent} \cup
- [type: {"rTrySchedule", "tNoAdvanceExp"}] \cup
- [type: {"bcStatusResponse"}, peerID: PeerIDs, height: Heights] \cup
- [type: {"bcBlockResponse"}, peerID: PeerIDs, height: Heights, block: Blocks] \cup
- [type: {"bcNoBlockResponse"}, peerID: PeerIDs, height: Heights] \cup
- [type: {"pcBlockProcessed"}, peerID: PeerIDs, height: Heights] \cup
- [type: {"pcBlockVerificationFailure"}, height: Heights, firstPeerID: PeerIDs, secondPeerID: PeerIDs] \cup
- [type: {"bcAddNewPeer"}, peerID: PeerIDs] \cup
- [type: {"bcRemovePeer"}, peerID: PeerIDs]
-
- \* Output events produced by the scheduler.
- \* Note: in v2 the status request is done by the reactor/ environment
- OutEvents ==
- {noEvent} \cup
- [type: {"scPeerError"}, peerID: PeerIDs, error: Errors] \cup
- [type: {"scSchedulerFail"}, error: Errors] \cup
- [type: {"scBlockRequest"}, peerID: PeerIDs, height: Heights] \cup
- [type: {"scBlockReceived"}, peerID: PeerIDs, block: Blocks] \cup
- [type: {"scPeersPruned"}, pruned: SUBSET [peerID: PeerIDs]] \cup
- [type: {"scFinishedEv"}, error: Errors]
-
- (* ----------------------------------------------------------------------------------------------*)
- (* The behavior of the scheduler that keeps track of peers, block requests and responses, etc. *)
- (* See scheduler.go *)
- (* https://github.com/tendermint/tendermint/blob/v0.33.3/blockchain/v2/scheduler.go *)
- (* ----------------------------------------------------------------------------------------------*)
-
- addPeer(sc, peerID) ==
- IF peerID \in sc.peers THEN
- [err |-> "errAddDuplicatePeer", val |-> sc]
- ELSE IF sc.peerStates[peerID] = "peerStateRemoved" THEN
- [err |-> "errAddRemovedPeer", val |-> sc]
- ELSE
- LET newPeers == sc.peers \cup { peerID } IN
- LET newPeerHeights == [sc.peerHeights EXCEPT ![peerID] = None] IN
- LET newPeerStates == [sc.peerStates EXCEPT ![peerID] = "peerStateNew"] IN
- LET newSc == [sc EXCEPT
- !.peers = newPeers,
- !.peerHeights = newPeerHeights,
- !.peerStates = newPeerStates] IN
- [err |-> noErr, val |-> newSc]
-
- maxHeight(states, heights) ==
- LET activePeers == {p \in DOMAIN states: states[p] = "peerStateReady"} IN
- IF activePeers = {} THEN
- 0 \* no peers, just return 0
- ELSE
- CHOOSE max \in { heights[p] : p \in activePeers }:
- \A p \in activePeers: heights[p] <= max \* max is the maximum
-
- maxHeightScheduler(sc) ==
- maxHeight(sc.peerStates, sc.peerHeights)
-
- removePeer(sc, peerID) ==
- IF peerID \notin sc.peers THEN
- [err |-> "errPeerNotFound", val |-> sc]
- ELSE IF sc.peerStates[peerID] = "peerStateRemoved" THEN
- [err |-> "errDelRemovedPeer", val |-> sc]
- ELSE
- LET newSc == [sc EXCEPT
- !.peerHeights[peerID] = None,
- !.peerStates[peerID] = "peerStateRemoved",
- \* remove all blocks from peerID and block requests to peerID, see scheduler.removePeer
- !.blockStates = [h \in Heights |->
- IF sc.pendingBlocks[h] = peerID \/ sc.receivedBlocks[h] = peerID THEN "blockStateNew"
- ELSE sc.blockStates[h]],
- !.pendingBlocks = [h \in Heights |-> IF sc.pendingBlocks[h] = peerID THEN None ELSE sc.pendingBlocks[h]],
- !.receivedBlocks = [h \in Heights |-> IF sc.receivedBlocks[h] = peerID THEN None ELSE sc.receivedBlocks[h]]
- ] IN
- [err |-> noErr, val |-> newSc]
-
- addNewBlocks(sc, newMph) ==
- \* add new blocks to be requested (e.g. when overall max peer height has changed)
- IF Cardinality(sc.blocks) >= numRequests THEN
- [newBlocks |-> sc.blocks, newBlockStates |-> sc.blockStates]
- ELSE
- LET requestSet == sc.height.. Min(numRequests+sc.height, newMph) IN
- LET heightsToRequest == {h \in requestSet: sc.blockStates[h] = "blockStateUnknown"} IN
- LET newBlockStates == [
- h \in Heights |-> IF h \in heightsToRequest THEN "blockStateNew"
- ELSE sc.blockStates[h]] IN
- [newBlocks |-> heightsToRequest, newBlockStates |-> newBlockStates]
-
- \* Update the peer height (the peer should have been previously added)
- setPeerHeight(sc, peerID, height) ==
- IF peerID \notin sc.peers THEN
- [err |-> "errPeerNotFound", val |-> sc]
- ELSE IF sc.peerStates[peerID] = "peerStateRemoved" THEN
- [err |-> "errUpdateRemovedPeer", val |-> sc]
- ELSE IF height < sc.peerHeights[peerID] THEN (* The peer is corrupt? Remove the peer. *)
- removePeer(sc, peerID)
- ELSE
- LET newPeerHeights == [sc.peerHeights EXCEPT ![peerID] = height] IN \* set the peer's height
- LET newPeerStates == [sc.peerStates EXCEPT ![peerID] = "peerStateReady"] IN \* set the peer's state
- LET newMph == maxHeight(newPeerStates, newPeerHeights) IN
- LET res == addNewBlocks(sc, newMph) IN
- LET newSc == [sc EXCEPT
- !.peerHeights = newPeerHeights, !.peerStates = newPeerStates,
- !.blocks = res.newBlocks, !.blockStates = res.newBlockStates] IN
- [err |-> noErr, val |-> newSc]
-
- nextHeightToSchedule(sc) ==
- LET toBeScheduled == {h \in DOMAIN sc.blockStates: sc.blockStates[h] = "blockStateNew"} \cup {ultimateHeight+1} IN
- CHOOSE minH \in toBeScheduled: \A h \in toBeScheduled: h >= minH
-
- getStateAtHeight(sc, h) ==
- IF h < sc.height THEN
- "blockStateProcessed"
- ELSE IF h \in DOMAIN sc.blockStates THEN
- sc.blockStates[h]
- ELSE
- "blockStateUnknown"
-
- markPending(sc, peerID, h) ==
- IF getStateAtHeight(sc, h) /= "blockStateNew" THEN
- [err |-> "errBadBlockState", val |-> sc]
- ELSE IF peerID \notin sc.peers \/ sc.peerStates[peerID] /= "peerStateReady" THEN
- [err |-> "errBadPeerState", val |-> sc]
- ELSE IF h > sc.peerHeights[peerID] THEN
- [err |-> "errPeerTooShort", val |-> sc]
- ELSE
- LET newSc == [sc EXCEPT
- !.blockStates = [sc.blockStates EXCEPT ![h] = "blockStatePending"],
- !.pendingBlocks = [sc.pendingBlocks EXCEPT ![h] = peerID]] IN
- [err |-> noErr, val |-> newSc]
-
- markReceived(sc, peerID, h) ==
- IF peerID \notin sc.peers \/ sc.peerStates[peerID] /= "peerStateReady" THEN
- [err |-> "errBadPeerState", val |-> sc]
- ELSE IF getStateAtHeight(sc, h) /= "blockStatePending" \/ sc.pendingBlocks[h] /= peerID THEN
- [err |-> "errBadPeer", val |-> sc]
- ELSE
- LET newSc == [sc EXCEPT
- !.blockStates = [sc.blockStates EXCEPT ![h] = "blockStateReceived"],
- !.pendingBlocks = [sc.pendingBlocks EXCEPT ![h] = None],
- !.receivedBlocks = [sc.receivedBlocks EXCEPT ![h] = peerID]] IN
- [err |-> noErr, val |-> newSc]
-
- markProcessed(sc, h) ==
- IF getStateAtHeight(sc, h) /= "blockStateReceived" THEN
- [err |-> "errProcessedBlockEv", val |-> sc]
- ELSE
- LET newSc == [sc EXCEPT
- !.blockStates = [sc.blockStates EXCEPT ![h] = "blockStateProcessed"],
- !.receivedBlocks = [sc.receivedBlocks EXCEPT ![h] = None],
- !.height = sc.height + 1] IN
- [err |-> noErr, val |-> newSc]
-
- reachedMaxHeight(sc) ==
- IF sc.peers = {} THEN
- FALSE
- ELSE
- LET maxH == maxHeightScheduler(sc) IN
- maxH > 0 /\ (sc.height >= maxH)
-
- highPeers(sc, minH) == {p \in sc.peers: sc.peerHeights[p] >= minH}
-
- (* ----------------------------------------------------------------------------------------------*)
- (* The behavior of the scheduler state machine *)
- (* See scheduler.go *)
- (* https://github.com/tendermint/tendermint/tree/brapse/blockchain-v2-riri-reactor-2/scheduler.go*)
- (* ----------------------------------------------------------------------------------------------*)
- blStateInit(h, start) ==
- IF h <= start THEN
- "blockStateProcessed"
- ELSE "blockStateUnknown"
-
- InitSc ==
- /\ scRunning = TRUE
- /\ outEvent = noEvent
- /\ \E startHeight \in Heights:
- scheduler = [
- initHeight |-> startHeight,
- height |-> startHeight + 1,
- peers |-> {},
- peerHeights |-> [p \in PeerIDs |-> None],
- peerStates |-> [p \in PeerIDs |-> "peerStateUnknown"],
- blocks |-> {},
- blockStates |-> [h \in Heights |-> blStateInit(h, startHeight)],
- pendingBlocks |-> [h \in Heights |-> None],
- receivedBlocks |-> [h \in Heights |-> None]
- ]
-
- handleAddNewPeer ==
- /\ inEvent.type = "bcAddNewPeer"
- /\ LET res == addPeer(scheduler, inEvent.peerID) IN
- IF res.err /= noErr THEN
- /\ outEvent' = [type |-> "scSchedulerFail", error |-> res.err]
- /\ UNCHANGED <<scheduler>>
- ELSE
- /\ scheduler' = res.val
- /\ UNCHANGED outEvent
- /\ UNCHANGED scRunning
-
- finishSc(event) ==
- event.type = "scFinishedEv" /\ event.error = "finished"
-
- handleRemovePeer ==
- /\ inEvent.type = "bcRemovePeer"
- /\ LET res == removePeer(scheduler, inEvent.peerID) IN
- IF res.err /= noErr THEN
- /\ outEvent' = [type |-> "scSchedulerFail", error |-> res.err]
- /\ UNCHANGED scheduler
- ELSE
- /\ scheduler' = res.val
- /\ IF reachedMaxHeight(scheduler') THEN
- outEvent' = [type |-> "scFinishedEv", error |-> "errAfterPeerRemove"]
- ELSE
- UNCHANGED outEvent
- /\ IF finishSc(outEvent') THEN
- scRunning' = FALSE
- ELSE UNCHANGED scRunning
-
- handleStatusResponse ==
- /\ inEvent.type = "bcStatusResponse"
- /\ LET res == setPeerHeight(scheduler, inEvent.peerID, inEvent.height) IN
- IF res.err /= noErr THEN
- /\ outEvent' = [type |-> "scPeerError", peerID |-> inEvent.peerID, error |-> res.err]
- /\ UNCHANGED scheduler
- ELSE
- /\ scheduler' = res.val
- /\ UNCHANGED outEvent
- /\ UNCHANGED scRunning
-
- handleTrySchedule == \* every 10 ms, but our spec is asynchronous
- /\ inEvent.type = "rTrySchedule"
- /\ LET minH == nextHeightToSchedule(scheduler) IN
- IF minH = ultimateHeight+1 THEN
- /\ outEvent' = noEvent
- /\ UNCHANGED scheduler
- ELSE IF minH = ultimateHeight+1 THEN
- /\ outEvent' = noEvent
- /\ UNCHANGED scheduler
- ELSE
- /\ LET hp == highPeers(scheduler, minH) IN
- IF hp = {} THEN
- /\ outEvent' = noEvent
- /\ UNCHANGED scheduler
- ELSE \E bestPeerID \in hp:
- /\ LET res == markPending(scheduler, bestPeerID, minH) IN
- /\ IF res.err /= noErr THEN
- outEvent' = [type |-> "scSchedulerFail", error|-> res.err]
- ELSE
- outEvent' = [type |-> "scBlockRequest", peerID |-> bestPeerID, height |-> minH]
- /\ scheduler' = res.val
- /\ UNCHANGED scRunning
-
- handleBlockResponse ==
- /\ inEvent.type = "bcBlockResponse"
- /\ LET res == markReceived(scheduler, inEvent.peerID, inEvent.height) IN
- IF res.err /= noErr THEN
- LET res1 == removePeer(scheduler, inEvent.peerID) IN
- /\ outEvent' = [type |-> "scPeerError", peerID |-> inEvent.peerID, error |-> res.err]
- /\ scheduler' = res1.val
- ELSE
- /\ outEvent' = [type |-> "scBlockReceived", peerID |-> inEvent.peerID, block |-> inEvent.block]
- /\ scheduler' = res.val
- /\ UNCHANGED scRunning
-
- handleNoBlockResponse ==
- /\ inEvent.type = "bcNoBlockResponse"
- /\ IF (scheduler.peers = {} \/ scheduler.peerStates[inEvent.peerID] = "peerStateRemoved") THEN
- /\ outEvent' = noEvent
- /\ UNCHANGED scheduler
- ELSE
- LET res == removePeer(scheduler, inEvent.peerID) IN
- /\ outEvent' = [type |-> "scPeerError", peerID |-> inEvent.peerID, error |-> "errPeerNoBlock"]
- /\ scheduler' = res.val
- /\ UNCHANGED scRunning
-
- handleBlockProcessed ==
- /\ inEvent.type = "pcBlockProcessed"
- /\ IF inEvent.height /= scheduler.height THEN
- /\ outEvent' = [type |-> "scSchedulerFail", error |-> "errProcessedBlockEv"]
- /\ UNCHANGED scheduler
- ELSE
- LET res == markProcessed(scheduler, inEvent.height) IN
- IF res.err /= noErr THEN
- /\ outEvent' = [type |-> "scSchedulerFail", error |-> res.err]
- /\ UNCHANGED scheduler
- ELSE
- /\ scheduler' = res.val
- /\ IF reachedMaxHeight(scheduler') THEN
- outEvent' = [type |-> "scFinishedEv", error |-> "finished"]
- ELSE
- outEvent' = noEvent
- /\ IF finishSc(outEvent') THEN
- scRunning' = FALSE
- ELSE UNCHANGED scRunning
-
- handleBlockProcessError ==
- /\ inEvent.type = "pcBlockVerificationFailure"
- /\ IF scheduler.peers = {} THEN
- /\ outEvent' = noEvent
- /\ UNCHANGED scheduler
- ELSE
- LET res1 == removePeer(scheduler, inEvent.firstPeerID) IN
- LET res2 == removePeer(res1.val, inEvent.secondPeerID) IN
- /\ IF reachedMaxHeight(res2.val) THEN
- outEvent' = [type |-> "scFinishedEv", error |-> "finished"]
- ELSE
- outEvent' = noEvent
- /\ scheduler' = res2.val
- /\ IF finishSc(outEvent') THEN
- scRunning' = FALSE
- ELSE UNCHANGED scRunning
-
- handleNoAdvanceExp ==
- /\ inEvent.type = "tNoAdvanceExp"
- /\ outEvent' = [type |-> "scFinishedEv", error |-> "timeout"]
- /\ scRunning' = FALSE
- /\ UNCHANGED <<scheduler>>
-
- NextSc ==
- IF ~scRunning THEN
- UNCHANGED <<outEvent, scRunning, scheduler>>
- ELSE
- \/ handleStatusResponse
- \/ handleAddNewPeer
- \/ handleRemovePeer
- \/ handleTrySchedule
- \/ handleBlockResponse
- \/ handleNoBlockResponse
- \/ handleBlockProcessed
- \/ handleBlockProcessError
- \/ handleNoAdvanceExp
-
- (* ----------------------------------------------------------------------------------------------*)
- (* The behavior of the environment. *)
- (* ----------------------------------------------------------------------------------------------*)
-
- InitEnv ==
- /\ inEvent = noEvent
- /\ envRunning = TRUE
-
- OnGlobalTimeoutTicker ==
- /\ inEvent' = [type |-> "tNoAdvanceExp"]
- /\ envRunning' = FALSE
-
- OnTrySchedule ==
- /\ inEvent' = [type |-> "rTrySchedule"]
- /\ UNCHANGED envRunning
-
- OnAddPeerEv ==
- /\ inEvent' \in [type: {"bcAddNewPeer"}, peerID: PeerIDs]
- /\ UNCHANGED envRunning
-
- OnStatusResponseEv ==
- \* any status response can come from the blockchain, pick one non-deterministically
- /\ inEvent' \in [type: {"bcStatusResponse"}, peerID: PeerIDs, height: Heights]
- /\ UNCHANGED envRunning
-
- OnBlockResponseEv ==
- \* any block response can come from the blockchain, pick one non-deterministically
- /\ inEvent' \in [type: {"bcBlockResponse"}, peerID: PeerIDs, height: Heights, block: Blocks]
- /\ UNCHANGED envRunning
-
- OnNoBlockResponseEv ==
- \* any no block response can come from the blockchain, pick one non-deterministically
- /\ inEvent' \in [type: {"bcNoBlockResponse"}, peerID: PeerIDs, height: Heights]
- /\ UNCHANGED envRunning
-
- OnRemovePeerEv ==
- \* although bcRemovePeer admits an arbitrary set, we produce just a singleton
- /\ inEvent' \in [type: {"bcRemovePeer"}, peerID: PeerIDs]
- /\ UNCHANGED envRunning
-
- OnPcBlockProcessed ==
- /\ inEvent' \in [type: {"pcBlockProcessed"}, peerID: PeerIDs, height: Heights]
- /\ UNCHANGED envRunning
-
- OnPcBlockVerificationFailure ==
- /\ inEvent' \in [type: {"pcBlockVerificationFailure"}, firstPeerID: PeerIDs, secondPeerID: PeerIDs, height: Heights]
- /\ UNCHANGED envRunning
-
- \* messages from scheduler
- OnScFinishedEv ==
- /\ outEvent.type = "scFinishedEv"
- /\ envRunning' = FALSE \* stop the env
- /\ UNCHANGED inEvent
-
- NextEnv ==
- IF ~envRunning THEN
- UNCHANGED <<inEvent, envRunning>>
- ELSE
- \/ OnScFinishedEv
- \/ OnGlobalTimeoutTicker
- \/ OnAddPeerEv
- \/ OnTrySchedule
- \/ OnStatusResponseEv
- \/ OnBlockResponseEv
- \/ OnNoBlockResponseEv
- \/ OnRemovePeerEv
- \/ OnPcBlockProcessed
- \/ OnPcBlockVerificationFailure
-
- (* ----------------------------------------------------------------------------------------------*)
- (* The system is the composition of the environment and the schedule *)
- (* ----------------------------------------------------------------------------------------------*)
- Init == turn = "environment" /\ InitEnv /\ InitSc
-
- FlipTurn ==
- turn' = (
- IF turn = "scheduler" THEN
- "environment"
- ELSE
- "scheduler"
- )
-
- \* scheduler and environment alternate their steps (synchronous composition introduces more states)
- Next ==
- /\ FlipTurn
- /\ IF turn = "scheduler" THEN
- /\ NextSc
- /\ inEvent' = noEvent
- /\ UNCHANGED envRunning
- ELSE
- /\ NextEnv
- /\ outEvent' = noEvent
- /\ UNCHANGED <<scRunning, scheduler>>
-
- Spec == Init /\ [][Next]_vars /\ WF_turn(FlipTurn)
-
- (* ----------------------------------------------------------------------------------------------*)
- (* Invariants *)
- (* ----------------------------------------------------------------------------------------------*)
- TypeOK ==
- /\ turn \in {"scheduler", "environment"}
- /\ inEvent \in InEvents
- /\ envRunning \in BOOLEAN
- /\ outEvent \in OutEvents
- /\ scheduler \in [
- initHeight: Heights,
- height: Heights \cup {ultimateHeight + 1},
- peers: SUBSET PeerIDs,
- peerHeights: [PeerIDs -> Heights \cup {None}],
- peerStates: [PeerIDs -> PeerStates],
- blocks: SUBSET Heights,
- blockStates: [Heights -> BlockStates],
- pendingBlocks: [Heights -> PeerIDs \cup {None}],
- receivedBlocks: [Heights -> PeerIDs \cup {None}]
- ]
-
- (* ----------------------------------------------------------------------------------------------*)
- (* Helpers for Properties *)
- (* ----------------------------------------------------------------------------------------------*)
- NoFailuresAndTimeouts ==
- /\ inEvent.type /= "bcRemovePeer"
- /\ inEvent.type /= "bcNoBlockResponse"
- /\ inEvent.type /= "pcBlockVerificationFailure"
-
- \* simulate good peer behavior using this formula. Useful to show termination in the presence of good peers.
- GoodResponse ==
- \/ inEvent.type
- \in {"bcAddNewPeer", "bcStatusResponse", "bcBlockResponse", "pcBlockProcessed", "rTrySchedule"}
- \/ ~envRunning
-
- \* all blocks from initHeight up to max peer height have been processed
- AllRequiredBlocksProcessed ==
- LET maxH == Max(scheduler.height, maxHeightScheduler(scheduler)) IN
- LET processedBlocks == {h \in scheduler.initHeight.. maxH-1: scheduler.blockStates[h] = "blockStateProcessed"} IN
- scheduler.height >= maxH /\ Cardinality(processedBlocks) = scheduler.height - scheduler.initHeight
-
- IncreaseHeight ==
- (scheduler'.height > scheduler.height) \/ (scheduler.height >= ultimateHeight)
-
- (* ----------------------------------------------------------------------------------------------*)
- (* Expected properties *)
- (* ----------------------------------------------------------------------------------------------*)
- (* *)
- (* 1. TerminationWhenNoAdvance - termination if there are no correct peers. *)
- (* The model assumes the "noAdvance" timer expires and the "tNoAdvanceExp" event is received *)
- (* *)
- TerminationWhenNoAdvance ==
- (inEvent.type = "tNoAdvanceExp")
- => <>(outEvent.type = "scFinishedEv" /\ outEvent.error = "timeout")
-
- (* ----------------------------------------------------------------------------------------------*)
- (* *)
- (* 2. TerminationGoodPeers - *)
- (* termination when IncreaseHeight holds true, fastsync is progressing, all blocks processed *)
- (* *)
- TerminationGoodPeers ==
- (/\ scheduler.height < ultimateHeight
- /\ <>[]GoodResponse
- /\[]<>(<<IncreaseHeight>>_<<scheduler, turn>>)
- )
- => <>(outEvent.type = "scFinishedEv" /\ AllRequiredBlocksProcessed)
-
- (* This property is violated. It shows that the precondition of TerminationGoodPeers is not *)
- (* always FALSE *)
- TerminationGoodPeersPre ==
- (/\ scheduler.height < ultimateHeight
- /\ <>[]GoodResponse
- /\[]<>(<<IncreaseHeight>>_<<scheduler, turn>>)
- )
- => FALSE
-
- (* ----------------------------------------------------------------------------------------------*)
- (* 3. TerminationAllCases - *)
- (* any peer behavior, either terminates with all blocks processed or times out *)
- TerminationAllCases ==
- (/\ scheduler.height < ultimateHeight
- /\(([]<> (<<IncreaseHeight>>_<<scheduler, turn>>)) \/ <>(inEvent.type = "tNoAdvanceExp"))
- )
- => <>(outEvent.type = "scFinishedEv" /\ (AllRequiredBlocksProcessed \/ outEvent.error = "timeout"))
-
- (* This property is violated. It shows that the precondition of TerminationAllCases is not *)
- (* always FALSE *)
- TerminationAllCasesPre ==
- (/\ scheduler.height < ultimateHeight
- /\(([]<> (<<IncreaseHeight>>_<<scheduler, turn>>)) \/ <>(inEvent.type = "tNoAdvanceExp"))
- )
- => FALSE
-
- (* This property is violated. TLC output shows an example of increasing heights in the scheduler *)
- SchedulerIncreasePre ==
- []<>(<<IncreaseHeight>>_<<scheduler, turn>>)
- => FALSE
-
- =============================================================================
- \* Modification History
- \* Last modified Thu Apr 16 13:21:33 CEST 2020 by ancaz
- \* Created Sat Feb 08 13:12:30 CET 2020 by ancaz
|