spec: Sync Light Client TLA+ code with master (#9238)

* Typo fix in README.md (#350)

* Updated Apalache type annotations (#395)

Co-authored-by: Prajjwol Gautam <prajjwol@gmail.com>
Co-authored-by: Kukovec <jure.kukovec@gmail.com>
This commit is contained in:
Thane Thomson
2022-08-12 14:33:47 -04:00
committed by GitHub
parent f36999e484
commit cbc7a1abcf
13 changed files with 429 additions and 94 deletions

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n4_f1 ------------------------------- ----------------------------- MODULE MC_n4_f1 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1", "c2", "c3"}, Corr <- {"c1", "c2", "c3"},

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n4_f2 ------------------------------- ----------------------------- MODULE MC_n4_f2 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1", "c2"}, Corr <- {"c1", "c2"},

View File

@@ -1,20 +1,42 @@
---------------------- MODULE MC_n4_f2_amnesia ------------------------------- ---------------------- MODULE MC_n4_f2_amnesia -------------------------------
EXTENDS Sequences EXTENDS Sequences
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
\* the variable declared in TendermintAccTrace3 \* the variable declared in TendermintAccTrace3
VARIABLE VARIABLE
\* @type: TRACE;
toReplay toReplay
\* old apalache annotations, fix with the new release
a <: b == a
INSTANCE TendermintAccTrace_004_draft WITH INSTANCE TendermintAccTrace_004_draft WITH
Corr <- {"c1", "c2"}, Corr <- {"c1", "c2"},
Faulty <- {"f3", "f4"}, Faulty <- {"f3", "f4"},
@@ -31,7 +53,7 @@ INSTANCE TendermintAccTrace_004_draft WITH
"UponProposalInPropose", "UponProposalInPropose",
"UponProposalInPrevoteOrCommitAndPrevote", "UponProposalInPrevoteOrCommitAndPrevote",
"UponProposalInPrecommitNoDecision" "UponProposalInPrecommitNoDecision"
>> <: Seq(STRING) >>
\* run Apalache with --cinit=ConstInit \* run Apalache with --cinit=ConstInit
ConstInit == \* the proposer is arbitrary -- works for safety ConstInit == \* the proposer is arbitrary -- works for safety

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n4_f3 ------------------------------- ----------------------------- MODULE MC_n4_f3 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1"}, Corr <- {"c1"},

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n5_f1 ------------------------------- ----------------------------- MODULE MC_n5_f1 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1", "c2", "c3", "c4"}, Corr <- {"c1", "c2", "c3", "c4"},

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n5_f2 ------------------------------- ----------------------------- MODULE MC_n5_f2 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1", "c2", "c3"}, Corr <- {"c1", "c2", "c3"},

View File

@@ -1,10 +1,34 @@
----------------------------- MODULE MC_n6_f1 ------------------------------- ----------------------------- MODULE MC_n6_f1 -------------------------------
CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N CONSTANT
\* @type: ROUND -> PROCESS;
Proposer
\* the variables declared in TendermintAcc3 \* the variables declared in TendermintAcc3
VARIABLES VARIABLES
round, step, decision, lockedValue, lockedRound, validValue, validRound, \* @type: PROCESS -> ROUND;
msgsPropose, msgsPrevote, msgsPrecommit, evidence, action round,
\* @type: PROCESS -> STEP;
step,
\* @type: PROCESS -> VALUE;
decision,
\* @type: PROCESS -> VALUE;
lockedValue,
\* @type: PROCESS -> ROUND;
lockedRound,
\* @type: PROCESS -> VALUE;
validValue,
\* @type: PROCESS -> ROUND;
validRound,
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote,
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit,
\* @type: Set(MESSAGE);
evidence,
\* @type: ACTION;
action
INSTANCE TendermintAccDebug_004_draft WITH INSTANCE TendermintAccDebug_004_draft WITH
Corr <- {"c1", "c2", "c3", "c4", "c5"}, Corr <- {"c1", "c2", "c3", "c4", "c5"},

View File

@@ -22,7 +22,7 @@ The agreement property says that for a given height, any two correct validators
However, faulty nodes may forge blocks and try to convince users (light clients) that the blocks had been correctly generated. In addition, Tendermint agreement might be violated in the case where 1/3 or more of the voting power belongs to faulty validators: Two correct validators decide on different blocks. The latter case motivates the term "fork": as Tendermint consensus also agrees on the next validator set, correct validators may have decided on disjoint next validator sets, and the chain branches into two or more partitions (possibly having faulty validators in common) and each branch continues to generate blocks independently of the other. However, faulty nodes may forge blocks and try to convince users (light clients) that the blocks had been correctly generated. In addition, Tendermint agreement might be violated in the case where 1/3 or more of the voting power belongs to faulty validators: Two correct validators decide on different blocks. The latter case motivates the term "fork": as Tendermint consensus also agrees on the next validator set, correct validators may have decided on disjoint next validator sets, and the chain branches into two or more partitions (possibly having faulty validators in common) and each branch continues to generate blocks independently of the other.
We say that a fork is a case in which there are two commits for different blocks at the same height of the blockchain. The proplem is to ensure that in those cases we are able to detect faulty validators (and not mistakenly accuse correct validators), and incentivize therefore validators to behave according to the protocol specification. We say that a fork is a case in which there are two commits for different blocks at the same height of the blockchain. The problem is to ensure that in those cases we are able to detect faulty validators (and not mistakenly accuse correct validators), and incentivize therefore validators to behave according to the protocol specification.
**Conceptual Limit.** In order to prove misbehavior of a node, we have to show that the behavior deviates from correct behavior with respect to a given algorithm. Thus, an algorithm that detects misbehavior of nodes executing some algorithm *A* must be defined with respect to algorithm *A*. In our case, *A* is Tendermint consensus (+ other protocols in the infrastructure; e.g.,full nodes and the Light Client). If the consensus algorithm is changed/updated/optimized in the future, we have to check whether changes to the accountability algorithm are also required. All the discussions in this document are thus inherently specific to Tendermint consensus and the Light Client specification. **Conceptual Limit.** In order to prove misbehavior of a node, we have to show that the behavior deviates from correct behavior with respect to a given algorithm. Thus, an algorithm that detects misbehavior of nodes executing some algorithm *A* must be defined with respect to algorithm *A*. In our case, *A* is Tendermint consensus (+ other protocols in the infrastructure; e.g.,full nodes and the Light Client). If the consensus algorithm is changed/updated/optimized in the future, we have to check whether changes to the accountability algorithm are also required. All the discussions in this document are thus inherently specific to Tendermint consensus and the Light Client specification.

View File

@@ -19,6 +19,7 @@ NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages
\* rounds to sets of messages. \* rounds to sets of messages.
\* Importantly, there will be exactly k messages in the image of msgFun. \* Importantly, there will be exactly k messages in the image of msgFun.
\* We use this action to produce k faults in an initial state. \* We use this action to produce k faults in an initial state.
\* @type: (ROUND -> Set(MESSAGE), Set(MESSAGE), Int) => Bool;
ProduceFaults(msgFun, From, k) == ProduceFaults(msgFun, From, k) ==
\E f \in [1..k -> From]: \E f \in [1..k -> From]:
msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}] msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}]
@@ -50,14 +51,14 @@ InitFewFaults ==
/\ validValue = [p \in Corr |-> NilValue] /\ validValue = [p \in Corr |-> NilValue]
/\ validRound = [p \in Corr |-> NilRound] /\ validRound = [p \in Corr |-> NilRound]
/\ ProduceFaults(msgsPrevote', /\ ProduceFaults(msgsPrevote',
SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), [type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values],
NFaultyPrevotes) NFaultyPrevotes)
/\ ProduceFaults(msgsPrecommit', /\ ProduceFaults(msgsPrecommit',
SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), [type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values],
NFaultyPrecommits) NFaultyPrecommits)
/\ ProduceFaults(msgsPropose', /\ ProduceFaults(msgsPropose',
SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, [type: {"PROPOSAL"}, src: Faulty, round: Rounds,
proposal: Values, validRound: Rounds \cup {NilRound}]), proposal: Values, validRound: Rounds \cup {NilRound}],
NFaultyProposals) NFaultyProposals)
/\ evidence = EmptyMsgSet /\ evidence = EmptyMsgSet

