|
|
- ------------------------ MODULE Blockchain_002_draft -----------------------------
- (*
- This is a high-level specification of Tendermint blockchain
- that is designed specifically for the light client.
- Validators have the voting power of one. If you like to model various
- voting powers, introduce multiple copies of the same validator
- (do not forget to give them unique names though).
- *)
- EXTENDS Integers, FiniteSets
-
- Min(a, b) == IF a < b THEN a ELSE b
-
- CONSTANT
- AllNodes,
- (* a set of all nodes that can act as validators (correct and faulty) *)
- ULTIMATE_HEIGHT,
- (* a maximal height that can be ever reached (modelling artifact) *)
- TRUSTING_PERIOD
- (* the period within which the validators are trusted *)
-
- Heights == 1..ULTIMATE_HEIGHT (* possible heights *)
-
- (* A commit is just a set of nodes who have committed the block *)
- Commits == SUBSET AllNodes
-
- (* The set of all block headers that can be on the blockchain.
- This is a simplified version of the Block data structure in the actual implementation. *)
- BlockHeaders == [
- height: Heights,
- \* the block height
- time: Int,
- \* the block timestamp in some integer units
- lastCommit: Commits,
- \* the nodes who have voted on the previous block, the set itself instead of a hash
- (* in the implementation, only the hashes of V and NextV are stored in a block,
- as V and NextV are stored in the application state *)
- VS: SUBSET AllNodes,
- \* the validators of this bloc. We store the validators instead of the hash.
- NextVS: SUBSET AllNodes
- \* the validators of the next block. We store the next validators instead of the hash.
- ]
-
- (* A signed header is just a header together with a set of commits *)
- LightBlocks == [header: BlockHeaders, Commits: Commits]
-
- VARIABLES
- now,
- (* the current global time in integer units *)
- blockchain,
- (* A sequence of BlockHeaders, which gives us a bird view of the blockchain. *)
- Faulty
- (* A set of faulty nodes, which can act as validators. We assume that the set
- of faulty processes is non-decreasing. If a process has recovered, it should
- connect using a different id. *)
-
- (* all variables, to be used with UNCHANGED *)
- vars == <<now, blockchain, Faulty>>
-
- (* The set of all correct nodes in a state *)
- Corr == AllNodes \ Faulty
-
- (* APALACHE annotations *)
- a <: b == a \* type annotation
-
- NT == STRING
- NodeSet(S) == S <: {NT}
- EmptyNodeSet == NodeSet({})
-
- BT == [height |-> Int, time |-> Int, lastCommit |-> {NT}, VS |-> {NT}, NextVS |-> {NT}]
-
- LBT == [header |-> BT, Commits |-> {NT}]
- (* end of APALACHE annotations *)
-
- (****************************** BLOCKCHAIN ************************************)
-
- (* the header is still within the trusting period *)
- InTrustingPeriod(header) ==
- now < header.time + TRUSTING_PERIOD
-
- (*
- Given a function pVotingPower \in D -> Powers for some D \subseteq AllNodes
- and pNodes \subseteq D, test whether the set pNodes \subseteq AllNodes has
- more than 2/3 of voting power among the nodes in D.
- *)
- TwoThirds(pVS, pNodes) ==
- LET TP == Cardinality(pVS)
- SP == Cardinality(pVS \intersect pNodes)
- IN
- 3 * SP > 2 * TP \* when thinking in real numbers, not integers: SP > 2.0 / 3.0 * TP
-
- (*
- Given a set of FaultyNodes, test whether the voting power of the correct nodes in D
- is more than 2/3 of the voting power of the faulty nodes in D.
- *)
- IsCorrectPower(pFaultyNodes, pVS) ==
- LET FN == pFaultyNodes \intersect pVS \* faulty nodes in pNodes
- CN == pVS \ pFaultyNodes \* correct nodes in pNodes
- CP == Cardinality(CN) \* power of the correct nodes
- FP == Cardinality(FN) \* power of the faulty nodes
- IN
- \* CP + FP = TP is the total voting power, so we write CP > 2.0 / 3 * TP as follows:
- CP > 2 * FP \* Note: when FP = 0, this implies CP > 0.
-
- (* This is what we believe is the assumption about failures in Tendermint *)
- FaultAssumption(pFaultyNodes, pNow, pBlockchain) ==
- \A h \in Heights:
- pBlockchain[h].time + TRUSTING_PERIOD > pNow =>
- IsCorrectPower(pFaultyNodes, pBlockchain[h].NextVS)
-
- (* Can a block be produced by a correct peer, or an authenticated Byzantine peer *)
- IsLightBlockAllowedByDigitalSignatures(ht, block) ==
- \/ block.header = blockchain[ht] \* signed by correct and faulty (maybe)
- \/ block.Commits \subseteq Faulty /\ block.header.height = ht /\ block.header.time >= 0 \* signed only by faulty
-
- (*
- Initialize the blockchain to the ultimate height right in the initial states.
- We pick the faulty validators statically, but that should not affect the light client.
- *)
- InitToHeight ==
- /\ Faulty \in SUBSET AllNodes \* some nodes may fail
- \* pick the validator sets and last commits
- /\ \E vs, lastCommit \in [Heights -> SUBSET AllNodes]:
- \E timestamp \in [Heights -> Int]:
- \* now is at least as early as the timestamp in the last block
- /\ \E tm \in Int: now = tm /\ tm >= timestamp[ULTIMATE_HEIGHT]
- \* the genesis starts on day 1
- /\ timestamp[1] = 1
- /\ vs[1] = AllNodes
- /\ lastCommit[1] = EmptyNodeSet
- /\ \A h \in Heights \ {1}:
- /\ lastCommit[h] \subseteq vs[h - 1] \* the non-validators cannot commit
- /\ TwoThirds(vs[h - 1], lastCommit[h]) \* the commit has >2/3 of validator votes
- /\ IsCorrectPower(Faulty, vs[h]) \* the correct validators have >2/3 of power
- /\ timestamp[h] > timestamp[h - 1] \* the time grows monotonically
- /\ timestamp[h] < timestamp[h - 1] + TRUSTING_PERIOD \* but not too fast
- \* form the block chain out of validator sets and commits (this makes apalache faster)
- /\ blockchain = [h \in Heights |->
- [height |-> h,
- time |-> timestamp[h],
- VS |-> vs[h],
- NextVS |-> IF h < ULTIMATE_HEIGHT THEN vs[h + 1] ELSE AllNodes,
- lastCommit |-> lastCommit[h]]
- ] \******
-
-
- (* is the blockchain in the faulty zone where the Tendermint security model does not apply *)
- InFaultyZone ==
- ~FaultAssumption(Faulty, now, blockchain)
-
- (********************* BLOCKCHAIN ACTIONS ********************************)
- (*
- Advance the clock by zero or more time units.
- *)
- AdvanceTime ==
- \E tm \in Int: tm >= now /\ now' = tm
- /\ UNCHANGED <<blockchain, Faulty>>
-
- (*
- One more process fails. As a result, the blockchain may move into the faulty zone.
- The light client is not using this action, as the faults are picked in the initial state.
- However, this action may be useful when reasoning about fork detection.
- *)
- OneMoreFault ==
- /\ \E n \in AllNodes \ Faulty:
- /\ Faulty' = Faulty \cup {n}
- /\ Faulty' /= AllNodes \* at least process remains non-faulty
- /\ UNCHANGED <<now, blockchain>>
- =============================================================================
- \* Modification History
- \* Last modified Wed Jun 10 14:10:54 CEST 2020 by igor
- \* Created Fri Oct 11 15:45:11 CEST 2019 by igor
|