View File

@@ -13,24 +13,27 @@ EXTENDS TendermintAcc_004_draft
(************************** TYPE INVARIANT ***********************************) (************************** TYPE INVARIANT ***********************************)
(* first, we define the sets of all potential messages *) (* first, we define the sets of all potential messages *)
\* @type: Set(PROPMESSAGE);
AllProposals == AllProposals ==
SetOfMsgs([type: {"PROPOSAL"}, [type: {"PROPOSAL"},
src: AllProcs, src: AllProcs,
round: Rounds, round: Rounds,
proposal: ValuesOrNil, proposal: ValuesOrNil,
validRound: RoundsOrNil]) validRound: RoundsOrNil]
\* @type: Set(PREMESSAGE);
AllPrevotes == AllPrevotes ==
SetOfMsgs([type: {"PREVOTE"}, [type: {"PREVOTE"},
src: AllProcs, src: AllProcs,
round: Rounds, round: Rounds,
id: ValuesOrNil]) id: ValuesOrNil]
\* @type: Set(PREMESSAGE);
AllPrecommits == AllPrecommits ==
SetOfMsgs([type: {"PRECOMMIT"}, [type: {"PRECOMMIT"},
src: AllProcs, src: AllProcs,
round: Rounds, round: Rounds,
id: ValuesOrNil]) id: ValuesOrNil]
(* the standard type invariant -- importantly, it is inductive *) (* the standard type invariant -- importantly, it is inductive *)
TypeOK == TypeOK ==
@@ -226,7 +229,7 @@ LatestPrecommitHasLockedRound(p) ==
LET pPrecommits == LET pPrecommits ==
{mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p /\ mm.id /= NilValue } {mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p /\ mm.id /= NilValue }
IN IN
pPrecommits /= {} <: {MT} pPrecommits /= {}
=> LET latest == => LET latest ==
CHOOSE m \in pPrecommits: CHOOSE m \in pPrecommits:
\A m2 \in pPrecommits: \A m2 \in pPrecommits:
@@ -242,6 +245,7 @@ AllLatestPrecommitHasLockedRound ==
\* Every correct process sends only one value or NilValue. \* Every correct process sends only one value or NilValue.
\* This test has quantifier alternation -- a threat to all decision procedures. \* This test has quantifier alternation -- a threat to all decision procedures.
\* Luckily, the sets Corr and ValidValues are small. \* Luckily, the sets Corr and ValidValues are small.
\* @type: (ROUND, ROUND -> Set(PREMESSAGE)) => Bool;
NoEquivocationByCorrect(r, msgs) == NoEquivocationByCorrect(r, msgs) ==
\A p \in Corr: \A p \in Corr:
\E v \in ValidValues \union {NilValue}: \E v \in ValidValues \union {NilValue}:
@@ -250,6 +254,7 @@ NoEquivocationByCorrect(r, msgs) ==
\/ m.id = v \/ m.id = v
\* a proposer nevers sends two values \* a proposer nevers sends two values
\* @type: (ROUND, ROUND -> Set(PROPMESSAGE)) => Bool;
ProposalsByProposer(r, msgs) == ProposalsByProposer(r, msgs) ==
\* if the proposer is not faulty, it sends only one value \* if the proposer is not faulty, it sends only one value
\E v \in ValidValues: \E v \in ValidValues:
@@ -264,6 +269,7 @@ AllNoEquivocationByCorrect ==
/\ NoEquivocationByCorrect(r, msgsPrecommit) /\ NoEquivocationByCorrect(r, msgsPrecommit)
\* construct the set of the message senders \* construct the set of the message senders
\* @type: (Set(MESSAGE)) => Set(PROCESS);
Senders(M) == { m.src: m \in M } Senders(M) == { m.src: m \in M }
\* The final piece by Josef Widder: \* The final piece by Josef Widder:

View File

@@ -13,9 +13,13 @@ EXTENDS Sequences, Apalache, TendermintAcc_004_draft
\* a sequence of action names that should appear in the given order, \* a sequence of action names that should appear in the given order,
\* excluding "Init" \* excluding "Init"
CONSTANT Trace CONSTANT
\* @type: TRACE;
Trace
VARIABLE toReplay VARIABLE
\* @type: TRACE;
toReplay
TraceInit == TraceInit ==
/\ toReplay = Trace /\ toReplay = Trace

View File

@@ -37,31 +37,43 @@
Zarko Milosevic, Igor Konnov, Informal Systems, 2019-2020. Zarko Milosevic, Igor Konnov, Informal Systems, 2019-2020.
*) *)
EXTENDS Integers, FiniteSets EXTENDS Integers, FiniteSets, typedefs
(********************* PROTOCOL PARAMETERS **********************************) (********************* PROTOCOL PARAMETERS **********************************)
CONSTANTS CONSTANTS
\* @type: Set(PROCESS);
Corr, \* the set of correct processes Corr, \* the set of correct processes
\* @type: Set(PROCESS);
Faulty, \* the set of Byzantine processes, may be empty Faulty, \* the set of Byzantine processes, may be empty
\* @type: Int;
N, \* the total number of processes: correct, defective, and Byzantine N, \* the total number of processes: correct, defective, and Byzantine
\* @type: Int;
T, \* an upper bound on the number of Byzantine processes T, \* an upper bound on the number of Byzantine processes
\* @type: Set(VALUE);
ValidValues, \* the set of valid values, proposed both by correct and faulty ValidValues, \* the set of valid values, proposed both by correct and faulty
\* @type: Set(VALUE);
InvalidValues, \* the set of invalid values, never proposed by the correct ones InvalidValues, \* the set of invalid values, never proposed by the correct ones
\* @type: ROUND;
MaxRound, \* the maximal round number MaxRound, \* the maximal round number
Proposer \* the proposer function from 0..NRounds to 1..N \* @type: ROUND -> PROCESS;
Proposer \* the proposer function from Rounds to AllProcs
ASSUME(N = Cardinality(Corr \union Faulty)) ASSUME(N = Cardinality(Corr \union Faulty))
(*************************** DEFINITIONS ************************************) (*************************** DEFINITIONS ************************************)
AllProcs == Corr \union Faulty \* the set of all processes AllProcs == Corr \union Faulty \* the set of all processes
\* @type: Set(ROUND);
Rounds == 0..MaxRound \* the set of potential rounds Rounds == 0..MaxRound \* the set of potential rounds
\* @type: ROUND;
NilRound == -1 \* a special value to denote a nil round, outside of Rounds NilRound == -1 \* a special value to denote a nil round, outside of Rounds
RoundsOrNil == Rounds \union {NilRound} RoundsOrNil == Rounds \union {NilRound}
Values == ValidValues \union InvalidValues \* the set of all values Values == ValidValues \union InvalidValues \* the set of all values
\* @type: VALUE;
NilValue == "None" \* a special value for a nil round, outside of Values NilValue == "None" \* a special value for a nil round, outside of Values
ValuesOrNil == Values \union {NilValue} ValuesOrNil == Values \union {NilValue}
\* a value hash is modeled as identity \* a value hash is modeled as identity
\* @type: (t) => t;
Id(v) == v Id(v) == v
\* The validity predicate \* The validity predicate
@@ -72,36 +84,39 @@ THRESHOLD1 == T + 1 \* at least one process is not faulty
THRESHOLD2 == 2 * T + 1 \* a quorum when having N > 3 * T THRESHOLD2 == 2 * T + 1 \* a quorum when having N > 3 * T
(********************* TYPE ANNOTATIONS FOR APALACHE **************************) (********************* TYPE ANNOTATIONS FOR APALACHE **************************)
\* the operator for type annotations
a <: b == a
\* the type of message records \* An empty set of messages
MT == [type |-> STRING, src |-> STRING, round |-> Int, \* @type: Set(MESSAGE);
proposal |-> STRING, validRound |-> Int, id |-> STRING] EmptyMsgSet == {}
\* a type annotation for a message
AsMsg(m) == m <: MT
\* a type annotation for a set of messages
SetOfMsgs(S) == S <: {MT}
\* a type annotation for an empty set of messages
EmptyMsgSet == SetOfMsgs({})
(********************* PROTOCOL STATE VARIABLES ******************************) (********************* PROTOCOL STATE VARIABLES ******************************)
VARIABLES VARIABLES
\* @type: PROCESS -> ROUND;
round, \* a process round number: Corr -> Rounds round, \* a process round number: Corr -> Rounds
\* @type: PROCESS -> STEP;
step, \* a process step: Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" } step, \* a process step: Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" }
\* @type: PROCESS -> VALUE;
decision, \* process decision: Corr -> ValuesOrNil decision, \* process decision: Corr -> ValuesOrNil
\* @type: PROCESS -> VALUE;
lockedValue, \* a locked value: Corr -> ValuesOrNil lockedValue, \* a locked value: Corr -> ValuesOrNil
\* @type: PROCESS -> ROUND;
lockedRound, \* a locked round: Corr -> RoundsOrNil lockedRound, \* a locked round: Corr -> RoundsOrNil
\* @type: PROCESS -> VALUE;
validValue, \* a valid value: Corr -> ValuesOrNil validValue, \* a valid value: Corr -> ValuesOrNil
\* @type: PROCESS -> ROUND;
validRound \* a valid round: Corr -> RoundsOrNil validRound \* a valid round: Corr -> RoundsOrNil
\* book-keeping variables \* book-keeping variables
VARIABLES VARIABLES
\* @type: ROUND -> Set(PROPMESSAGE);
msgsPropose, \* PROPOSE messages broadcast in the system, Rounds -> Messages msgsPropose, \* PROPOSE messages broadcast in the system, Rounds -> Messages
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrevote, \* PREVOTE messages broadcast in the system, Rounds -> Messages msgsPrevote, \* PREVOTE messages broadcast in the system, Rounds -> Messages
\* @type: ROUND -> Set(PREMESSAGE);
msgsPrecommit, \* PRECOMMIT messages broadcast in the system, Rounds -> Messages msgsPrecommit, \* PRECOMMIT messages broadcast in the system, Rounds -> Messages
\* @type: Set(MESSAGE);
evidence, \* the messages that were used by the correct processes to make transitions evidence, \* the messages that were used by the correct processes to make transitions
\* @type: ACTION;
action \* we use this variable to see which action was taken action \* we use this variable to see which action was taken
(* to see a type invariant, check TendermintAccInv3 *) (* to see a type invariant, check TendermintAccInv3 *)
@@ -111,26 +126,63 @@ vars == <<round, step, decision, lockedValue, lockedRound,
validValue, validRound, evidence, msgsPropose, msgsPrevote, msgsPrecommit>> validValue, validRound, evidence, msgsPropose, msgsPrevote, msgsPrecommit>>
(********************* PROTOCOL INITIALIZATION ******************************) (********************* PROTOCOL INITIALIZATION ******************************)
\* @type: (ROUND) => Set(PROPMESSAGE);
FaultyProposals(r) == FaultyProposals(r) ==
SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, [
round: {r}, proposal: Values, validRound: RoundsOrNil]) type : {"PROPOSAL"},
src : Faulty,
round : {r},
proposal : Values,
validRound: RoundsOrNil
]
\* @type: Set(PROPMESSAGE);
AllFaultyProposals == AllFaultyProposals ==
SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, [
round: Rounds, proposal: Values, validRound: RoundsOrNil]) type : {"PROPOSAL"},
src : Faulty,
round : Rounds,
proposal : Values,
validRound: RoundsOrNil
]
\* @type: (ROUND) => Set(PREMESSAGE);
FaultyPrevotes(r) == FaultyPrevotes(r) ==
SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: {r}, id: Values]) [
type : {"PREVOTE"},
src : Faulty,
round: {r},
id : Values
]
\* @type: Set(PREMESSAGE);
AllFaultyPrevotes == AllFaultyPrevotes ==
SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]) [
type : {"PREVOTE"},
src : Faulty,
round: Rounds,
id : Values
]
\* @type: (ROUND) => Set(PREMESSAGE);
FaultyPrecommits(r) == FaultyPrecommits(r) ==
SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: {r}, id: Values]) [
type : {"PRECOMMIT"},
src : Faulty,
round: {r},
id : Values
]
\* @type: Set(PREMESSAGE);
AllFaultyPrecommits == AllFaultyPrecommits ==
SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]) [
type : {"PRECOMMIT"},
src : Faulty,
round: Rounds,
id : Values
]
\* @type: (ROUND -> Set(MESSAGE)) => Bool;
BenignRoundsInMessages(msgfun) == BenignRoundsInMessages(msgfun) ==
\* the message function never contains a message for a wrong round \* the message function never contains a message for a wrong round
\A r \in Rounds: \A r \in Rounds:
@@ -153,25 +205,49 @@ Init ==
/\ BenignRoundsInMessages(msgsPrevote) /\ BenignRoundsInMessages(msgsPrevote)
/\ BenignRoundsInMessages(msgsPrecommit) /\ BenignRoundsInMessages(msgsPrecommit)
/\ evidence = EmptyMsgSet /\ evidence = EmptyMsgSet
/\ action' = "Init" /\ action = "Init"
(************************ MESSAGE PASSING ********************************) (************************ MESSAGE PASSING ********************************)
\* @type: (PROCESS, ROUND, VALUE, ROUND) => Bool;
BroadcastProposal(pSrc, pRound, pProposal, pValidRound) == BroadcastProposal(pSrc, pRound, pProposal, pValidRound) ==
LET newMsg == LET
AsMsg([type |-> "PROPOSAL", src |-> pSrc, round |-> pRound, \* @type: PROPMESSAGE;
proposal |-> pProposal, validRound |-> pValidRound]) newMsg ==
[
type |-> "PROPOSAL",
src |-> pSrc,
round |-> pRound,
proposal |-> pProposal,
validRound |-> pValidRound
]
IN IN
msgsPropose' = [msgsPropose EXCEPT ![pRound] = msgsPropose[pRound] \union {newMsg}] msgsPropose' = [msgsPropose EXCEPT ![pRound] = msgsPropose[pRound] \union {newMsg}]
\* @type: (PROCESS, ROUND, VALUE) => Bool;
BroadcastPrevote(pSrc, pRound, pId) == BroadcastPrevote(pSrc, pRound, pId) ==
LET newMsg == AsMsg([type |-> "PREVOTE", LET
src |-> pSrc, round |-> pRound, id |-> pId]) \* @type: PREMESSAGE;
newMsg ==
[
type |-> "PREVOTE",
src |-> pSrc,
round |-> pRound,
id |-> pId
]
IN IN
msgsPrevote' = [msgsPrevote EXCEPT ![pRound] = msgsPrevote[pRound] \union {newMsg}] msgsPrevote' = [msgsPrevote EXCEPT ![pRound] = msgsPrevote[pRound] \union {newMsg}]
\* @type: (PROCESS, ROUND, VALUE) => Bool;
BroadcastPrecommit(pSrc, pRound, pId) == BroadcastPrecommit(pSrc, pRound, pId) ==
LET newMsg == AsMsg([type |-> "PRECOMMIT", LET
src |-> pSrc, round |-> pRound, id |-> pId]) \* @type: PREMESSAGE;
newMsg ==
[
type |-> "PRECOMMIT",
src |-> pSrc,
round |-> pRound,
id |-> pId
]
IN IN
msgsPrecommit' = [msgsPrecommit EXCEPT ![pRound] = msgsPrecommit[pRound] \union {newMsg}] msgsPrecommit' = [msgsPrecommit EXCEPT ![pRound] = msgsPrecommit[pRound] \union {newMsg}]
@@ -184,6 +260,7 @@ StartRound(p, r) ==
/\ step' = [step EXCEPT ![p] = "PROPOSE"] /\ step' = [step EXCEPT ![p] = "PROPOSE"]
\* lines 14-19, a proposal may be sent later \* lines 14-19, a proposal may be sent later
\* @type: (PROCESS) => Bool;
InsertProposal(p) == InsertProposal(p) ==
LET r == round[p] IN LET r == round[p] IN
/\ p = Proposer[r] /\ p = Proposer[r]
@@ -192,8 +269,13 @@ InsertProposal(p) ==
\* by the correct processes for the same round \* by the correct processes for the same round
/\ \A m \in msgsPropose[r]: m.src /= p /\ \A m \in msgsPropose[r]: m.src /= p
/\ \E v \in ValidValues: /\ \E v \in ValidValues:
LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN LET
BroadcastProposal(p, round[p], proposal, validRound[p]) \* @type: VALUE;
proposal ==
IF validValue[p] /= NilValue
THEN validValue[p]
ELSE v
IN BroadcastProposal(p, round[p], proposal, validRound[p])
/\ UNCHANGED <<evidence, round, decision, lockedValue, lockedRound, /\ UNCHANGED <<evidence, round, decision, lockedValue, lockedRound,
validValue, step, validRound, msgsPrevote, msgsPrecommit>> validValue, step, validRound, msgsPrevote, msgsPrecommit>>
/\ action' = "InsertProposal" /\ action' = "InsertProposal"
@@ -202,9 +284,17 @@ InsertProposal(p) ==
UponProposalInPropose(p) == UponProposalInPropose(p) ==
\E v \in Values: \E v \in Values:
/\ step[p] = "PROPOSE" (* line 22 *) /\ step[p] = "PROPOSE" (* line 22 *)
/\ LET msg == /\ LET
AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], \* @type: PROPMESSAGE;
round |-> round[p], proposal |-> v, validRound |-> NilRound]) IN msg ==
[
type |-> "PROPOSAL",
src |-> Proposer[round[p]],
round |-> round[p],
proposal |-> v,
validRound |-> NilRound
]
IN
/\ msg \in msgsPropose[round[p]] \* line 22 /\ msg \in msgsPropose[round[p]] \* line 22
/\ evidence' = {msg} \union evidence /\ evidence' = {msg} \union evidence
/\ LET mid == (* line 23 *) /\ LET mid == (* line 23 *)
@@ -222,9 +312,16 @@ UponProposalInPropose(p) ==
UponProposalInProposeAndPrevote(p) == UponProposalInProposeAndPrevote(p) ==
\E v \in Values, vr \in Rounds: \E v \in Values, vr \in Rounds:
/\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part
/\ LET msg == /\ LET
AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], \* @type: PROPMESSAGE;
round |-> round[p], proposal |-> v, validRound |-> vr]) msg ==
[
type |-> "PROPOSAL",
src |-> Proposer[round[p]],
round |-> round[p],
proposal |-> v,
validRound |-> vr
]
IN IN
/\ msg \in msgsPropose[round[p]] \* line 28 /\ msg \in msgsPropose[round[p]] \* line 28
/\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN
@@ -260,9 +357,17 @@ UponQuorumOfPrevotesAny(p) ==
UponProposalInPrevoteOrCommitAndPrevote(p) == UponProposalInPrevoteOrCommitAndPrevote(p) ==
\E v \in ValidValues, vr \in RoundsOrNil: \E v \in ValidValues, vr \in RoundsOrNil:
/\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36
/\ LET msg == /\ LET
AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], \* @type: PROPMESSAGE;
round |-> round[p], proposal |-> v, validRound |-> vr]) IN msg ==
[
type |-> "PROPOSAL",
src |-> Proposer[round[p]],
round |-> round[p],
proposal |-> v,
validRound |-> vr
]
IN
/\ msg \in msgsPropose[round[p]] \* line 36 /\ msg \in msgsPropose[round[p]] \* line 36
/\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN
/\ Cardinality(PV) >= THRESHOLD2 \* line 36 /\ Cardinality(PV) >= THRESHOLD2 \* line 36
@@ -299,8 +404,17 @@ UponQuorumOfPrecommitsAny(p) ==
UponProposalInPrecommitNoDecision(p) == UponProposalInPrecommitNoDecision(p) ==
/\ decision[p] = NilValue \* line 49 /\ decision[p] = NilValue \* line 49
/\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in RoundsOrNil: /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in RoundsOrNil:
/\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer[r], /\ LET
round |-> r, proposal |-> v, validRound |-> vr]) IN \* @type: PROPMESSAGE;
msg ==
[
type |-> "PROPOSAL",
src |-> Proposer[r],
round |-> r,
proposal |-> v,
validRound |-> vr
]
IN
/\ msg \in msgsPropose[r] \* line 49 /\ msg \in msgsPropose[r] \* line 49
/\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN
/\ Cardinality(PV) >= THRESHOLD2 \* line 49 /\ Cardinality(PV) >= THRESHOLD2 \* line 49
@@ -386,10 +500,18 @@ AmnesiaBy(p) ==
/\ r1 < r2 /\ r1 < r2
/\ \E v1, v2 \in ValidValues: /\ \E v1, v2 \in ValidValues:
/\ v1 /= v2 /\ v1 /= v2
/\ AsMsg([type |-> "PRECOMMIT", src |-> p, /\ [
round |-> r1, id |-> Id(v1)]) \in evidence type |-> "PRECOMMIT",
/\ AsMsg([type |-> "PREVOTE", src |-> p, src |-> p,
round |-> r2, id |-> Id(v2)]) \in evidence round |-> r1,
id |-> Id(v1)
] \in evidence
/\ [
type |-> "PREVOTE",
src |-> p,
round |-> r2,
id |-> Id(v2)
] \in evidence
/\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd < r2 }: /\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd < r2 }:
LET prevotes == LET prevotes ==
{ m \in evidence: { m \in evidence:

View File

@@ -0,0 +1,36 @@
-------------------- MODULE typedefs ---------------------------
(*
@typeAlias: PROCESS = Str;
@typeAlias: VALUE = Str;
@typeAlias: STEP = Str;
@typeAlias: ROUND = Int;
@typeAlias: ACTION = Str;
@typeAlias: TRACE = Seq(Str);
@typeAlias: PROPMESSAGE =
[
type: STEP,
src: PROCESS,
round: ROUND,
proposal: VALUE,
validRound: ROUND
];
@typeAlias: PREMESSAGE =
[
type: STEP,
src: PROCESS,
round: ROUND,
id: VALUE
];
@typeAlias: MESSAGE =
[
type: STEP,
src: PROCESS,
round: ROUND,
proposal: VALUE,
validRound: ROUND,
id: VALUE
];
*)
TypeAliases == TRUE
=============================================================================