From 3ba7fff02955c28e0620e132fe4d26d65db8f72e Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Mon, 4 Nov 2019 11:21:18 +0100 Subject: [PATCH 01/38] copied the tendermint spec from tendermint-safety --- spec/fork-cases/Tendermint.tla | 374 +++++++++++++++++++++++++++++++++ 1 file changed, 374 insertions(+) create mode 100644 spec/fork-cases/Tendermint.tla diff --git a/spec/fork-cases/Tendermint.tla b/spec/fork-cases/Tendermint.tla new file mode 100644 index 0000000..461d2e0 --- /dev/null +++ b/spec/fork-cases/Tendermint.tla @@ -0,0 +1,374 @@ +----------------------------- MODULE Tendermint ----------------------------- +(* + A TLA+ specification of Tendermint consensus by Ethan Buchman, Jae Kwon, and Zarko Milosevic. + + For the moment, we assume the following: + + 1. Every process has the voting power of 1. + 2. Timeouts are non-deterministic (works for safety). + 3. The proposer function is non-deterministic (works for safety). + + Encoded in TLA+ by Igor Konnov. It took me 4 hours to translate the pseudo-code to TLA+. + *) + +EXTENDS Integers, FiniteSets + +CONSTANTS + PropFun, \* the proposer function + Injected \* a set of the messages injected by the faulty processes + +N == 5 \* the total number of processes: correct and faulty +T == 1 \* an upper bound on the number of Byzantine processes +F == 1 \* the number of Byzantine processes +Procs == 1..N-F +Faulty == N-F+1..N +Heights == 0..1 \* the set of consensus instances +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {2} \* e.g., sent by a Byzantine process +Values == ValidValues \cup InvalidValues \* all values +nil == -1 + +\* these are two tresholds that are used in the algorithm +THRESHOLD1 == T + 1 +THRESHOLD2 == 2 * T + 1 + +(* APALACHE-BEGIN annotations *) +a <: b == a + +MT == [type |-> STRING, src |-> Int, h |-> Int, round |-> Int, + proposal |-> Int, validRound |-> Int, hash |-> Int] +ET == [type |-> STRING, src |-> Int, h |-> Int, round |-> Int, value |-> Int] \* processed events, "for the first time" + +ValueT == Int +RoundT == Int +TimeoutT == <> \* process, height, round +(* APALACHE-END *) + +FaultyMessages == \* the messages that can be sent by the faulty processes + ([type: {"PROPOSAL"}, src: Faulty, h: Heights, + round: Rounds, proposal: Values, validRound: Rounds \cup {-1}] <: {MT}) + \cup + ([type: {"PREVOTE"}, src: Faulty, h: Heights, round: Rounds, hash: Values] <: {MT}) + \cup + ([type: {"PRECOMMIT"}, src: Faulty, h: Heights, round: Rounds, hash: Values] <: {MT}) + +NInjected == 1 \* the number of injected faulty messages +ConstInit == + /\ PropFun \in [Heights \X Rounds -> Procs] + /\ Injected \in [1..NInjected -> FaultyMessages] + + +\* these variables are exactly as in the pseudo-code +VARIABLES h, round, step, decision, lockedValue, lockedRound, validValue, validRound + +\* book-keeping variables +VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages + oldEvents, \* the messages processed once, as expressed by "for the first time" + timeoutPropose, \* a set of proposed timeouts: <> + timeoutPrevote, \* a set of proposed timeouts: <> + timeoutPrecommit \* a set of proposed timeouts: <> + +\* this is needed for UNCHANGED +vars == <> + +\* A function which gives the proposer for a given round at a given height. +\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, +\* it does not really matter who starts first. +Proposer(ht, rd) == PropFun[ht, rd] \*1 + ((ht + rd) % N) + +Id(v) == v + +IsValid(v) == v \in ValidValues + +MixinFaults(ht, rd, type, msgs) == + \* add the messages from the faulty processes, filtered by height, round, and type + msgs \cup {m \in {Injected[x] : x \in 1..NInjected} : m.h = ht /\ m.round = rd /\ m.type = type} + +\* here we start with StartRound(0) +Init == + /\ h = [p \in Procs |-> 0] + /\ round = [p \in Procs |-> 0] + /\ step = [p \in Procs |-> "PROPOSE"] + /\ decision = [p \in Procs |-> [ht \in Heights |-> nil]] + /\ lockedValue = [p \in Procs |-> nil] + /\ lockedRound = [p \in Procs |-> -1] + /\ validValue = [p \in Procs |-> nil] + /\ validRound = [p \in Procs |-> -1] + /\ \E v \in ValidValues: + msgsPropose = [<> \in Heights \X Rounds |-> + MixinFaults(ht, rd, "PROPOSAL", + IF ht = 0 /\ rd = 0 + THEN {[type |-> "PROPOSAL", src |-> Proposer(0, 0), h |-> 0, round |-> 0, + proposal |-> v, validRound |-> -1] <: MT} + ELSE {} <: {MT})] \* no initial messages in other rounds + /\ msgsPrevote = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PREVOTE", {} <: {MT})] + /\ msgsPrecommit = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PRECOMMIT", {} <: {MT})] + /\ oldEvents = {} <: {ET} + /\ timeoutPropose = { <> : p \in Procs \ {Proposer(0, 0)}} + /\ timeoutPrevote = {} <: {TimeoutT} \* no PREVOTE timeouts + /\ timeoutPrecommit = {} <: {TimeoutT} \* no PRECOMMIT timeouts + +\* lines 22-27 +UponProposalInPropose(p) == + \E v \in Values: + /\ step[p] = "PROPOSE" \* line 22 + /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], + round |-> round[p], proposal |-> v, validRound |-> -1] <: MT) \in msgsPropose[h[p], round[p]] \* line 22 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 + LET newMsgs == ({[type |-> "PREVOTE", src |-> p, h |-> h[p], + round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil] <: MT}) + IN \* lines 24-26 + msgsPrevote' = [msgsPrevote EXCEPT ![h[p], round[p]] = + msgsPrevote[h[p], round[p]] \cup newMsgs] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 28-33 +UponProposalInProposeAndPrevote(p) == + \E v \in Values, vr \in Rounds: + /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part + /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], + round |-> round[p], proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], round[p]] \* line 28 + /\ LET PV == { m \in msgsPrevote[h[p], vr]: m.hash = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 28 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 + LET newMsgs == ({[type |-> "PREVOTE", src |-> p, h |-> h[p], + round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil] <: MT}) + IN \* lines 30-32 + msgsPrevote' = [msgsPrevote EXCEPT ![h[p], round[p]] = + msgsPrevote[h[p], round[p]] \cup newMsgs] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 34-35 +UponPrevoteFirstTime(p) == + /\ step[p] = "PREVOTE" \* line 34 + /\ Cardinality(msgsPrevote[h[p], round[p]]) >= THRESHOLD2 \* line 34 + /\ LET event == [type |-> "PREVOTE", src |-> p, h |-> h[p], round |-> round[p], value |-> nil] IN + /\ event \notin oldEvents \* for the first time + /\ oldEvents' = oldEvents \cup {event} \* process it only once + /\ timeoutPrevote' = timeoutPrevote \cup {<>} \* line 35 + /\ UNCHANGED <> + +\* lines 36-46 +UponProposalInPrevoteOrCommitAndPrevote(p) == + \E v \in ValidValues, vr \in Rounds \cup {-1}: + /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 + /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], + round |-> round[p], proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], round[p]] \* line 36 + /\ LET event == [type |-> "PREVOTE", src |-> p, h |-> h[p], round |-> round[p], value |-> Id(v)] IN + /\ event \notin oldEvents \* for the first time + /\ oldEvents' = oldEvents \cup {event} \* record that it should not happen again + /\ LET PV == { m \in msgsPrevote[h[p], round[p]]: m.hash = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ lockedValue' = + IF step[p] = "PREVOTE" + THEN [lockedValue EXCEPT ![p] = v] \* line 38 + ELSE lockedValue \* else of line 37 + /\ lockedRound' = + IF step[p] = "PREVOTE" + THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 + ELSE lockedRound \* else of line 37 + /\ LET newMsgs == + IF step[p] = "PREVOTE" + THEN {[type |-> "PRECOMMIT", src |-> p, h |-> h[p], round |-> round[p], hash |-> Id(v)] <: MT} \* line 40 + ELSE {} <: {MT} + IN \* else of line 37 + msgsPrecommit' = [msgsPrecommit EXCEPT ![h[p], round[p]] = + msgsPrecommit[h[p], round[p]] \cup newMsgs] \* line 40, or else of 37 + /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 + /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 + /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 + /\ UNCHANGED <> + +\* Apparently, this action is needed to deal with a value proposed by a Byzantine process +\* This action is particularly hard for the model checker +\* lines 44-46 +UponPrevoteNil(p) == + /\ step[p] = "PREVOTE" \* line 44 + /\ LET PV == { m \in msgsPrevote[h[p], round[p]]: m.hash = nil } IN + Cardinality(PV) >= THRESHOLD2 \* line 34 + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + /\ LET newMsgs == ({[type |-> "PRECOMMIT", src |-> p, h |-> h[p], + round |-> round[p], hash |-> nil] <: MT}) \* line 45 + IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![h[p], round[p]] = + msgsPrecommit[h[p], round[p]] \cup newMsgs] \* line 45 + /\ UNCHANGED <> + +\* lines 47-48 +UponPrecommitFirstTime(p) == + /\ Cardinality(msgsPrecommit[h[p], round[p]]) >= THRESHOLD2 \* line 47 + /\ LET event == [type |-> "PRECOMMIT", src |-> p, h |-> h[p], round |-> round[p], value |-> nil] IN + /\ event \notin oldEvents \* for the first time + /\ oldEvents' = oldEvents \cup {event} \* process it only once + /\ timeoutPrecommit' = timeoutPrecommit \cup {<>} \* line 48 + /\ UNCHANGED <> + +\* lines 11-21 +StartRound(p, ht, r) == + /\ round' = [round EXCEPT ![p] = r] + /\ step' = [step EXCEPT ![p] = "PROPOSE"] + /\ \E v \in ValidValues: \* lines 14-21 + LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN + LET newMsgs == + IF p = Proposer(ht, r) + THEN {[type |-> "PROPOSAL", src |-> p, h |-> ht, round |-> r, + proposal |-> proposal, validRound |-> validRound[p]] <: MT} + ELSE {} <: {MT} + IN + msgsPropose' = [msgsPropose EXCEPT ![ht, r] = + msgsPropose[ht, r] \cup newMsgs] \* line 19 + /\ LET newTimeouts == \* line 21 + IF p = Proposer(ht, r) + THEN {} <: {TimeoutT} \* no new timeouts + ELSE { <> } + IN + timeoutPropose' = timeoutPropose \cup newTimeouts + +\* lines 49-54 +UponProposalInPrecommitNoDecision(p) == + /\ h[p] + 1 \in Heights + \* THIS IS NOT PART OF THE ORIGINAL ALGORITHM, + \* BUT A SAFEGUARD TO PREVENT ROUNDS FROM OVERFLOWING + /\ decision[p][h[p]] = nil \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: + /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], r), h |-> h[p], + round |-> r, proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], r] \* line 49 + /\ LET PV == { m \in msgsPrecommit[h[p], r]: m.hash = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 49 + /\ decision' = [decision EXCEPT ![p][h[p]] = v] \* update the decision, line 51 + /\ h' = [h EXCEPT ![p] = h[p] + 1] \* line 52 + \* line 53 + /\ lockedRound' = [lockedRound EXCEPT ![p] = -1] + /\ lockedValue' = [lockedValue EXCEPT ![p] = nil] + /\ validRound' = [validRound EXCEPT ![p] = -1] + /\ validValue' = [validValue EXCEPT ![p] = nil] + \* What does it mean to reset the message buffer? Do it for one process only? + /\ StartRound(p, h[p] + 1, 0) + /\ UNCHANGED <> + +\* lines 55-56 +UponCatchupRound(p) == + \E r \in Rounds: + /\ r > round[p] + /\ LET PV == msgsPropose[h[p], r] \cup msgsPrevote[h[p], r] \cup msgsPrecommit[h[p], r] IN + Cardinality(PV) >= THRESHOLD1 \* line 55 + /\ StartRound(p, h[p], r) + /\ UNCHANGED <> + +\* lines 57-60 +OnTimeoutPropose(p) == + \E tm \in timeoutPropose: \* a timeout occurs + /\ tm[1] = p + /\ timeoutPropose' = timeoutPropose \ {tm} \* remove from the future timeouts + /\ LET UpdateNeeded == tm[2] = h[p] /\ tm[3] = round[p] /\ step[p] = "PROPOSE" IN + /\ step' = IF UpdateNeeded THEN [step EXCEPT ![p] = "PREVOTE"] ELSE step \* line 60 + /\ LET newMsgs == + IF UpdateNeeded + THEN {[type |-> "PREVOTE", src |-> tm[1], h |-> tm[2], round |-> tm[3], hash |-> nil] <: MT} \* line 59 + ELSE {} <: {MT} \* else of line 58 + IN \* line 59, or else of 58 + msgsPrevote' = [msgsPrevote EXCEPT ![tm[2], tm[3]] = + msgsPrevote[tm[2], tm[3]] \cup newMsgs] + /\ UNCHANGED <> + +\* lines 61-64 +OnTimeoutPrevote(p) == + \E tm \in timeoutPrevote: \* a timeout occurs + /\ tm[1] = p + /\ timeoutPrevote' = timeoutPrevote \ {tm} \* remove from the future timeouts + /\ LET UpdateNeeded == tm[2] = h[p] /\ tm[3] = round[p] /\ step[p] = "PREVOTE" IN + /\ step' = IF UpdateNeeded THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 64 + /\ LET newMsgs == + IF UpdateNeeded + THEN {[type |-> "PRECOMMIT", src |-> tm[1], h |-> tm[2], round |-> tm[3], hash |-> nil] <: MT} \* line 63 + ELSE {} <: {MT} \* else of line 62 + IN \* line 63, or else of 62 + msgsPrecommit' = [msgsPrecommit EXCEPT ![tm[2], tm[3]] = + msgsPrecommit[tm[2], tm[3]] \cup newMsgs] + /\ UNCHANGED <> + +\* lines 65-67 +OnTimeoutPrecommitOutside(p) == + \E tm \in timeoutPrecommit: \* a timeout occurs + /\ tm[1] = p + /\ (tm[2] /= h[p] \/ tm[3] /= round[p]) \* but we are in another round or height + /\ timeoutPrecommit' = timeoutPrecommit \ {tm} \* remove from the future timeouts + /\ UNCHANGED <> + +\* lines 65-67 +OnTimeoutPrecommitInside(p) == + /\ round[p] + 1 \in Rounds + \* THIS IS NOT PART OF THE ORIGINAL ALGORITHM, + \* BUT A SAFEGUARD TO PREVENT ROUNDS FROM OVERFLOWING + /\ <> \in timeoutPrecommit \* the timeout occurs for the current round and height + /\ timeoutPrecommit' = timeoutPrecommit \ {<>} \* remove from the future timeouts + /\ StartRound(p, h[p], round[p] + 1) + /\ UNCHANGED <> + + +Next == + \/ \E p \in Procs: + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ UponPrevoteFirstTime(p) + \/ UponProposalInPrevoteOrCommitAndPrevote(p) + \/ UponPrevoteNil(p) + \/ UponPrecommitFirstTime(p) + \/ UponProposalInPrecommitNoDecision(p) + \/ UponCatchupRound(p) + \/ OnTimeoutPropose(p) + \/ OnTimeoutPrevote(p) + \/ OnTimeoutPrecommitOutside(p) + \/ OnTimeoutPrecommitInside(p) + \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds + \*\/ UNCHANGED vars + +\* safety +Agreement == + \A p, q \in Procs, ht \in Heights: + decision[p][ht] = nil \/ decision[q][ht] = nil \/ decision[p][ht] = decision[q][ht] + +\* simple reachability properties to make sure that the algorithm is doing anything useful +NoDecision == \A p \in Procs, ht \in Heights: decision[p][ht] = nil + +NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" + +NoTwoLockedValues == + \A p, q \in Procs: + h[p] = h[q] => lockedValue[p] = nil \/ lockedValue[q] = nil \/ lockedValue[p] = lockedValue[q] + +NoTwoLockedRounds == + \A p, q \in Procs: + h[p] = h[q] => lockedRound[p] = -1 \/ lockedRound[q] = -1 \/ lockedRound[p] = lockedRound[q] + + +============================================================================= +\* Modification History +\* Last modified Mon Sep 16 15:00:22 CEST 2019 by igor +\* Created Fri Mar 15 10:30:17 CET 2019 by igor From 6562a1f0e65d540064874b409df184916a1db55f Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Mon, 4 Nov 2019 11:35:00 +0100 Subject: [PATCH 02/38] renamed --- spec/fork-cases/{Tendermint.tla => TendermintFork.tla} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename spec/fork-cases/{Tendermint.tla => TendermintFork.tla} (99%) diff --git a/spec/fork-cases/Tendermint.tla b/spec/fork-cases/TendermintFork.tla similarity index 99% rename from spec/fork-cases/Tendermint.tla rename to spec/fork-cases/TendermintFork.tla index 461d2e0..791daec 100644 --- a/spec/fork-cases/Tendermint.tla +++ b/spec/fork-cases/TendermintFork.tla @@ -1,4 +1,4 @@ ------------------------------ MODULE Tendermint ----------------------------- +------------------------ MODULE TendermintFork -------------------------- (* A TLA+ specification of Tendermint consensus by Ethan Buchman, Jae Kwon, and Zarko Milosevic. From c8ee6d58683b201cf27f725d6dec7d510142ba47 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Mon, 4 Nov 2019 16:22:19 +0100 Subject: [PATCH 03/38] starting on the fork scenarios --- spec/fork-cases/TendermintFork.tla | 70 +++++++++------------- spec/fork-cases/TendermintForkn_n4t1f2.tla | 40 +++++++++++++ 2 files changed, 68 insertions(+), 42 deletions(-) create mode 100644 spec/fork-cases/TendermintForkn_n4t1f2.tla diff --git a/spec/fork-cases/TendermintFork.tla b/spec/fork-cases/TendermintFork.tla index 791daec..87e08a0 100644 --- a/spec/fork-cases/TendermintFork.tla +++ b/spec/fork-cases/TendermintFork.tla @@ -1,31 +1,25 @@ ------------------------ MODULE TendermintFork -------------------------- (* - A TLA+ specification of Tendermint consensus by Ethan Buchman, Jae Kwon, and Zarko Milosevic. - - For the moment, we assume the following: - - 1. Every process has the voting power of 1. - 2. Timeouts are non-deterministic (works for safety). - 3. The proposer function is non-deterministic (works for safety). - - Encoded in TLA+ by Igor Konnov. It took me 4 hours to translate the pseudo-code to TLA+. + This is a fork of the tendermint-safety/Tendermint that captures various + fork scenarious under the assumption that there are more faults than + the algorithm should be able to handle. *) EXTENDS Integers, FiniteSets CONSTANTS - PropFun, \* the proposer function - Injected \* a set of the messages injected by the faulty processes + N, \* the total number of processes + T, \* the upper bound on the number of Byzantine processes + F, \* the number of Byzantine processes + Heights, \* the set of consensus instances + Rounds, \* the set of possible rounds + ValidValues, \* the set of values proposed by a correct process or a faulty one + InvalidValues, \* the set of values proposed by a faulty process + PropFun, \* the proposer function + FaultyMessages \* the set of faulty messages that can be sent by the faulty processes -N == 5 \* the total number of processes: correct and faulty -T == 1 \* an upper bound on the number of Byzantine processes -F == 1 \* the number of Byzantine processes Procs == 1..N-F Faulty == N-F+1..N -Heights == 0..1 \* the set of consensus instances -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {2} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values nil == -1 @@ -33,7 +27,7 @@ nil == -1 THRESHOLD1 == T + 1 THRESHOLD2 == 2 * T + 1 -(* APALACHE-BEGIN annotations *) +(* Type annotations needed by APALACHE *) a <: b == a MT == [type |-> STRING, src |-> Int, h |-> Int, round |-> Int, @@ -45,20 +39,6 @@ RoundT == Int TimeoutT == <> \* process, height, round (* APALACHE-END *) -FaultyMessages == \* the messages that can be sent by the faulty processes - ([type: {"PROPOSAL"}, src: Faulty, h: Heights, - round: Rounds, proposal: Values, validRound: Rounds \cup {-1}] <: {MT}) - \cup - ([type: {"PREVOTE"}, src: Faulty, h: Heights, round: Rounds, hash: Values] <: {MT}) - \cup - ([type: {"PRECOMMIT"}, src: Faulty, h: Heights, round: Rounds, hash: Values] <: {MT}) - -NInjected == 1 \* the number of injected faulty messages -ConstInit == - /\ PropFun \in [Heights \X Rounds -> Procs] - /\ Injected \in [1..NInjected -> FaultyMessages] - - \* these variables are exactly as in the pseudo-code VARIABLES h, round, step, decision, lockedValue, lockedRound, validValue, validRound @@ -85,8 +65,19 @@ Id(v) == v IsValid(v) == v \in ValidValues MixinFaults(ht, rd, type, msgs) == - \* add the messages from the faulty processes, filtered by height, round, and type - msgs \cup {m \in {Injected[x] : x \in 1..NInjected} : m.h = ht /\ m.round = rd /\ m.type = type} + \* add the messages from the faulty processes, filtered by height, round + msgs \cup {m \in FaultyMessages : m.h = ht /\ m.round = rd /\ m.type = type} + +\* compute the set of initial messages for a height, round, and value +InitProposals(ht, rd, v) == + IF ht = 0 /\ rd = 0 + THEN + LET proposals == + {[type |-> "PROPOSAL", src |-> Proposer(0, 0), + h |-> 0, round |-> 0, proposal |-> v, validRound |-> -1] <: MT} IN + MixinFaults(ht, rd, "PROPOSAL", proposals) + ELSE MixinFaults(ht, rd, "PROPOSAL", {} <: {MT}) + \* here we start with StartRound(0) Init == @@ -99,12 +90,7 @@ Init == /\ validValue = [p \in Procs |-> nil] /\ validRound = [p \in Procs |-> -1] /\ \E v \in ValidValues: - msgsPropose = [<> \in Heights \X Rounds |-> - MixinFaults(ht, rd, "PROPOSAL", - IF ht = 0 /\ rd = 0 - THEN {[type |-> "PROPOSAL", src |-> Proposer(0, 0), h |-> 0, round |-> 0, - proposal |-> v, validRound |-> -1] <: MT} - ELSE {} <: {MT})] \* no initial messages in other rounds + msgsPropose = [<> \in Heights \X Rounds |-> InitProposals(ht, rd, v)] /\ msgsPrevote = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PREVOTE", {} <: {MT})] /\ msgsPrecommit = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PRECOMMIT", {} <: {MT})] /\ oldEvents = {} <: {ET} @@ -370,5 +356,5 @@ NoTwoLockedRounds == ============================================================================= \* Modification History -\* Last modified Mon Sep 16 15:00:22 CEST 2019 by igor +\* Last modified Mon Nov 04 12:07:03 CET 2019 by igor \* Created Fri Mar 15 10:30:17 CET 2019 by igor diff --git a/spec/fork-cases/TendermintForkn_n4t1f2.tla b/spec/fork-cases/TendermintForkn_n4t1f2.tla new file mode 100644 index 0000000..5b43d9b --- /dev/null +++ b/spec/fork-cases/TendermintForkn_n4t1f2.tla @@ -0,0 +1,40 @@ +----------------------- MODULE TendermintForkn_n4t1f2 ----------------------- +EXTENDS Integers + +NInjected == 1 \* the number of injected faulty messages + +N == 4 \* the total number of processes: correct and faulty +T == 1 \* an upper bound on the number of Byzantine processes +F == 1 \* the number of Byzantine processes +Heights == 0..1 \* the set of consensus instances +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {2} \* e.g., sent by a Byzantine process + +PValues == ValidValues \cup InvalidValues \* all values +PProcs == 1..N-F +PFaulty == N-F+1..N + +FaultyMessages == \* the messages that can be sent by the faulty processes + [type: {"PROPOSAL"}, src: PFaulty, h: Heights, + round: Rounds, proposal: PValues, validRound: Rounds \cup {-1}] + \cup + [type: {"PREVOTE"}, src: PFaulty, h: Heights, round: Rounds, hash: PValues] + \cup + [type: {"PRECOMMIT"}, src: PFaulty, h: Heights, round: Rounds, hash: PValues] + +\* the proposer is rotating +PropFun == [<> \in Heights \X Rounds |-> (ht + rd) % N] + +\* the variables that are declared in TendermintFork +VARIABLES h, round, step, decision, lockedValue, lockedRound, validValue, validRound +VARIABLES msgsPropose, msgsPrevote, msgsPrecommit, + oldEvents, timeoutPropose, timeoutPrevote, timeoutPrecommit + +INSTANCE TendermintFork + + +============================================================================= +\* Modification History +\* Last modified Mon Nov 04 14:59:54 CET 2019 by igor +\* Created Mon Nov 04 11:47:48 CET 2019 by igor From a747999a64a768ef71fb057253bd7a226a8a878c Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 10 Dec 2019 18:16:01 +0100 Subject: [PATCH 04/38] the spec by Zarko --- spec/fork-cases/TendermintAccountability.tla | 246 +++++++++++++++++++ 1 file changed, 246 insertions(+) create mode 100644 spec/fork-cases/TendermintAccountability.tla diff --git a/spec/fork-cases/TendermintAccountability.tla b/spec/fork-cases/TendermintAccountability.tla new file mode 100644 index 0000000..4a82055 --- /dev/null +++ b/spec/fork-cases/TendermintAccountability.tla @@ -0,0 +1,246 @@ +----------------------------- MODULE TendermintAccountability ----------------------------- +(* + A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability + protocol. + + *) + +EXTENDS Integers, FiniteSets + + +N == 4 \* the total number of processes: correct and faulty +T == 1 \* an upper bound on the number of Byzantine processes +F == 2 \* the number of Byzantine processes +Procs == 1..N-F +Faulty == N-F+1..N +Rounds == 0..4 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {2} \* e.g., sent by a Byzantine process +Values == ValidValues \cup InvalidValues \* all values +nil == -1 + +\* these are two tresholds that are used in the algorithm +THRESHOLD1 == T + 1 +THRESHOLD2 == 2 * T + 1 + + +\* these variables are exactly as in the pseudo-code +VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound + +\* book-keeping variables +VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsReceived, \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages + oldEvents \* the messages processed once, as expressed by "for the first time" + + +\* this is needed for UNCHANGED +vars == <> + +\* A function which gives the proposer for a given round at a given height. +\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, +\* it does not really matter who starts first. +Proposer(rd) == 1 + (rd % N) + +Id(v) == v + +IsValid(v) == v \in ValidValues + + +\* here we start with StartRound(0) +Init == + /\ round = [p \in Procs |-> 0] + /\ step = [p \in Procs |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? + /\ decision = [p \in Procs |-> nil] + /\ lockedValue = [p \in Procs |-> nil] + /\ lockedRound = [p \in Procs |-> -1] + /\ validValue = [p \in Procs |-> nil] + /\ validRound = [p \in Procs |-> -1] + /\ msgsPrevote = [rd \in Rounds |-> {}] + /\ msgsPrecommit = [rd \in Rounds |-> {}] + /\ msgsPropose = [rd \in Rounds |-> {}] + /\ msgsReceived = [p \in Procs |-> {}] + /\ oldEvents = {} + +FaultyMessages == \* the messages that can be sent by the faulty processes + ([type: {"PROPOSAL"}, src: Faulty, + round: Rounds, proposal: Values, validRound: Rounds \cup {-1}]) + \cup + ([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]) + \cup + ([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]) + + +\* lines 22-27 +UponProposalInPropose(p) == + \E v \in Values: + /\ step[p] = "PROPOSE" \* line 22 + /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> -1]) \in msgsPropose[round[p]] \* line 22 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 + LET newMsg == ({[type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil]}) + IN \* lines 24-26 + msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup newMsg] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + + +\* lines 28-33 +UponProposalInProposeAndPrevote(p) == + \E v \in Values, vr \in Rounds: + /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part + /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr]) \in msgsPropose[round[p]] \* line 28 + /\ LET PV == { m \in msgsPrevote[vr]: m.is = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 28 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 + LET newMsg == ({[type |-> "PREVOTE", src |-> p, + round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil]}) + IN \* lines 30-32 + msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup newMsg] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! +InsertProposal(p) == + \E v \in ValidValues: + LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN + LET newMsg == + IF p = Proposer(round[p]) /\ step[p] = "PROPOSE" + THEN {[type |-> "PROPOSAL", src |-> p, round |-> round[p], + proposal |-> proposal, validRound |-> validRound[p]]} + ELSE {} + IN + msgsPropose' = [msgsPropose EXCEPT ![round[p]] = + msgsPropose[round[p]] \cup newMsg] + /\ UNCHANGED <> + + + \* lines 34-35 +UponQuorumOfPrevotesAny(p) == + /\ step[p] = "PREVOTE" \* line 34 and 61 + /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! + /\ LET newMsg == [type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> nil] IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup {newMsg}] + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + /\ UNCHANGED <> + + +\* lines 36-46 +UponProposalInPrevoteOrCommitAndPrevote(p) == + \E v \in ValidValues, vr \in Rounds \cup {-1}: + /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 + /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr]) \in msgsPropose[round[p]] \* line 36 + /\ LET PV == { m \in msgsPrevote[round[p]]: m.is = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ lockedValue' = + IF step[p] = "PREVOTE" + THEN [lockedValue EXCEPT ![p] = v] \* line 38 + ELSE lockedValue \* else of line 37 + /\ lockedRound' = + IF step[p] = "PREVOTE" + THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 + ELSE lockedRound \* else of line 37 + /\ LET newMsgs == + IF step[p] = "PREVOTE" + THEN {[type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]} \* line 40 + ELSE {} + IN \* else of line 37 + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup newMsgs] \* line 40, or else of 37 + /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 + /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 + /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 + /\ UNCHANGED <> + + +\* lines 11-21 +StartRound(p, r) == + /\ round' = [round EXCEPT ![p] = r] + /\ step' = [step EXCEPT ![p] = "PROPOSE"] + + +\* lines 47-48 +UponQuorumOfPrecommitsAny(p) == + /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 + /\ round[p] + 1 \in Rounds + /\ StartRound(p, round[p] + 1) + /\ UNCHANGED <> + + +\* lines 49-54 +UponProposalInPrecommitNoDecision(p) == + /\ decision[p] = nil \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: + /\ ([type |-> "PROPOSAL", src |-> Proposer(r), + round |-> r, proposal |-> v, validRound |-> vr]) \in msgsPropose[r] \* line 49 + /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 49 + /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 + /\ UNCHANGED <> + + + +\** TODO: With this rule we are generating duplicate messages +InsertFaultyPrevoteMessage == + \E msg \in FaultyMessages: msg.type = "PREVOTE" + /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] + /\ UNCHANGED <> + +InsertFaultyPrecommitMessage == + \E msg \in FaultyMessages: msg.type = "PRECOMMIT" + /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] + /\ UNCHANGED <> + +InsertFaultyProposalMessage == + \E srcA \in Faulty, r \in Rounds, idV \in Values: + LET newMsg == [type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV] IN + msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] + /\ UNCHANGED <> + + +Next == + \/ \E p \in Procs: + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ InsertProposal(p) + \/ UponQuorumOfPrevotesAny(p) + \/ UponQuorumOfPrecommitsAny(p) + \/ InsertFaultyPrevoteMessage + \/ InsertFaultyPrecommitMessage + \/ UponProposalInPrecommitNoDecision(p) + \/ InsertFaultyProposalMessage + + \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds + \*\/ UNCHANGED vars + +\* simple reachability properties to make sure that the algorithm is doing anything useful +NoPrevote == \A p \in Procs: step[p] /= "PREVOTE" + +NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" + +NoHigherRounds == \A p \in Procs: round[p] < 1 + +NoDecision == \A p \in Procs: decision[p] = nil + + +============================================================================= + + + \ No newline at end of file From 964371ef92792f994012bdf493243ab4f715fd50 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 10 Dec 2019 18:23:09 +0100 Subject: [PATCH 05/38] removed oldEvents, as they are not used anymore --- spec/fork-cases/TendermintAccountability.tla | 26 +++++++++----------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/spec/fork-cases/TendermintAccountability.tla b/spec/fork-cases/TendermintAccountability.tla index 4a82055..d6f4a3f 100644 --- a/spec/fork-cases/TendermintAccountability.tla +++ b/spec/fork-cases/TendermintAccountability.tla @@ -31,13 +31,12 @@ VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRoun VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsReceived, \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages - oldEvents \* the messages processed once, as expressed by "for the first time" + msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages \* this is needed for UNCHANGED vars == <> + validRound, msgsPropose, msgsPrevote, msgsPrecommit, msgsReceived>> \* A function which gives the proposer for a given round at a given height. \* Here we use round robin. As Procs and Faulty are assigned non-deterministically, @@ -62,7 +61,6 @@ Init == /\ msgsPrecommit = [rd \in Rounds |-> {}] /\ msgsPropose = [rd \in Rounds |-> {}] /\ msgsReceived = [p \in Procs |-> {}] - /\ oldEvents = {} FaultyMessages == \* the messages that can be sent by the faulty processes ([type: {"PROPOSAL"}, src: Faulty, @@ -87,7 +85,7 @@ UponProposalInPropose(p) == msgsPrevote[round[p]] \cup newMsg] /\ step' = [step EXCEPT ![p] = "PREVOTE"] /\ UNCHANGED <> + validRound, msgsPropose, msgsPrecommit, msgsReceived>> \* lines 28-33 @@ -106,7 +104,7 @@ UponProposalInProposeAndPrevote(p) == msgsPrevote[round[p]] \cup newMsg] /\ step' = [step EXCEPT ![p] = "PREVOTE"] /\ UNCHANGED <> + validRound, msgsPropose, msgsPrecommit, msgsReceived>> \* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! InsertProposal(p) == @@ -121,7 +119,7 @@ InsertProposal(p) == msgsPropose' = [msgsPropose EXCEPT ![round[p]] = msgsPropose[round[p]] \cup newMsg] /\ UNCHANGED <> + validRound, msgsPrevote, msgsPrecommit, msgsReceived>> \* lines 34-35 @@ -133,7 +131,7 @@ UponQuorumOfPrevotesAny(p) == msgsPrecommit[round[p]] \cup {newMsg}] /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] /\ UNCHANGED <> + validRound, msgsPropose, msgsPrevote, msgsReceived>> \* lines 36-46 @@ -162,7 +160,7 @@ UponProposalInPrevoteOrCommitAndPrevote(p) == /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 - /\ UNCHANGED <> + /\ UNCHANGED <> \* lines 11-21 @@ -177,7 +175,7 @@ UponQuorumOfPrecommitsAny(p) == /\ round[p] + 1 \in Rounds /\ StartRound(p, round[p] + 1) /\ UNCHANGED <> + validRound, msgsPropose, msgsPrevote, msgsPrecommit, msgsReceived>> \* lines 49-54 @@ -190,7 +188,7 @@ UponProposalInPrecommitNoDecision(p) == Cardinality(PV) >= THRESHOLD2 \* line 49 /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 /\ UNCHANGED <> + validRound, msgsPropose, msgsPrevote, msgsPrecommit, msgsReceived>> @@ -199,20 +197,20 @@ InsertFaultyPrevoteMessage == \E msg \in FaultyMessages: msg.type = "PREVOTE" /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] /\ UNCHANGED <> + validRound, msgsPropose, msgsPrecommit, msgsReceived>> InsertFaultyPrecommitMessage == \E msg \in FaultyMessages: msg.type = "PRECOMMIT" /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] /\ UNCHANGED <> + validRound, msgsPropose, msgsPrevote, msgsReceived>> InsertFaultyProposalMessage == \E srcA \in Faulty, r \in Rounds, idV \in Values: LET newMsg == [type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV] IN msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED <> + validRound, msgsPrecommit, msgsPrevote, msgsReceived>> Next == From 9f6c13ecb8ef188e88aba543692815a0109fba15 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 10 Dec 2019 20:51:22 +0100 Subject: [PATCH 06/38] fixed a few typos --- spec/fork-cases/TendermintAccountability.tla | 6 +- .../TendermintAccountabilityApa.tla | 260 ++++++++++++++++++ 2 files changed, 263 insertions(+), 3 deletions(-) create mode 100644 spec/fork-cases/TendermintAccountabilityApa.tla diff --git a/spec/fork-cases/TendermintAccountability.tla b/spec/fork-cases/TendermintAccountability.tla index d6f4a3f..73cb1e2 100644 --- a/spec/fork-cases/TendermintAccountability.tla +++ b/spec/fork-cases/TendermintAccountability.tla @@ -94,11 +94,11 @@ UponProposalInProposeAndPrevote(p) == /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), round |-> round[p], proposal |-> v, validRound |-> vr]) \in msgsPropose[round[p]] \* line 28 - /\ LET PV == { m \in msgsPrevote[vr]: m.is = Id(v) } IN + /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN Cardinality(PV) >= THRESHOLD2 \* line 28 /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 LET newMsg == ({[type |-> "PREVOTE", src |-> p, - round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil]}) + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil]}) IN \* lines 30-32 msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = msgsPrevote[round[p]] \cup newMsg] @@ -241,4 +241,4 @@ NoDecision == \A p \in Procs: decision[p] = nil ============================================================================= - \ No newline at end of file + diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla new file mode 100644 index 0000000..f7988f1 --- /dev/null +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -0,0 +1,260 @@ +----------------------------- MODULE TendermintAccountabilityApa ----------------------------- +(* + A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability + protocol. + + This is the version for compatibility with Apalache. + *) + +EXTENDS Integers, FiniteSets + +CONSTANTS + StartId \* the id of the starting process in the proposer round robin + +N == 4 \* the total number of processes: correct and faulty +T == 1 \* an upper bound on the number of Byzantine processes +F == 2 \* the number of Byzantine processes +Procs == 1..N-F +Faulty == N-F+1..N +Rounds == 0..4 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {2} \* e.g., sent by a Byzantine process +Values == ValidValues \cup InvalidValues \* all values +nil == -1 + +\* these are two thresholds that are used in the algorithm +THRESHOLD1 == T + 1 +THRESHOLD2 == 2 * T + 1 + +(* APALACHE *) +a <: b == a + +MT == [type |-> STRING, src |-> Int, round |-> Int, + proposal |-> Int, validRound |-> Int, id |-> Int] + +AsMsg(m) == m <: MT +SetOfMsgs(S) == S <: {MT} +EmptyMsgSet == SetOfMsgs({}) + +ConstInit == StartId \in 1..N +(* END-OF-APALACHE *) + + +\* these variables are exactly as in the pseudo-code +VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound + +\* book-keeping variables +VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages + + +\* this is needed for UNCHANGED +vars == <> + +\* A function which gives the proposer for a given round at a given height. +\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, +\* it does not really matter who starts first. +Proposer(rd) == 1 + ((StartId + rd) % N) + +Id(v) == v + +IsValid(v) == v \in ValidValues + + +\* here we start with StartRound(0) +Init == + /\ round = [p \in Procs |-> 0] + /\ step = [p \in Procs |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? + /\ decision = [p \in Procs |-> nil] + /\ lockedValue = [p \in Procs |-> nil] + /\ lockedRound = [p \in Procs |-> -1] + /\ validValue = [p \in Procs |-> nil] + /\ validRound = [p \in Procs |-> -1] + /\ msgsPrevote = [rd \in Rounds |-> EmptyMsgSet] + /\ msgsPrecommit = [rd \in Rounds |-> EmptyMsgSet] + /\ msgsPropose = [rd \in Rounds |-> EmptyMsgSet] + /\ msgsReceived = [p \in Procs |-> EmptyMsgSet] + +FaultyMessages == \* the messages that can be sent by the faulty processes + (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, + round: Rounds, proposal: Values, validRound: Rounds \cup {-1}])) + \cup + (SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values])) + \cup + (SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values])) + + +\* lines 22-27 +UponProposalInPropose(p) == + \E v \in Values: + /\ step[p] = "PROPOSE" \* line 22 + /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> -1])) \in msgsPropose[round[p]] \* line 22 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 + LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil])}) + IN \* lines 24-26 + msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup newMsg] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + + +\* lines 28-33 +UponProposalInProposeAndPrevote(p) == + \E v \in Values, vr \in Rounds: + /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part + /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr])) \in msgsPropose[round[p]] \* line 28 + /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 28 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 + LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil])}) + IN \* lines 30-32 + msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup newMsg] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! +InsertProposal(p) == + \E v \in ValidValues: + LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN + LET newMsg == + IF p = Proposer(round[p]) /\ step[p] = "PROPOSE" + THEN {AsMsg([type |-> "PROPOSAL", src |-> p, round |-> round[p], + proposal |-> proposal, validRound |-> validRound[p]])} + ELSE EmptyMsgSet + IN + msgsPropose' = [msgsPropose EXCEPT ![round[p]] = + msgsPropose[round[p]] \cup newMsg] + /\ UNCHANGED <> + + + \* lines 34-35 +UponQuorumOfPrevotesAny(p) == + /\ step[p] = "PREVOTE" \* line 34 and 61 + /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! + /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> nil]) IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup {newMsg}] + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + /\ UNCHANGED <> + + +\* lines 36-46 +UponProposalInPrevoteOrCommitAndPrevote(p) == + \E v \in ValidValues, vr \in Rounds \cup {-1}: + /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 + /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr])) \in msgsPropose[round[p]] \* line 36 + /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ lockedValue' = + IF step[p] = "PREVOTE" + THEN [lockedValue EXCEPT ![p] = v] \* line 38 + ELSE lockedValue \* else of line 37 + /\ lockedRound' = + IF step[p] = "PREVOTE" + THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 + ELSE lockedRound \* else of line 37 + /\ LET newMsgs == + IF step[p] = "PREVOTE" + THEN {AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)])} \* line 40 + ELSE EmptyMsgSet + IN \* else of line 37 + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup newMsgs] \* line 40, or else of 37 + /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 + /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 + /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 + /\ UNCHANGED <> + + +\* lines 11-21 +StartRound(p, r) == + /\ round' = [round EXCEPT ![p] = r] + /\ step' = [step EXCEPT ![p] = "PROPOSE"] + + +\* lines 47-48 +UponQuorumOfPrecommitsAny(p) == + /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 + /\ round[p] + 1 \in Rounds + /\ StartRound(p, round[p] + 1) + /\ UNCHANGED <> + + +\* lines 49-54 +UponProposalInPrecommitNoDecision(p) == + /\ decision[p] = nil \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: + /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), + round |-> r, proposal |-> v, validRound |-> vr])) \in msgsPropose[r] \* line 49 + /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN + Cardinality(PV) >= THRESHOLD2 \* line 49 + /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 + /\ UNCHANGED <> + + + +\** TODO: With this rule we are generating duplicate messages +InsertFaultyPrevoteMessage == + \E msg \in FaultyMessages: msg.type = "PREVOTE" + /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] + /\ UNCHANGED <> + +InsertFaultyPrecommitMessage == + \E msg \in FaultyMessages: msg.type = "PRECOMMIT" + /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] + /\ UNCHANGED <> + +InsertFaultyProposalMessage == + \E srcA \in Faulty, r \in Rounds, idV \in Values: + LET newMsg == AsMsg([type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV]) IN + msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] + /\ UNCHANGED <> + + +Next == + \/ \E p \in Procs: + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ InsertProposal(p) + \/ UponQuorumOfPrevotesAny(p) + \/ UponQuorumOfPrecommitsAny(p) + \/ InsertFaultyPrevoteMessage + \/ InsertFaultyPrecommitMessage + \/ UponProposalInPrecommitNoDecision(p) + \/ InsertFaultyProposalMessage + + \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds + \*\/ UNCHANGED vars + +\* simple reachability properties to make sure that the algorithm is doing anything useful +NoPrevote == \A p \in Procs: step[p] /= "PREVOTE" + +NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" + +NoHigherRounds == \A p \in Procs: round[p] < 1 + +NoDecision == \A p \in Procs: decision[p] = nil + + +============================================================================= + + + From 9415b837027e5162fd6ab266083c678392302f6d Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Fri, 20 Dec 2019 14:26:14 +0100 Subject: [PATCH 07/38] tuning the spec for model checking --- spec/fork-cases/TendermintAccountability.tla | 2 +- .../TendermintAccountabilityApa.tla | 78 +++++++++++-------- 2 files changed, 46 insertions(+), 34 deletions(-) diff --git a/spec/fork-cases/TendermintAccountability.tla b/spec/fork-cases/TendermintAccountability.tla index 73cb1e2..04b1696 100644 --- a/spec/fork-cases/TendermintAccountability.tla +++ b/spec/fork-cases/TendermintAccountability.tla @@ -13,7 +13,7 @@ T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes Procs == 1..N-F Faulty == N-F+1..N -Rounds == 0..4 \* the set of possible rounds, give a bit more freedom to the solver +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one InvalidValues == {2} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index f7988f1..1085c31 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -9,18 +9,20 @@ EXTENDS Integers, FiniteSets CONSTANTS - StartId \* the id of the starting process in the proposer round robin + PropFun \* the proposer function N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes Procs == 1..N-F Faulty == N-F+1..N -Rounds == 0..4 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {2} \* e.g., sent by a Byzantine process +AllProcs == 1..N +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {"2"} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values -nil == -1 +NilRound == -1 +NilValue == "None" \* these are two thresholds that are used in the algorithm THRESHOLD1 == T + 1 @@ -30,13 +32,17 @@ THRESHOLD2 == 2 * T + 1 a <: b == a MT == [type |-> STRING, src |-> Int, round |-> Int, - proposal |-> Int, validRound |-> Int, id |-> Int] + proposal |-> STRING, validRound |-> Int, id |-> STRING] AsMsg(m) == m <: MT SetOfMsgs(S) == S <: {MT} EmptyMsgSet == SetOfMsgs({}) -ConstInit == StartId \in 1..N +ConstInit == + \*StartId \in 1..N + \* the proposer is arbitrary -- ok for safety + PropFun \in [Rounds -> AllProcs] + (* END-OF-APALACHE *) @@ -57,7 +63,8 @@ vars == < 0] /\ step = [p \in Procs |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? - /\ decision = [p \in Procs |-> nil] - /\ lockedValue = [p \in Procs |-> nil] - /\ lockedRound = [p \in Procs |-> -1] - /\ validValue = [p \in Procs |-> nil] - /\ validRound = [p \in Procs |-> -1] + /\ decision = [p \in Procs |-> NilValue] + /\ lockedValue = [p \in Procs |-> NilValue] + /\ lockedRound = [p \in Procs |-> NilRound] + /\ validValue = [p \in Procs |-> NilValue] + /\ validRound = [p \in Procs |-> NilRound] /\ msgsPrevote = [rd \in Rounds |-> EmptyMsgSet] /\ msgsPrecommit = [rd \in Rounds |-> EmptyMsgSet] /\ msgsPropose = [rd \in Rounds |-> EmptyMsgSet] @@ -80,7 +87,7 @@ Init == FaultyMessages == \* the messages that can be sent by the faulty processes (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, - round: Rounds, proposal: Values, validRound: Rounds \cup {-1}])) + round: Rounds, proposal: Values, validRound: Rounds \cup {NilRound}])) \cup (SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values])) \cup @@ -92,10 +99,10 @@ UponProposalInPropose(p) == \E v \in Values: /\ step[p] = "PROPOSE" \* line 22 /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> -1])) \in msgsPropose[round[p]] \* line 22 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 + round |-> round[p], proposal |-> v, validRound |-> NilRound])) \in msgsPropose[round[p]] \* line 22 + /\ LET isGood == IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) IN \* line 23 LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil])}) + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue])}) IN \* lines 24-26 msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = msgsPrevote[round[p]] \cup newMsg] @@ -114,7 +121,7 @@ UponProposalInProposeAndPrevote(p) == Cardinality(PV) >= THRESHOLD2 \* line 28 /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil])}) + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue])}) IN \* lines 30-32 msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = msgsPrevote[round[p]] \cup newMsg] @@ -125,7 +132,7 @@ UponProposalInProposeAndPrevote(p) == \* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! InsertProposal(p) == \E v \in ValidValues: - LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN + LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN LET newMsg == IF p = Proposer(round[p]) /\ step[p] = "PROPOSE" THEN {AsMsg([type |-> "PROPOSAL", src |-> p, round |-> round[p], @@ -142,7 +149,7 @@ InsertProposal(p) == UponQuorumOfPrevotesAny(p) == /\ step[p] = "PREVOTE" \* line 34 and 61 /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! - /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> nil]) IN + /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = msgsPrecommit[round[p]] \cup {newMsg}] /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] @@ -152,7 +159,7 @@ UponQuorumOfPrevotesAny(p) == \* lines 36-46 UponProposalInPrevoteOrCommitAndPrevote(p) == - \E v \in ValidValues, vr \in Rounds \cup {-1}: + \E v \in ValidValues, vr \in Rounds \cup {NilRound}: /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), round |-> round[p], proposal |-> v, validRound |-> vr])) \in msgsPropose[round[p]] \* line 36 @@ -196,8 +203,8 @@ UponQuorumOfPrecommitsAny(p) == \* lines 49-54 UponProposalInPrecommitNoDecision(p) == - /\ decision[p] = nil \* line 49 - /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: + /\ decision[p] = NilValue \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {NilRound}: /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), round |-> r, proposal |-> v, validRound |-> vr])) \in msgsPropose[r] \* line 49 /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN @@ -205,20 +212,25 @@ UponProposalInPrecommitNoDecision(p) == /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 /\ UNCHANGED <> - - -\** TODO: With this rule we are generating duplicate messages InsertFaultyPrevoteMessage == - \E msg \in FaultyMessages: msg.type = "PREVOTE" - /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] - /\ UNCHANGED < "PREVOTE", src |-> src, round |-> r, id |-> id]) + IN + /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] + /\ UNCHANGED <> InsertFaultyPrecommitMessage == - \E msg \in FaultyMessages: msg.type = "PRECOMMIT" - /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] - /\ UNCHANGED < "PRECOMMIT", src |-> src, round |-> r, id |-> id]) + IN + /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] + /\ UNCHANGED <> InsertFaultyProposalMessage == @@ -251,7 +263,7 @@ NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" NoHigherRounds == \A p \in Procs: round[p] < 1 -NoDecision == \A p \in Procs: decision[p] = nil +NoDecision == \A p \in Procs: decision[p] = NilValue ============================================================================= From 38c9879504470a1eeb551d4f7ae2e55bd94b335e Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Fri, 20 Dec 2019 16:40:19 +0100 Subject: [PATCH 08/38] fixed the spec: there should be at least one execution where processes decide --- .../TendermintAccountabilityApa.tla | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index 1085c31..226ddc2 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -14,9 +14,9 @@ CONSTANTS N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes -Procs == 1..N-F +Corr == 1..N-F Faulty == N-F+1..N -AllProcs == 1..N +AllCorr == 1..N Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one InvalidValues == {"2"} \* e.g., sent by a Byzantine process @@ -41,7 +41,7 @@ EmptyMsgSet == SetOfMsgs({}) ConstInit == \*StartId \in 1..N \* the proposer is arbitrary -- ok for safety - PropFun \in [Rounds -> AllProcs] + PropFun \in [Rounds -> AllCorr] (* END-OF-APALACHE *) @@ -53,7 +53,7 @@ VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRoun VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages + msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Corr -> set of messages \* this is needed for UNCHANGED @@ -61,7 +61,7 @@ vars == <> \* A function which gives the proposer for a given round at a given height. -\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, +\* Here we use round robin. As Corr and Faulty are assigned non-deterministically, \* it does not really matter who starts first. \*Proposer(rd) == 1 + ((StartId + rd) % N) Proposer(rd) == PropFun[rd] @@ -73,17 +73,17 @@ IsValid(v) == v \in ValidValues \* here we start with StartRound(0) Init == - /\ round = [p \in Procs |-> 0] - /\ step = [p \in Procs |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? - /\ decision = [p \in Procs |-> NilValue] - /\ lockedValue = [p \in Procs |-> NilValue] - /\ lockedRound = [p \in Procs |-> NilRound] - /\ validValue = [p \in Procs |-> NilValue] - /\ validRound = [p \in Procs |-> NilRound] + /\ round = [p \in Corr |-> 0] + /\ step = [p \in Corr |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? + /\ decision = [p \in Corr |-> NilValue] + /\ lockedValue = [p \in Corr |-> NilValue] + /\ lockedRound = [p \in Corr |-> NilRound] + /\ validValue = [p \in Corr |-> NilValue] + /\ validRound = [p \in Corr |-> NilRound] /\ msgsPrevote = [rd \in Rounds |-> EmptyMsgSet] /\ msgsPrecommit = [rd \in Rounds |-> EmptyMsgSet] /\ msgsPropose = [rd \in Rounds |-> EmptyMsgSet] - /\ msgsReceived = [p \in Procs |-> EmptyMsgSet] + /\ msgsReceived = [p \in Corr |-> EmptyMsgSet] FaultyMessages == \* the messages that can be sent by the faulty processes (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, @@ -145,7 +145,7 @@ InsertProposal(p) == validRound, msgsPrevote, msgsPrecommit, msgsReceived>> - \* lines 34-35 + \* lines 34-35 + lines 61-64 UponQuorumOfPrevotesAny(p) == /\ step[p] = "PREVOTE" \* line 34 and 61 /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! @@ -239,14 +239,13 @@ InsertFaultyProposalMessage == msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED <> - - Next == - \/ \E p \in Procs: + \/ \E p \in Corr: \/ UponProposalInPropose(p) \/ UponProposalInProposeAndPrevote(p) \/ InsertProposal(p) \/ UponQuorumOfPrevotesAny(p) + \/ UponProposalInPrevoteOrCommitAndPrevote(p) \/ UponQuorumOfPrecommitsAny(p) \/ InsertFaultyPrevoteMessage \/ InsertFaultyPrecommitMessage @@ -257,13 +256,23 @@ Next == \*\/ UNCHANGED vars \* simple reachability properties to make sure that the algorithm is doing anything useful -NoPrevote == \A p \in Procs: step[p] /= "PREVOTE" +NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" + +NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" + +NoValidPrecommit == + \A r \in Rounds: + \A m \in msgsPrecommit[r]: + m.id = NilValue \/ m.src \in Faulty -NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" +NoHigherRounds == \A p \in Corr: round[p] < 1 -NoHigherRounds == \A p \in Procs: round[p] < 1 +NoDecision == \A p \in Corr: decision[p] = NilValue -NoDecision == \A p \in Procs: decision[p] = NilValue +Agreement == \A p, q \in Corr: + \/ decision[p] = NilValue + \/ decision[q] = NilValue + \/ decision[p] = decision[q] ============================================================================= From 20d95bffad21a26ce33e083b729a59aa5790f045 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 22 Dec 2019 11:32:50 +0100 Subject: [PATCH 09/38] a few fixes --- .../TendermintAccountabilityApa.tla | 69 ++++++++++--------- 1 file changed, 37 insertions(+), 32 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index 226ddc2..3cd7b12 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -101,11 +101,11 @@ UponProposalInPropose(p) == /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), round |-> round[p], proposal |-> v, validRound |-> NilRound])) \in msgsPropose[round[p]] \* line 22 /\ LET isGood == IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) IN \* line 23 - LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue])}) + LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) IN \* lines 24-26 - msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup newMsg] + /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup {newMsg}] /\ step' = [step EXCEPT ![p] = "PREVOTE"] /\ UNCHANGED <> @@ -120,29 +120,31 @@ UponProposalInProposeAndPrevote(p) == /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN Cardinality(PV) >= THRESHOLD2 \* line 28 /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 - LET newMsg == ({AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue])}) + LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) IN \* lines 30-32 - msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup newMsg] + /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup {newMsg}] /\ step' = [step EXCEPT ![p] = "PREVOTE"] /\ UNCHANGED <> -\* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! InsertProposal(p) == - \E v \in ValidValues: - LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN - LET newMsg == - IF p = Proposer(round[p]) /\ step[p] = "PROPOSE" - THEN {AsMsg([type |-> "PROPOSAL", src |-> p, round |-> round[p], - proposal |-> proposal, validRound |-> validRound[p]])} - ELSE EmptyMsgSet - IN - msgsPropose' = [msgsPropose EXCEPT ![round[p]] = - msgsPropose[round[p]] \cup newMsg] - /\ UNCHANGED <> + /\ p = Proposer(round[p]) /\ step[p] = "PROPOSE" + /\ \E v \in ValidValues: + \* TODO: shall the faulty processes send InvalidValues too? + LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN + LET newMsg == + AsMsg([type |-> "PROPOSAL", src |-> p, round |-> round[p], + proposal |-> proposal, validRound |-> validRound[p]]) + IN + LET r == round[p] IN + \* a correct proposer never sends two proposals + /\ p \in Corr => msgsPropose[r] = EmptyMsgSet + /\ msgsPropose' = [msgsPropose EXCEPT ![r] = + msgsPropose[r] \cup {newMsg}] + /\ UNCHANGED <> \* lines 34-35 + lines 61-64 @@ -150,7 +152,7 @@ UponQuorumOfPrevotesAny(p) == /\ step[p] = "PREVOTE" \* line 34 and 61 /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = msgsPrecommit[round[p]] \cup {newMsg}] /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] /\ UNCHANGED < "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)])} \* line 40 - ELSE EmptyMsgSet - IN \* else of line 37 - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup newMsgs] \* line 40, or else of 37 + /\ IF step[p] = "PREVOTE" + THEN + LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]) + IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup {newMsg}] \* line 40, or else of 37 + ELSE UNCHANGED msgsPrecommit \* line 40 /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 @@ -219,7 +221,8 @@ InsertFaultyPrevoteMessage == \E id \in Values: LET msg == AsMsg([type |-> "PREVOTE", src |-> src, round |-> r, id |-> id]) IN - /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] + \*/\ msg \notin msgsPrevote[r] \* optimization + /\ msgsPrevote' = [msgsPrevote EXCEPT ![r] = msgsPrevote[r] \cup {msg}] /\ UNCHANGED <> @@ -229,14 +232,16 @@ InsertFaultyPrecommitMessage == \E id \in Values: LET msg == AsMsg([type |-> "PRECOMMIT", src |-> src, round |-> r, id |-> id]) IN - /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] + \*/\ msg \notin msgsPrecommit[r] \* optimization + /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![r] = msgsPrecommit[r] \cup {msg}] /\ UNCHANGED <> InsertFaultyProposalMessage == \E srcA \in Faulty, r \in Rounds, idV \in Values: LET newMsg == AsMsg([type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV]) IN - msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] + \*/\ newMsg \notin msgsPropose[r] \* optimization + /\ msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED <> Next == From 882a6919e1f9e6120c0080addd0654666602649e Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 22 Dec 2019 20:55:38 +0100 Subject: [PATCH 10/38] fixed the proposal and moved the fault logic --- .../TendermintAccountabilityApa.tla | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index 3cd7b12..2eab153 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -130,17 +130,21 @@ UponProposalInProposeAndPrevote(p) == validRound, msgsPropose, msgsPrecommit, msgsReceived>> InsertProposal(p) == - /\ p = Proposer(round[p]) /\ step[p] = "PROPOSE" + LET r == round[p] IN + /\ p = Proposer(round[p]) + /\ step[p] = "PROPOSE" + \* if the proposer is sending a proposal, then there are no other proposals + \* by the correct processes for the same round + /\ \A m \in msgsPropose[r]: m.src \in Faulty /\ \E v \in ValidValues: - \* TODO: shall the faulty processes send InvalidValues too? LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN LET newMsg == - AsMsg([type |-> "PROPOSAL", src |-> p, round |-> round[p], + AsMsg([type |-> "PROPOSAL", src |-> p, round |-> r, proposal |-> proposal, validRound |-> validRound[p]]) IN - LET r == round[p] IN + \* a correct proposer never sends two proposals - /\ p \in Corr => msgsPropose[r] = EmptyMsgSet + /\ msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED < "PREVOTE", src |-> src, round |-> r, id |-> id]) IN - \*/\ msg \notin msgsPrevote[r] \* optimization /\ msgsPrevote' = [msgsPrevote EXCEPT ![r] = msgsPrevote[r] \cup {msg}] /\ UNCHANGED <> @@ -232,7 +235,6 @@ InsertFaultyPrecommitMessage == \E id \in Values: LET msg == AsMsg([type |-> "PRECOMMIT", src |-> src, round |-> r, id |-> id]) IN - \*/\ msg \notin msgsPrecommit[r] \* optimization /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![r] = msgsPrecommit[r] \cup {msg}] /\ UNCHANGED <> @@ -240,11 +242,13 @@ InsertFaultyPrecommitMessage == InsertFaultyProposalMessage == \E srcA \in Faulty, r \in Rounds, idV \in Values: LET newMsg == AsMsg([type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV]) IN - \*/\ newMsg \notin msgsPropose[r] \* optimization /\ msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED <> Next == + \/ InsertFaultyPrevoteMessage + \/ InsertFaultyPrecommitMessage + \/ InsertFaultyProposalMessage \/ \E p \in Corr: \/ UponProposalInPropose(p) \/ UponProposalInProposeAndPrevote(p) @@ -252,10 +256,7 @@ Next == \/ UponQuorumOfPrevotesAny(p) \/ UponProposalInPrevoteOrCommitAndPrevote(p) \/ UponQuorumOfPrecommitsAny(p) - \/ InsertFaultyPrevoteMessage - \/ InsertFaultyPrecommitMessage \/ UponProposalInPrecommitNoDecision(p) - \/ InsertFaultyProposalMessage \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds \*\/ UNCHANGED vars From 5f164bb13b87af27d9d0d0ff170e4d81794c650b Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Mon, 23 Dec 2019 13:44:02 +0100 Subject: [PATCH 11/38] injecting faults before running consensus --- .../TendermintAccountabilityApa.tla | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index 2eab153..5b5f6c5 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -14,6 +14,7 @@ CONSTANTS N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes +NFaultyMessages == 8 \* the number of injected faulty messages Corr == 1..N-F Faulty == N-F+1..N AllCorr == 1..N @@ -55,6 +56,7 @@ VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a func msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Corr -> set of messages +VARIABLES nfaultsInjected \* the number of faulty messages injected in the system \* this is needed for UNCHANGED vars == < EmptyMsgSet] /\ msgsPropose = [rd \in Rounds |-> EmptyMsgSet] /\ msgsReceived = [p \in Corr |-> EmptyMsgSet] + /\ nfaultsInjected = 0 FaultyMessages == \* the messages that can be sent by the faulty processes (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, @@ -246,17 +249,21 @@ InsertFaultyProposalMessage == /\ UNCHANGED <> Next == - \/ InsertFaultyPrevoteMessage - \/ InsertFaultyPrecommitMessage - \/ InsertFaultyProposalMessage - \/ \E p \in Corr: - \/ UponProposalInPropose(p) - \/ UponProposalInProposeAndPrevote(p) - \/ InsertProposal(p) - \/ UponQuorumOfPrevotesAny(p) - \/ UponProposalInPrevoteOrCommitAndPrevote(p) - \/ UponQuorumOfPrecommitsAny(p) - \/ UponProposalInPrecommitNoDecision(p) + IF nfaultsInjected < NFaultyMessages + THEN /\ nfaultsInjected' = nfaultsInjected + 1 + /\ \/ InsertFaultyPrevoteMessage + \/ InsertFaultyPrecommitMessage + \/ InsertFaultyProposalMessage + ELSE + /\ UNCHANGED nfaultsInjected + /\ \E p \in Corr: + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ InsertProposal(p) + \/ UponQuorumOfPrevotesAny(p) + \/ UponProposalInPrevoteOrCommitAndPrevote(p) + \/ UponQuorumOfPrecommitsAny(p) + \/ UponProposalInPrecommitNoDecision(p) \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds \*\/ UNCHANGED vars From f5a800d7271845cbcd344730b5b7a867d775b560 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Thu, 26 Dec 2019 19:38:16 +0100 Subject: [PATCH 12/38] bugfix in InsertFaultyProposalMessage --- spec/fork-cases/TendermintAccountabilityApa.tla | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla index 5b5f6c5..a1b01be 100644 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ b/spec/fork-cases/TendermintAccountabilityApa.tla @@ -243,11 +243,13 @@ InsertFaultyPrecommitMessage == validRound, msgsPropose, msgsPrevote, msgsReceived>> InsertFaultyProposalMessage == - \E srcA \in Faulty, r \in Rounds, idV \in Values: - LET newMsg == AsMsg([type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV]) IN + \E srcA \in Faulty, r \in Rounds, vr \in Rounds \cup {NilRound}, idV \in Values: + LET newMsg == AsMsg([type |-> "PROPOSAL", + src |-> srcA, proposal |-> idV, round |-> r, validRound |-> vr]) IN /\ msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] /\ UNCHANGED <> + validRound, msgsPrecommit, msgsPrevote, msgsReceived>> + Next == IF nfaultsInjected < NFaultyMessages THEN /\ nfaultsInjected' = nfaultsInjected + 1 From 3d679ba63f073c119ef9c90deb7a40b3ed3c909c Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sat, 28 Dec 2019 13:11:59 +0100 Subject: [PATCH 13/38] the specification that introduces faults in the initial state --- .../TendermintAccountabilityApa2.tla | 288 ++++++++++++++++++ 1 file changed, 288 insertions(+) create mode 100644 spec/fork-cases/TendermintAccountabilityApa2.tla diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla new file mode 100644 index 0000000..5536244 --- /dev/null +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -0,0 +1,288 @@ +----------------------------- MODULE TendermintAccountabilityApa2 ----------------------------- +(* + A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability + protocol. + + This is the version for compatibility with Apalache. + *) + +EXTENDS Integers, FiniteSets + +CONSTANTS + PropFun \* the proposer function + +N == 4 \* the total number of processes: correct and faulty +T == 1 \* an upper bound on the number of Byzantine processes +F == 2 \* the number of Byzantine processes +NFaultyMessages == 8 \* the number of injected faulty messages +Corr == 1..N-F +Faulty == N-F+1..N +AllProcs == 1..N +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver +ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {"2"} \* e.g., sent by a Byzantine process +Values == ValidValues \cup InvalidValues \* all values +NilRound == -1 +NilValue == "None" + +\* these are two thresholds that are used in the algorithm +THRESHOLD1 == T + 1 +THRESHOLD2 == 2 * T + 1 + +(* APALACHE *) +a <: b == a + +MT == [type |-> STRING, src |-> Int, round |-> Int, + proposal |-> STRING, validRound |-> Int, id |-> STRING] + +AsMsg(m) == m <: MT +SetOfMsgs(S) == S <: {MT} +EmptyMsgSet == SetOfMsgs({}) + +ConstInit == + \*StartId \in 1..N + \* the proposer is arbitrary -- ok for safety + PropFun \in [Rounds -> AllProcs] + +(* END-OF-APALACHE *) + + +\* these variables are exactly as in the pseudo-code +VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound + +\* book-keeping variables +VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages + msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages + evidence \* at every step, evidence contains the messages that were used by the active process + +\* this is needed for UNCHANGED +vars == <> + +\* A function which gives the proposer for a given round at a given height. +\* Here we use round robin. As Corr and Faulty are assigned non-deterministically, +\* it does not really matter who starts first. +\*Proposer(rd) == 1 + ((StartId + rd) % N) +Proposer(rd) == PropFun[rd] + +Id(v) == v + +IsValid(v) == v \in ValidValues + +\* Given a set of allowed messages Msgs, this operators produces a function from rounds to sets of messages. +\* Importantly, there will be exactly k messages in the image of msgFun. +\* We use this action to produce k faults in an initial state. +ProduceFaults(msgFun, From, k) == + \E f \in [1..k -> From]: + msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}] + +NoEquivocation == + \A r \in Rounds: + /\ \A m1, m2 \in msgsPropose[r]: + m1 /= m2 => m1.src /= m2.src + /\ \A m1, m2 \in msgsPrevote[r]: + m1 /= m2 => m1.src /= m2.src + /\ \A m1, m2 \in msgsPrecommit[r]: + m1 /= m2 => m1.src /= m2.src + +\* here we start with StartRound(0) +Init == + /\ round = [p \in Corr |-> 0] + /\ step = [p \in Corr |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? + /\ decision = [p \in Corr |-> NilValue] + /\ lockedValue = [p \in Corr |-> NilValue] + /\ lockedRound = [p \in Corr |-> NilRound] + /\ validValue = [p \in Corr |-> NilValue] + /\ validRound = [p \in Corr |-> NilRound] + /\ ProduceFaults(msgsPrevote', SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), 8) + /\ ProduceFaults(msgsPrecommit', SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), 8) + /\ ProduceFaults(msgsPropose', + SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, + proposal: Values, validRound: Rounds \cup {NilRound}]), 0) + /\ evidence = EmptyMsgSet + +InitNoEquivocation == + Init /\ NoEquivocation + +FaultyMessages == \* the messages that can be sent by the faulty processes + (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, + round: Rounds, proposal: Values, validRound: Rounds \cup {NilRound}])) + \cup + (SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values])) + \cup + (SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values])) + + +\* lines 22-27 +UponProposalInPropose(p) == + \E v \in Values: + /\ step[p] = "PROPOSE" \* line 22 + /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> NilRound]) IN + /\ msg \in msgsPropose[round[p]] \* line 22 + /\ evidence' = {msg} + /\ LET isGood == IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) IN \* line 23 + LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) + IN \* lines 24-26 + /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup {newMsg}] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 28-33 +UponProposalInProposeAndPrevote(p) == + \E v \in Values, vr \in Rounds: + /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part + /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr]) IN + /\ msg \in msgsPropose[round[p]] \* line 28 + /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 28 + /\ evidence' = PV \union {msg} + /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 + LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, + round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) + IN \* lines 30-32 + /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = + msgsPrevote[round[p]] \cup {newMsg}] + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +InsertProposal(p) == + LET r == round[p] IN + /\ p = Proposer(r) + /\ step[p] = "PROPOSE" + \* if the proposer is sending a proposal, then there are no other proposals + \* by the correct processes for the same round + /\ \A m \in msgsPropose[r]: m.src \in Faulty + /\ \E v \in ValidValues: + LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN + LET newMsg == + AsMsg([type |-> "PROPOSAL", src |-> p, round |-> r, + proposal |-> proposal, validRound |-> validRound[p]]) + IN + \* a correct proposer never sends two proposals + msgsPropose' = [msgsPropose EXCEPT ![r] = + msgsPropose[r] \cup {newMsg}] + /\ evidence' = EmptyMsgSet + /\ UNCHANGED <> + + + \* lines 34-35 + lines 61-64 +UponQuorumOfPrevotesAny(p) == + /\ step[p] = "PREVOTE" \* line 34 and 61 + /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! + /\ evidence' = msgsPrevote[round[p]] + /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup {newMsg}] + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + /\ UNCHANGED <> + + +\* lines 36-46 +UponProposalInPrevoteOrCommitAndPrevote(p) == + \E v \in ValidValues, vr \in Rounds \cup {NilRound}: + /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 + /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), + round |-> round[p], proposal |-> v, validRound |-> vr]) IN + /\ msg \in msgsPropose[round[p]] \* line 36 + /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ evidence' = PV \union {msg} + /\ lockedValue' = + IF step[p] = "PREVOTE" + THEN [lockedValue EXCEPT ![p] = v] \* line 38 + ELSE lockedValue \* else of line 37 + /\ lockedRound' = + IF step[p] = "PREVOTE" + THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 + ELSE lockedRound \* else of line 37 + /\ IF step[p] = "PREVOTE" + THEN + LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]) + IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = + msgsPrecommit[round[p]] \cup {newMsg}] \* line 40, or else of 37 + ELSE UNCHANGED msgsPrecommit \* line 40 + /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 + /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 + /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 + /\ UNCHANGED <> + + +\* lines 11-21 +StartRound(p, r) == + /\ round' = [round EXCEPT ![p] = r] + /\ step' = [step EXCEPT ![p] = "PROPOSE"] + +\* lines 47-48 +UponQuorumOfPrecommitsAny(p) == + /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 + /\ evidence' = msgsPrecommit[round[p]] + /\ round[p] + 1 \in Rounds + /\ StartRound(p, round[p] + 1) + /\ UNCHANGED <> + +\* lines 49-54 +UponProposalInPrecommitNoDecision(p) == + /\ decision[p] = NilValue \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {NilRound}: + /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), + round |-> r, proposal |-> v, validRound |-> vr]) IN + /\ msg \in msgsPropose[r] \* line 49 + /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 49 + /\ evidence' = PV \union {msg} + /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 + \* The original algorithm does not have the 'DECIDED' step, but it increments the height. + \* Thus, we introduced 'DECIDED' here to prevent the process from changing its decision. + /\ step' = [step EXCEPT ![p] = "DECIDED"] + /\ UNCHANGED <> + + +Next == + /\ \E p \in Corr: + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ InsertProposal(p) + \/ UponQuorumOfPrevotesAny(p) + \/ UponProposalInPrevoteOrCommitAndPrevote(p) + \/ UponQuorumOfPrecommitsAny(p) + \/ UponProposalInPrecommitNoDecision(p) + + \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds + \*\/ UNCHANGED vars + +\* simple reachability properties to make sure that the algorithm is doing anything useful +NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" + +NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" + +NoValidPrecommit == + \A r \in Rounds: + \A m \in msgsPrecommit[r]: + m.id = NilValue \/ m.src \in Faulty + +NoHigherRounds == \A p \in Corr: round[p] < 1 + +NoDecision == \A p \in Corr: decision[p] = NilValue + +Agreement == \A p, q \in Corr: + \/ decision[p] = NilValue + \/ decision[q] = NilValue + \/ decision[p] = decision[q] + + +============================================================================= + + + From d14fe5670408c3a8a569b6d622b07917fe2a1d01 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 7 Jan 2020 16:00:33 +0100 Subject: [PATCH 14/38] split faults in three categories --- .../fork-cases/TendermintAccountabilityApa2.tla | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index 5536244..a33b4e1 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -14,7 +14,9 @@ CONSTANTS N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes -NFaultyMessages == 8 \* the number of injected faulty messages +NFaultyProposals == 8 \* the number of injected faulty PROPOSE messages +NFaultyPrevotes == 8 \* the number of injected faulty PREVOTE messages +NFaultyPrecommits == 8 \* the number of injected faulty PRECOMMIT messages Corr == 1..N-F Faulty == N-F+1..N AllProcs == 1..N @@ -95,11 +97,16 @@ Init == /\ lockedRound = [p \in Corr |-> NilRound] /\ validValue = [p \in Corr |-> NilValue] /\ validRound = [p \in Corr |-> NilRound] - /\ ProduceFaults(msgsPrevote', SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), 8) - /\ ProduceFaults(msgsPrecommit', SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), 8) + /\ ProduceFaults(msgsPrevote', + SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), + NFaultyPrevotes) + /\ ProduceFaults(msgsPrecommit', + SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), + NFaultyPrecommits) /\ ProduceFaults(msgsPropose', - SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, - proposal: Values, validRound: Rounds \cup {NilRound}]), 0) + SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, + proposal: Values, validRound: Rounds \cup {NilRound}]), + NFaultyProposals) /\ evidence = EmptyMsgSet InitNoEquivocation == From 87036f266aa856500ba0e9d5128eea23838fab06 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 12 Jan 2020 16:53:54 +0100 Subject: [PATCH 15/38] bugfix in StartRound + explicit NoEquivocation and Amnesia --- .../TendermintAccountabilityApa2.tla | 57 +++++++++++++------ 1 file changed, 40 insertions(+), 17 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index a33b4e1..630a02e 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -14,13 +14,13 @@ CONSTANTS N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes -NFaultyProposals == 8 \* the number of injected faulty PROPOSE messages -NFaultyPrevotes == 8 \* the number of injected faulty PREVOTE messages -NFaultyPrecommits == 8 \* the number of injected faulty PRECOMMIT messages +NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages +NFaultyPrevotes == 4 \* the number of injected faulty PREVOTE messages +NFaultyPrecommits == 4 \* the number of injected faulty PRECOMMIT messages Corr == 1..N-F Faulty == N-F+1..N AllProcs == 1..N -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver +Rounds == 0..3 \* the set of possible rounds, give a bit more freedom to the solver ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one InvalidValues == {"2"} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values @@ -79,15 +79,6 @@ ProduceFaults(msgFun, From, k) == \E f \in [1..k -> From]: msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}] -NoEquivocation == - \A r \in Rounds: - /\ \A m1, m2 \in msgsPropose[r]: - m1 /= m2 => m1.src /= m2.src - /\ \A m1, m2 \in msgsPrevote[r]: - m1 /= m2 => m1.src /= m2.src - /\ \A m1, m2 \in msgsPrecommit[r]: - m1 /= m2 => m1.src /= m2.src - \* here we start with StartRound(0) Init == /\ round = [p \in Corr |-> 0] @@ -108,9 +99,6 @@ Init == proposal: Values, validRound: Rounds \cup {NilRound}]), NFaultyProposals) /\ evidence = EmptyMsgSet - -InitNoEquivocation == - Init /\ NoEquivocation FaultyMessages == \* the messages that can be sent by the faulty processes (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, @@ -226,6 +214,7 @@ UponProposalInPrevoteOrCommitAndPrevote(p) == \* lines 11-21 StartRound(p, r) == + /\ step[p] /= "DECIDED" \* when decided, do not switch the round /\ round' = [round EXCEPT ![p] = r] /\ step' = [step EXCEPT ![p] = "PROPOSE"] @@ -268,7 +257,36 @@ Next == \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds \*\/ UNCHANGED vars - + +(******************************** FORK ACCOUNTABILITY ***************************************) +NoEquivocation == + \A r \in Rounds: + /\ \A m1, m2 \in msgsPropose[r]: + m1 /= m2 => m1.src /= m2.src + /\ \A m1, m2 \in msgsPrevote[r]: + m1 /= m2 => m1.src /= m2.src + /\ \A m1, m2 \in msgsPrecommit[r]: + m1 /= m2 => m1.src /= m2.src + +InitNoEquivocation == + Init /\ NoEquivocation + +\* amneasic behavior by process p +Amnesia(p) == + \E r1, r2 \in Rounds: + /\ r1 < r2 + /\ \E v1, v2 \in ValidValues: + /\ v1 /= v2 + /\ AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> r1, id |-> Id(v1)]) \in msgsPrecommit[r1] + /\ AsMsg([type |-> "PREVOTE", src |-> p, round |-> r2, id |-> Id(v2)]) \in msgsPrecommit[r2] + /\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd <= r2 }: + LET prevotes == + { m \in msgsPrevote[r]: + m.type = "PREVOTE" /\ m.round = r /\ m.id = Id(v2) } + IN + Cardinality(prevotes) < THRESHOLD2 + +(******************************** PROPERTIES ***************************************) \* simple reachability properties to make sure that the algorithm is doing anything useful NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" @@ -288,6 +306,11 @@ Agreement == \A p, q \in Corr: \/ decision[q] = NilValue \/ decision[p] = decision[q] +AgreementAndAmnesia == + Agreement \/ (\E p \in Faulty: Amnesia(p)) + +AgreementNoAmnesia == + Agreement \/ ~(\E p \in Faulty: Amnesia(p)) ============================================================================= From f88869e1aa604c8d2965837f3620dcd4507a8282 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 14 Jan 2020 09:10:47 +0100 Subject: [PATCH 16/38] small fixes --- spec/fork-cases/TendermintAccountabilityApa2.tla | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index 630a02e..c7a20e6 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -15,12 +15,12 @@ N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes F == 2 \* the number of Byzantine processes NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages -NFaultyPrevotes == 4 \* the number of injected faulty PREVOTE messages -NFaultyPrecommits == 4 \* the number of injected faulty PRECOMMIT messages +NFaultyPrevotes == 6 \* the number of injected faulty PREVOTE messages +NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages Corr == 1..N-F Faulty == N-F+1..N AllProcs == 1..N -Rounds == 0..3 \* the set of possible rounds, give a bit more freedom to the solver +Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one InvalidValues == {"2"} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values From 576f4276c63a116324ef587b47d456808c4c8e7f Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Fri, 7 Feb 2020 17:38:21 +0100 Subject: [PATCH 17/38] wip: still working on an inductive invariant --- .../TendermintAccountabilityApa2.tla | 252 +++++++++++++++++- 1 file changed, 249 insertions(+), 3 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index c7a20e6..1f20432 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -1,7 +1,7 @@ ----------------------------- MODULE TendermintAccountabilityApa2 ----------------------------- (* A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability - protocol. + protocol. In this version, the faults are injected right in the initial states. This is the version for compatibility with Apalache. *) @@ -42,7 +42,6 @@ SetOfMsgs(S) == S <: {MT} EmptyMsgSet == SetOfMsgs({}) ConstInit == - \*StartId \in 1..N \* the proposer is arbitrary -- ok for safety PropFun \in [Rounds -> AllProcs] @@ -171,7 +170,8 @@ InsertProposal(p) == \* lines 34-35 + lines 61-64 UponQuorumOfPrevotesAny(p) == /\ step[p] = "PREVOTE" \* line 34 and 61 - /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! + /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 + \* TODO: Note that multiple messages from the same (faulty) process may trigger this rule! /\ evidence' = msgsPrevote[round[p]] /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = @@ -230,6 +230,7 @@ UponQuorumOfPrecommitsAny(p) == \* lines 49-54 UponProposalInPrecommitNoDecision(p) == /\ decision[p] = NilValue \* line 49 + \* TODO: a catch-up is going on here, not exactly Algorithm 1 /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {NilRound}: /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), round |-> r, proposal |-> v, validRound |-> vr]) IN @@ -258,6 +259,251 @@ Next == \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds \*\/ UNCHANGED vars +(******************************** INVARIANTS *************************************************) +(* first, we define the sets of all potential messages *) +AllProposals == + [type: {"PROPOSAL"}, + src: AllProcs, + round: Rounds, + proposal: Values \cup {NilValue}, + validRound: Rounds \cup {NilRound}] <: {MT} + +AllPrevotes == + [type: {"PREVOTE"}, + src: AllProcs, + round: Rounds, + id: Values \cup {NilValue}] <: {MT} + +AllPrecommits == + [type: {"PRECOMMIT"}, + src: AllProcs, + round: Rounds, + id: Values \cup {NilValue}] <: {MT} + +BenignRoundsInMessages(msgfun) == + \* the message function never contains a message for a wrong round + \A r \in Rounds: + \A m \in msgfun[r]: + r = m.round + +(* the standard type invariant -- importantly, it is inductive *) +TypeOK == + /\ round \in [Corr -> Rounds] + /\ step \in [Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" }] + /\ decision \in [Corr -> ValidValues \cup {NilValue}] + /\ lockedValue \in [Corr -> ValidValues \cup {NilValue}] + /\ lockedRound \in [Corr -> Rounds \cup {NilRound}] + /\ validValue \in [Corr -> ValidValues \cup {NilValue}] + /\ validRound \in [Corr -> Rounds \cup {NilRound}] + /\ msgsPropose \in [Rounds -> SUBSET AllProposals] + /\ BenignRoundsInMessages(msgsPropose) + /\ msgsPrevote \in [Rounds -> SUBSET AllPrevotes] + /\ BenignRoundsInMessages(msgsPrevote) + /\ msgsPrecommit \in [Rounds -> SUBSET AllPrecommits] + /\ BenignRoundsInMessages(msgsPrecommit) + /\ evidence \in SUBSET (AllProposals \union AllPrevotes \union AllPrecommits) + +NoFutureMessagesSent(p) == + \* a correct process does not send messages in the future + \A r \in { rr \in Rounds: rr >= round[p] }: + /\ step[p] /= "PROPOSE" \/ \A m \in msgsPropose[r]: m.src /= p + /\ \/ step[p] \in {"PREVOTE", "PRECOMMIT", "DECIDED"} + \/ \A m \in msgsPrevote[r]: m.src /= p + /\ \/ step[p] \in {"PRECOMMIT", "DECIDED"} + \/ \A m \in msgsPrecommit[r]: m.src /= p + +AllNoFutureMessagesSent == + \A p \in Corr: + NoFutureMessagesSent(p) + +IfInPrevoteThenSentPrevote(p) == + step[p] = "PREVOTE" => + \E m \in msgsPrevote[round[p]]: + /\ m.id \in ValidValues \cup { NilValue } + /\ m.src = p + +AllIfInPrevoteThenSentPrevote == + \A p \in Corr: IfInPrevoteThenSentPrevote(p) + +IfInPrecommitThenSentPrecommit(p) == + step[p] = "PRECOMMIT" => + \E m \in msgsPrecommit[round[p]]: + /\ m.id \in ValidValues \cup { NilValue } + /\ m.src = p + +AllIfInPrecommitThenSentPrecommit == + \A p \in Corr: IfInPrecommitThenSentPrecommit(p) + +IfInDecidedThenValidDecision(p) == + step[p] = "DECIDED" <=> decision[p] \in ValidValues + +AllIfInDecidedThenValidDecision == + \A p \in Corr: IfInDecidedThenValidDecision(p) + +IfInDecidedThenReceivedProposal(p) == + step[p] = "DECIDED" => + \E r \in Rounds: \* r is not necessarily round[p] + /\ \E m \in msgsPropose[r]: + /\ m.src = Proposer(r) + /\ m.proposal = decision[p] + \* not inductive: /\ m.src \in Corr => (m.validRound <= r) + +AllIfInDecidedThenReceivedProposal == + \A p \in Corr: IfInDecidedThenReceivedProposal(p) + +IfInDecidedThenReceivedTwoThirds(p) == + step[p] = "DECIDED" => + \E r \in Rounds: + LET PV == { m \in msgsPrecommit[r]: m.id = decision[p] } IN + Cardinality(PV) >= THRESHOLD2 + +AllIfInDecidedThenReceivedTwoThirds == + \A p \in Corr: IfInDecidedThenReceivedTwoThirds(p) + +ProposalsNeverSendLargerValidRound == + \A r \in Rounds: + \A m \in msgsPropose[r]: + \/ m.src \in Faulty + \/ m.validRound <= m.round + +ProposalInRound(r, proposedVal, vr) == + \E m \in msgsPropose[r]: + /\ m.src = Proposer(r) + /\ m.proposal = proposedVal + /\ m.validRound = vr + +TwoThirdsPrevotes(vr, v) == + LET PV == { mm \in msgsPrevote[vr]: mm.id = v } IN + Cardinality(PV) >= THRESHOLD2 + +IfSentPrevoteThenReceivedProposalOrTwoThirds(r) == + \A mpv \in msgsPrevote[r]: + \/ mpv.src \in Faulty + \* lockedRound and lockedValue is beyond my comprehension + \/ mpv.id = NilValue + \//\ mpv.src \in Corr + /\ mpv.id /= NilValue + /\ \/ ProposalInRound(r, mpv.id, NilRound) + \/ \E vr \in { rr \in Rounds: rr < r }: + /\ ProposalInRound(r, mpv.id, vr) + /\ TwoThirdsPrevotes(vr, mpv.id) + +AllIfSentPrevoteThenReceivedProposalOrTwoThirds == + \A r \in Rounds: + IfSentPrevoteThenReceivedProposalOrTwoThirds(r) + +IfSentPrecommitThenReceivedTwoThirds == + \A r \in Rounds: + \A mpc \in msgsPrecommit[r]: + \/ mpc.src \in Faulty + \/ /\ mpc.src \in Corr + /\ \/ /\ mpc.id \in ValidValues + /\ LET PV == { m \in msgsPrevote[r]: m.id = mpc.id } IN + Cardinality(PV) >= THRESHOLD2 + \/ /\ mpc.id = NilValue + /\ Cardinality(msgsPrevote[r]) >= THRESHOLD2 + +LockedRoundIffLockedValue(p) == + (lockedRound[p] = NilRound) <=> (lockedValue[p] = NilValue) + +AllLockedRoundIffLockedValue == + \A p \in Corr: LockedRoundIffLockedValue(p) + +IfLockedRoundThenSentCommit(p) == + lockedRound[p] /= NilRound + => \E r \in { rr \in Rounds: rr <= round[p] }: + \E m \in msgsPrecommit[r]: + m.src = p /\ m.id = lockedValue[p] + +AllIfLockedRoundThenSentCommit == + \A p \in Corr: IfLockedRoundThenSentCommit(p) + +LatestPrecommitHasLockedRound(p) == + LET pPrecommits == {mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p } IN + pPrecommits /= {} <: {MT} + => LET latest == + CHOOSE m \in pPrecommits: + \A m2 \in pPrecommits: + m2.round < m.round + IN + /\ lockedRound[p] = latest.round + /\ lockedValue[p] = latest.id + +AllLatestPrecommitHasLockedRound == + \A p \in Corr: + LatestPrecommitHasLockedRound(p) + +ValidRoundNotSmallerThanLockedRound(p) == + validRound[p] >= lockedRound[p] + +AllValidRoundNotSmallerThanLockedRound == + \A p \in Corr: + ValidRoundNotSmallerThanLockedRound(p) + +ValidRoundIffValidValue(p) == + (validRound[p] = NilRound) <=> (validValue[p] = NilValue) + +AllValidRoundIffValidValue == + \A p \in Corr: ValidRoundIffValidValue(p) + +IfValidRoundThenTwoThirds(p) == + \/ validRound[p] = NilRound + \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN + Cardinality(PV) >= THRESHOLD2 + +AllIfValidRoundThenTwoThirds == + \A p \in Corr: IfValidRoundThenTwoThirds(p) + +IfValidRoundThenProposal(p) == + \/ validRound[p] = NilRound + \/ \E m \in msgsPropose[validRound[p]]: + m.proposal = validValue[p] + +AllIfValidRoundThenProposal == + \A p \in Corr: IfValidRoundThenProposal(p) + +NoEquivocationByCorrect(r, msgs) == + \* Every correct process sends only one value or NilValue. + \* This test has quantifier alternation -- a threat for all decision procedures. + \* Luckily, the sets Corr and ValidValues are small. + \A p \in Corr: + \E v \in ValidValues \cup {NilValue}: + \A m \in msgs[r]: + \/ m.src /= p + \/ m.id = v + +ProposalsByProposer(r, msgs) == + \* if the proposer is not faulty, it sends only one value + \E v \in ValidValues: + \A m \in msgs[r]: + \/ m.src \in Faulty + \/ m.src = Proposer(r) /\ m.proposal = v + +AllNoEquivocationByCorrect == + \A r \in Rounds: + /\ ProposalsByProposer(r, msgsPropose) + /\ NoEquivocationByCorrect(r, msgsPrevote) + /\ NoEquivocationByCorrect(r, msgsPrecommit) + +Inv == + /\ TypeOK + /\ AllNoFutureMessagesSent + /\ AllIfInPrevoteThenSentPrevote + /\ AllIfInPrecommitThenSentPrecommit + /\ AllIfInDecidedThenReceivedProposal + /\ AllIfInDecidedThenReceivedTwoThirds + /\ AllIfInDecidedThenValidDecision + /\ AllLockedRoundIffLockedValue + /\ AllIfLockedRoundThenSentCommit + /\ AllLatestPrecommitHasLockedRound + \* not inductive: /\ AllValidRoundNotSmallerThanLockedRound + /\ AllIfValidRoundThenTwoThirds + /\ AllIfValidRoundThenProposal + \* not inductive: /\ ProposalsNeverSendLargerValidRound + /\ AllIfSentPrevoteThenReceivedProposalOrTwoThirds + /\ IfSentPrecommitThenReceivedTwoThirds + /\ AllNoEquivocationByCorrect + (******************************** FORK ACCOUNTABILITY ***************************************) NoEquivocation == \A r \in Rounds: From 3840d2a73f7a0870613163e9a4827feb9c3a21b5 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 18 Feb 2020 13:42:36 +0100 Subject: [PATCH 18/38] the first inductive invariant --- .../TendermintAccountabilityApa2.tla | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index 1f20432..add7057 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -13,7 +13,7 @@ CONSTANTS N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes -F == 2 \* the number of Byzantine processes +F == 1 \* the number of Byzantine processes NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages NFaultyPrevotes == 6 \* the number of injected faulty PREVOTE messages NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages @@ -303,10 +303,17 @@ TypeOK == /\ BenignRoundsInMessages(msgsPrecommit) /\ evidence \in SUBSET (AllProposals \union AllPrevotes \union AllPrecommits) -NoFutureMessagesSent(p) == +NoFutureMessagesForLargerRounds(p) == + \* a correct process does not send messages for the later rounds + \A r \in { rr \in Rounds: rr > round[p] }: + /\ \A m \in msgsPropose[r]: m.src /= p + /\ \A m \in msgsPrevote[r]: m.src /= p + /\ \A m \in msgsPrecommit[r]: m.src /= p + +NoFutureMessagesForCurrentRound(p) == \* a correct process does not send messages in the future - \A r \in { rr \in Rounds: rr >= round[p] }: - /\ step[p] /= "PROPOSE" \/ \A m \in msgsPropose[r]: m.src /= p + LET r == round[p] IN + /\ Proposer(r) = p \/ \A m \in msgsPropose[r]: m.src /= p /\ \/ step[p] \in {"PREVOTE", "PRECOMMIT", "DECIDED"} \/ \A m \in msgsPrevote[r]: m.src /= p /\ \/ step[p] \in {"PRECOMMIT", "DECIDED"} @@ -314,7 +321,8 @@ NoFutureMessagesSent(p) == AllNoFutureMessagesSent == \A p \in Corr: - NoFutureMessagesSent(p) + /\ NoFutureMessagesForCurrentRound(p) + /\ NoFutureMessagesForLargerRounds(p) IfInPrevoteThenSentPrevote(p) == step[p] = "PREVOTE" => @@ -484,6 +492,20 @@ AllNoEquivocationByCorrect == /\ ProposalsByProposer(r, msgsPropose) /\ NoEquivocationByCorrect(r, msgsPrevote) /\ NoEquivocationByCorrect(r, msgsPrecommit) + +\* construct the set of the message senders +Senders(M) == { m.src: m \in M } + +\* an invariant by Josef: +\* if T + 1 processes precommit on the same value in a round, +\* then in the future rounds there are less than 2T + 1 prevotes for another value +PrecommitsLockValue == + \A r \in Rounds: + \A v \in ValidValues \union {NilValue}: + \/ Cardinality(Senders({m \in msgsPrecommit[r]: m.id = v})) < T + 1 + \/ \A fr \in { rr \in Rounds: rr > r }: \* future rounds + \A w \in (Values \union {NilValue}) \ {v}: + Cardinality(Senders({m \in msgsPrevote[fr]: m.id = w})) < 2 * T + 1 Inv == /\ TypeOK @@ -503,6 +525,7 @@ Inv == /\ AllIfSentPrevoteThenReceivedProposalOrTwoThirds /\ IfSentPrecommitThenReceivedTwoThirds /\ AllNoEquivocationByCorrect + /\ PrecommitsLockValue (******************************** FORK ACCOUNTABILITY ***************************************) NoEquivocation == @@ -547,6 +570,7 @@ NoHigherRounds == \A p \in Corr: round[p] < 1 NoDecision == \A p \in Corr: decision[p] = NilValue +\* the safety property -- agreement Agreement == \A p, q \in Corr: \/ decision[p] = NilValue \/ decision[q] = NilValue From 01492e56e1033e270e4bdedf9be1c08585da2fc2 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 19 Feb 2020 09:31:43 +0100 Subject: [PATCH 19/38] tidying up the inductive invariants --- .../TendermintAccountabilityApa2.tla | 147 +++++++++++------- 1 file changed, 88 insertions(+), 59 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index add7057..a4a16af 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -1,15 +1,17 @@ ----------------------------- MODULE TendermintAccountabilityApa2 ----------------------------- (* - A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability - protocol. In this version, the faults are injected right in the initial states. + A TLA+ specification of subset of Tendermint consensus needed to formalize + fork accountability protocol. In this version, the faults are injected right + in the initial states. This is the version for compatibility with Apalache. + + Zarko Milosevic, Igor Konnov, 2019-2020. *) EXTENDS Integers, FiniteSets -CONSTANTS - PropFun \* the proposer function +CONSTANTS PropFun \* the proposer function N == 4 \* the total number of processes: correct and faulty T == 1 \* an upper bound on the number of Byzantine processes @@ -17,9 +19,9 @@ F == 1 \* the number of Byzantine processes NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages NFaultyPrevotes == 6 \* the number of injected faulty PREVOTE messages NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages -Corr == 1..N-F -Faulty == N-F+1..N -AllProcs == 1..N +Corr == {"c1", "c2", "c3"} \* 1..N-F +Faulty == {"f1"} \* N-F+1..N +AllProcs == Corr \cup Faulty Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one InvalidValues == {"2"} \* e.g., sent by a Byzantine process @@ -41,10 +43,8 @@ AsMsg(m) == m <: MT SetOfMsgs(S) == S <: {MT} EmptyMsgSet == SetOfMsgs({}) -ConstInit == - \* the proposer is arbitrary -- ok for safety - PropFun \in [Rounds -> AllProcs] - +\* the proposer is arbitrary -- works for safety +ConstInit == PropFun \in [Rounds -> AllProcs] (* END-OF-APALACHE *) @@ -52,26 +52,31 @@ ConstInit == VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound \* book-keeping variables -VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - evidence \* at every step, evidence contains the messages that were used by the active process + +VARIABLES msgsPropose, + \* PROPOSE messages broadcasted in the system, a function Rounds -> set of messages + msgsPrevote, + \* PREVOTE messages broadcasted in the system, a function Rounds -> set of messages + msgsPrecommit, + \* PRECOMMIT messages broadcasted in the system, a function Rounds -> set of messages + evidence + \* at every step, evidence contains the messages that were used by the active process \* this is needed for UNCHANGED -vars == <> +vars == <> \* A function which gives the proposer for a given round at a given height. -\* Here we use round robin. As Corr and Faulty are assigned non-deterministically, -\* it does not really matter who starts first. -\*Proposer(rd) == 1 + ((StartId + rd) % N) Proposer(rd) == PropFun[rd] +\* A value hash. The identity is a perfect hash, except it does not shrink the set. Id(v) == v +\* The validity predicate IsValid(v) == v \in ValidValues -\* Given a set of allowed messages Msgs, this operators produces a function from rounds to sets of messages. +\* Given a set of allowed messages Msgs, this operator produces a function from +\* rounds to sets of messages. \* Importantly, there will be exactly k messages in the image of msgFun. \* We use this action to produce k faults in an initial state. ProduceFaults(msgFun, From, k) == @@ -259,7 +264,7 @@ Next == \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds \*\/ UNCHANGED vars -(******************************** INVARIANTS *************************************************) +(******************************** INVARIANTS *********************************) (* first, we define the sets of all potential messages *) AllProposals == [type: {"PROPOSAL"}, @@ -304,7 +309,7 @@ TypeOK == /\ evidence \in SUBSET (AllProposals \union AllPrevotes \union AllPrecommits) NoFutureMessagesForLargerRounds(p) == - \* a correct process does not send messages for the later rounds + \* a correct process does not send messages for the future rounds \A r \in { rr \in Rounds: rr > round[p] }: /\ \A m \in msgsPropose[r]: m.src /= p /\ \A m \in msgsPrevote[r]: m.src /= p @@ -319,11 +324,13 @@ NoFutureMessagesForCurrentRound(p) == /\ \/ step[p] \in {"PRECOMMIT", "DECIDED"} \/ \A m \in msgsPrecommit[r]: m.src /= p +\* the correct processes never send future messages AllNoFutureMessagesSent == \A p \in Corr: /\ NoFutureMessagesForCurrentRound(p) /\ NoFutureMessagesForLargerRounds(p) +\* a correct process in the PREVOTE state has sent a PREVOTE message IfInPrevoteThenSentPrevote(p) == step[p] = "PREVOTE" => \E m \in msgsPrevote[round[p]]: @@ -333,6 +340,7 @@ IfInPrevoteThenSentPrevote(p) == AllIfInPrevoteThenSentPrevote == \A p \in Corr: IfInPrevoteThenSentPrevote(p) +\* a correct process in the PRECOMMIT state has sent a PRECOMMIT message IfInPrecommitThenSentPrecommit(p) == step[p] = "PRECOMMIT" => \E m \in msgsPrecommit[round[p]]: @@ -342,12 +350,14 @@ IfInPrecommitThenSentPrecommit(p) == AllIfInPrecommitThenSentPrecommit == \A p \in Corr: IfInPrecommitThenSentPrecommit(p) +\* a process in the PRECOMMIT state has sent a PRECOMMIT message IfInDecidedThenValidDecision(p) == step[p] = "DECIDED" <=> decision[p] \in ValidValues AllIfInDecidedThenValidDecision == \A p \in Corr: IfInDecidedThenValidDecision(p) +\* a decided process should have received a proposal on its decision IfInDecidedThenReceivedProposal(p) == step[p] = "DECIDED" => \E r \in Rounds: \* r is not necessarily round[p] @@ -359,6 +369,7 @@ IfInDecidedThenReceivedProposal(p) == AllIfInDecidedThenReceivedProposal == \A p \in Corr: IfInDecidedThenReceivedProposal(p) +\* a decided process has received two-thirds of precommit messages IfInDecidedThenReceivedTwoThirds(p) == step[p] = "DECIDED" => \E r \in Rounds: @@ -368,12 +379,7 @@ IfInDecidedThenReceivedTwoThirds(p) == AllIfInDecidedThenReceivedTwoThirds == \A p \in Corr: IfInDecidedThenReceivedTwoThirds(p) -ProposalsNeverSendLargerValidRound == - \A r \in Rounds: - \A m \in msgsPropose[r]: - \/ m.src \in Faulty - \/ m.validRound <= m.round - +\* for a round r, there is proposal by the round proposer for a valid round vr ProposalInRound(r, proposedVal, vr) == \E m \in msgsPropose[r]: /\ m.src = Proposer(r) @@ -384,6 +390,9 @@ TwoThirdsPrevotes(vr, v) == LET PV == { mm \in msgsPrevote[vr]: mm.id = v } IN Cardinality(PV) >= THRESHOLD2 +\* if a process sends a PREVOTE, then there are three possibilities: +\* 1) the process is faulty, 2) the PREVOTE cotains Nil, +\* 3) there is a proposal in an earlier (valid) round and two thirds of PREVOTES IfSentPrevoteThenReceivedProposalOrTwoThirds(r) == \A mpv \in msgsPrevote[r]: \/ mpv.src \in Faulty @@ -400,6 +409,8 @@ AllIfSentPrevoteThenReceivedProposalOrTwoThirds == \A r \in Rounds: IfSentPrevoteThenReceivedProposalOrTwoThirds(r) +\* if a correct process has sent a PRECOMMIT, then there are two thirds, +\* either on a valid value, or a nil value IfSentPrecommitThenReceivedTwoThirds == \A r \in Rounds: \A mpc \in msgsPrecommit[r]: @@ -411,12 +422,14 @@ IfSentPrecommitThenReceivedTwoThirds == \/ /\ mpc.id = NilValue /\ Cardinality(msgsPrevote[r]) >= THRESHOLD2 +\* there is a locked round if a only if there is a locked value LockedRoundIffLockedValue(p) == (lockedRound[p] = NilRound) <=> (lockedValue[p] = NilValue) AllLockedRoundIffLockedValue == \A p \in Corr: LockedRoundIffLockedValue(p) +\* when a process locked a round, it should have seen a precommit on a locked value IfLockedRoundThenSentCommit(p) == lockedRound[p] /= NilRound => \E r \in { rr \in Rounds: rr <= round[p] }: @@ -426,6 +439,7 @@ IfLockedRoundThenSentCommit(p) == AllIfLockedRoundThenSentCommit == \A p \in Corr: IfLockedRoundThenSentCommit(p) +\* a process always locks the latest round, for which it has sent a PRECOMMIT LatestPrecommitHasLockedRound(p) == LET pPrecommits == {mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p } IN pPrecommits /= {} <: {MT} @@ -441,12 +455,9 @@ AllLatestPrecommitHasLockedRound == \A p \in Corr: LatestPrecommitHasLockedRound(p) +\* NOT USED? ValidRoundNotSmallerThanLockedRound(p) == validRound[p] >= lockedRound[p] - -AllValidRoundNotSmallerThanLockedRound == - \A p \in Corr: - ValidRoundNotSmallerThanLockedRound(p) ValidRoundIffValidValue(p) == (validRound[p] = NilRound) <=> (validValue[p] = NilValue) @@ -454,6 +465,7 @@ ValidRoundIffValidValue(p) == AllValidRoundIffValidValue == \A p \in Corr: ValidRoundIffValidValue(p) +\* if validRound is defined, then there are two-thirds of PREVOTEs IfValidRoundThenTwoThirds(p) == \/ validRound[p] = NilRound \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN @@ -462,6 +474,7 @@ IfValidRoundThenTwoThirds(p) == AllIfValidRoundThenTwoThirds == \A p \in Corr: IfValidRoundThenTwoThirds(p) +\* a valid round can be only set to a valid value that was proposed earlier IfValidRoundThenProposal(p) == \/ validRound[p] = NilRound \/ \E m \in msgsPropose[validRound[p]]: @@ -470,16 +483,17 @@ IfValidRoundThenProposal(p) == AllIfValidRoundThenProposal == \A p \in Corr: IfValidRoundThenProposal(p) +\* Every correct process sends only one value or NilValue. +\* This test has quantifier alternation -- a threat to all decision procedures. +\* Luckily, the sets Corr and ValidValues are small. NoEquivocationByCorrect(r, msgs) == - \* Every correct process sends only one value or NilValue. - \* This test has quantifier alternation -- a threat for all decision procedures. - \* Luckily, the sets Corr and ValidValues are small. \A p \in Corr: \E v \in ValidValues \cup {NilValue}: \A m \in msgs[r]: \/ m.src /= p \/ m.id = v +\* a proposer nevers sends two values ProposalsByProposer(r, msgs) == \* if the proposer is not faulty, it sends only one value \E v \in ValidValues: @@ -496,19 +510,18 @@ AllNoEquivocationByCorrect == \* construct the set of the message senders Senders(M) == { m.src: m \in M } -\* an invariant by Josef: +\* The final piece by Josef Widder: \* if T + 1 processes precommit on the same value in a round, \* then in the future rounds there are less than 2T + 1 prevotes for another value PrecommitsLockValue == \A r \in Rounds: \A v \in ValidValues \union {NilValue}: - \/ Cardinality(Senders({m \in msgsPrecommit[r]: m.id = v})) < T + 1 + \/ Cardinality(Senders({m \in msgsPrecommit[r]: m.id = v})) < THRESHOLD1 \/ \A fr \in { rr \in Rounds: rr > r }: \* future rounds \A w \in (Values \union {NilValue}) \ {v}: - Cardinality(Senders({m \in msgsPrevote[fr]: m.id = w})) < 2 * T + 1 + Cardinality(Senders({m \in msgsPrevote[fr]: m.id = w})) < THRESHOLD2 Inv == - /\ TypeOK /\ AllNoFutureMessagesSent /\ AllIfInPrevoteThenSentPrevote /\ AllIfInPrecommitThenSentPrecommit @@ -518,29 +531,29 @@ Inv == /\ AllLockedRoundIffLockedValue /\ AllIfLockedRoundThenSentCommit /\ AllLatestPrecommitHasLockedRound - \* not inductive: /\ AllValidRoundNotSmallerThanLockedRound - /\ AllIfValidRoundThenTwoThirds - /\ AllIfValidRoundThenProposal - \* not inductive: /\ ProposalsNeverSendLargerValidRound + \* /\ AllIfValidRoundThenTwoThirds \* no need for safety + \*/\ AllIfValidRoundThenProposal \* no need for safety /\ AllIfSentPrevoteThenReceivedProposalOrTwoThirds /\ IfSentPrecommitThenReceivedTwoThirds /\ AllNoEquivocationByCorrect /\ PrecommitsLockValue + +TypedInv == TypeOK /\ Inv -(******************************** FORK ACCOUNTABILITY ***************************************) -NoEquivocation == - \A r \in Rounds: - /\ \A m1, m2 \in msgsPropose[r]: - m1 /= m2 => m1.src /= m2.src - /\ \A m1, m2 \in msgsPrevote[r]: - m1 /= m2 => m1.src /= m2.src - /\ \A m1, m2 \in msgsPrecommit[r]: - m1 /= m2 => m1.src /= m2.src +(**************************** FORK ACCOUNTABILITY ***************************) +Equivocation == + \E r \in Rounds: + \/ \E m1, m2 \in msgsPropose[r]: + m1 /= m2 /\ m1.src = m2.src + \/ \E m1, m2 \in msgsPrevote[r]: + m1 /= m2 /\ m1.src = m2.src + \/ \E m1, m2 \in msgsPrecommit[r]: + m1 /= m2 /\ m1.src = m2.src InitNoEquivocation == - Init /\ NoEquivocation + Init /\ ~Equivocation -\* amneasic behavior by process p +\* amnesic behavior by a process p Amnesia(p) == \E r1, r2 \in Rounds: /\ r1 < r2 @@ -556,7 +569,7 @@ Amnesia(p) == Cardinality(prevotes) < THRESHOLD2 (******************************** PROPERTIES ***************************************) -\* simple reachability properties to make sure that the algorithm is doing anything useful +\* simple reachability properties to see that the spec is progressing NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" @@ -575,14 +588,30 @@ Agreement == \A p, q \in Corr: \/ decision[p] = NilValue \/ decision[q] = NilValue \/ decision[p] = decision[q] + +Validity == + \A p \in Corr: decision[p] \in ValidValues \union {NilValue} -AgreementAndAmnesia == +\* when agreement is violated, at least one faulty process has amnesia +AgreementOrAmnesia == Agreement \/ (\E p \in Faulty: Amnesia(p)) AgreementNoAmnesia == Agreement \/ ~(\E p \in Faulty: Amnesia(p)) + +InvAndNoEquivocation == + Inv /\ ~Equivocation +\* use this predicate for the initial states +TypedInvNoEquivocationNoAmnesia == + TypeOK /\ Inv /\ ~Equivocation /\ (\A p \in Faulty: ~Amnesia(p)) + +\* the invariant to check +AgreementOrEquivocationOrAmnesia == + \/ Agreement + \/ Equivocation + \/ \E p \in Faulty: Amnesia(p) + + ============================================================================= - - From be2b696978f3ccf746ba5a7d828de59eb547e36b Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 19 Feb 2020 09:36:21 +0100 Subject: [PATCH 20/38] fixed the types --- spec/fork-cases/TendermintAccountabilityApa2.tla | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index a4a16af..e8a3035 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -36,7 +36,7 @@ THRESHOLD2 == 2 * T + 1 (* APALACHE *) a <: b == a -MT == [type |-> STRING, src |-> Int, round |-> Int, +MT == [type |-> STRING, src |-> STRING, round |-> Int, proposal |-> STRING, validRound |-> Int, id |-> STRING] AsMsg(m) == m <: MT From 52902a8a3567b4a3b680e85ad58c3e70ee54ecfb Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Mon, 9 Mar 2020 13:31:01 +0100 Subject: [PATCH 21/38] moving things around --- .../TendermintAccountabilityApa2.tla | 67 ++++++++++--------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla index e8a3035..62dc199 100644 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ b/spec/fork-cases/TendermintAccountabilityApa2.tla @@ -23,8 +23,8 @@ Corr == {"c1", "c2", "c3"} \* 1..N-F Faulty == {"f1"} \* N-F+1..N AllProcs == Corr \cup Faulty Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {"2"} \* e.g., sent by a Byzantine process +ValidValues == {"v0", "v1"} \* e.g., picked by a correct process, or a faulty one +InvalidValues == {"v2"} \* e.g., sent by a Byzantine process Values == ValidValues \cup InvalidValues \* all values NilRound == -1 NilValue == "None" @@ -454,34 +454,6 @@ LatestPrecommitHasLockedRound(p) == AllLatestPrecommitHasLockedRound == \A p \in Corr: LatestPrecommitHasLockedRound(p) - -\* NOT USED? -ValidRoundNotSmallerThanLockedRound(p) == - validRound[p] >= lockedRound[p] - -ValidRoundIffValidValue(p) == - (validRound[p] = NilRound) <=> (validValue[p] = NilValue) - -AllValidRoundIffValidValue == - \A p \in Corr: ValidRoundIffValidValue(p) - -\* if validRound is defined, then there are two-thirds of PREVOTEs -IfValidRoundThenTwoThirds(p) == - \/ validRound[p] = NilRound - \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN - Cardinality(PV) >= THRESHOLD2 - -AllIfValidRoundThenTwoThirds == - \A p \in Corr: IfValidRoundThenTwoThirds(p) - -\* a valid round can be only set to a valid value that was proposed earlier -IfValidRoundThenProposal(p) == - \/ validRound[p] = NilRound - \/ \E m \in msgsPropose[validRound[p]]: - m.proposal = validValue[p] - -AllIfValidRoundThenProposal == - \A p \in Corr: IfValidRoundThenProposal(p) \* Every correct process sends only one value or NilValue. \* This test has quantifier alternation -- a threat to all decision procedures. @@ -539,7 +511,39 @@ Inv == /\ PrecommitsLockValue TypedInv == TypeOK /\ Inv - + +\* UNUSED FOR SAFETY +ValidRoundNotSmallerThanLockedRound(p) == + validRound[p] >= lockedRound[p] + +\* UNUSED FOR SAFETY +ValidRoundIffValidValue(p) == + (validRound[p] = NilRound) <=> (validValue[p] = NilValue) + +\* UNUSED FOR SAFETY +AllValidRoundIffValidValue == + \A p \in Corr: ValidRoundIffValidValue(p) + +\* if validRound is defined, then there are two-thirds of PREVOTEs +IfValidRoundThenTwoThirds(p) == + \/ validRound[p] = NilRound + \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN + Cardinality(PV) >= THRESHOLD2 + +\* UNUSED FOR SAFETY +AllIfValidRoundThenTwoThirds == + \A p \in Corr: IfValidRoundThenTwoThirds(p) + +\* a valid round can be only set to a valid value that was proposed earlier +IfValidRoundThenProposal(p) == + \/ validRound[p] = NilRound + \/ \E m \in msgsPropose[validRound[p]]: + m.proposal = validValue[p] + +\* UNUSED FOR SAFETY +AllIfValidRoundThenProposal == + \A p \in Corr: IfValidRoundThenProposal(p) + (**************************** FORK ACCOUNTABILITY ***************************) Equivocation == \E r \in Rounds: @@ -612,6 +616,5 @@ AgreementOrEquivocationOrAmnesia == \/ Equivocation \/ \E p \in Faulty: Amnesia(p) - ============================================================================= From cab07848152b0c2f75173f3080f1cd6bc3dab72e Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 10 Mar 2020 17:07:20 +0100 Subject: [PATCH 22/38] refactored TendermintAccountabilityApa2 and made it modular --- spec/fork-cases/MC_n4_f1.tla | 26 ++ spec/fork-cases/MC_n4_f2.tla | 26 ++ spec/fork-cases/TendermintAcc3.tla | 358 ++++++++++++++++++++++++ spec/fork-cases/TendermintAccDebug3.tla | 100 +++++++ spec/fork-cases/TendermintAccInv3.tla | 304 ++++++++++++++++++++ 5 files changed, 814 insertions(+) create mode 100644 spec/fork-cases/MC_n4_f1.tla create mode 100644 spec/fork-cases/MC_n4_f2.tla create mode 100644 spec/fork-cases/TendermintAcc3.tla create mode 100644 spec/fork-cases/TendermintAccDebug3.tla create mode 100644 spec/fork-cases/TendermintAccInv3.tla diff --git a/spec/fork-cases/MC_n4_f1.tla b/spec/fork-cases/MC_n4_f1.tla new file mode 100644 index 0000000..6d3072f --- /dev/null +++ b/spec/fork-cases/MC_n4_f1.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n4_f1 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3"}, + Amnesic <- {} <: {STRING}, + Byzantine <- {"f1"}, + N <- 4, + T <- 1, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/MC_n4_f2.tla b/spec/fork-cases/MC_n4_f2.tla new file mode 100644 index 0000000..68d25f1 --- /dev/null +++ b/spec/fork-cases/MC_n4_f2.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n4_f2 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2"}, + Amnesic <- {"f3"}, + Byzantine <- {"f4"}, + N <- 4, + T <- 1, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/TendermintAcc3.tla b/spec/fork-cases/TendermintAcc3.tla new file mode 100644 index 0000000..3ad3fb4 --- /dev/null +++ b/spec/fork-cases/TendermintAcc3.tla @@ -0,0 +1,358 @@ +----------------------------- MODULE TendermintAcc3 --------------------------- +(* + A TLA+ specification of a simplified Tendermint consensus, tuned for + fork accountability. The simplifications are as follows: + + - the procotol runs for one height, that is, one-shot consensus + + - this specification focuses on safety, so timeouts are modelled with + with non-determinism + + - the proposer function is non-determinstic, no fairness is assumed + + - the messages by the faulty processes are injected right in the initial states + + - every process has the voting power of 1 + + - hashes are modelled as identity + + Having the above assumptions in mind, the specification follows the pseudo-code + of the Tendermint paper: https://arxiv.org/abs/1807.04938 + + For the purposes of fork accountability, the faulty processes are partitioned + into two sets: the Byzantine processes and the amnesic processes. + While the Byzantine processes can demonstrate arbitrary behavior, including + no communication, the amnesic processes send their messages but do not hold + to the contract of locked values. + + * Version 3. Modular and parameterized definitions. + * Version 2. Bugfixes in the spec and an inductive invariant. + * Version 1. A preliminary specification. + + Zarko Milosevic, Igor Konnov, 2019-2020. + *) + +EXTENDS Integers, FiniteSets + +(********************* PROTOCOL PARAMETERS **********************************) +CONSTANTS + Corr, \* the set of correct processes + Amnesic, \* the set of amnesic processes, may be empty + Byzantine, \* the set of Byzantine processes, may be empty + N, \* the total number of processes: correct, amnesic, and Byzantine + T, \* an upper bound on the number of Byzantine processes + ValidValues, \* the set of valid values, proposed both by correct and faulty + InvalidValues, \* the set of invalid values, never proposed by the correct ones + MaxRound, \* the maximal round number + Proposer \* the proposer function from 0..NRounds to 1..N + +ASSUME(N = Cardinality(Corr \union Amnesic \union Byzantine)) + +(*************************** DEFINITIONS ************************************) +Faulty == Amnesic \union Byzantine \* the set of faulty processes +AllProcs == Corr \union Faulty \* the set of all processes +Rounds == 0..MaxRound \* the set of potential rounds +NilRound == -1 \* a special value to denote a nil round, outside of Rounds +RoundsOrNil == Rounds \union {NilRound} +Values == ValidValues \union InvalidValues \* the set of all values +NilValue == "None" \* a special value for a nil round, outside of Values +ValuesOrNil == Values \union {NilValue} + +\* a value hash is modeled as identity +Id(v) == v + +\* The validity predicate +IsValid(v) == v \in ValidValues + +\* the two thresholds that are used in the algorithm +THRESHOLD1 == T + 1 \* at least one process is not faulty +THRESHOLD2 == 2 * T + 1 \* a quorum when having N > 3 * T + +(********************* TYPE ANNOTATIONS FOR APALACHE **************************) +\* the operator for type annotations +a <: b == a + +\* the type of message records +MT == [type |-> STRING, src |-> STRING, round |-> Int, + proposal |-> STRING, validRound |-> Int, id |-> STRING] + +\* 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 ******************************) +VARIABLES + round, \* a process round number: Corr -> Rounds + step, \* a process step: Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" } + decision, \* process decision: Corr -> ValuesOrNil + lockedValue, \* a locked value: Corr -> ValuesOrNil + lockedRound, \* a locked round: Corr -> RoundsOrNil + validValue, \* a valid value: Corr -> ValuesOrNil + validRound \* a valid round: Corr -> RoundsOrNil + +\* book-keeping variables +VARIABLES + msgsPropose, \* PROPOSE messages broadcast in the system, Rounds -> Messages + msgsPrevote, \* PREVOTE messages broadcast in the system, Rounds -> Messages + msgsPrecommit, \* PRECOMMIT messages broadcast in the system, Rounds -> Messages + evidence \* the messages that were used by the process that made a transition + +(* to see a type invariant, check TendermintAccInv3 *) + +\* a handy definition used in UNCHANGED +vars == <> + +(********************* PROTOCOL INITIALIZATION ******************************) +FaultyProposals(r) == + SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, + round: {r}, proposal: Values, validRound: RoundsOrNil]) + +FaultyPrevotes(r) == + SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: {r}, id: Values]) + +FaultyPrecommits(r) == + SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: {r}, id: Values]) + +\* The initial states of the protocol. The faults can be in the system already. +Init == + /\ round = [p \in Corr |-> 0] + /\ step = [p \in Corr |-> "PROPOSE"] + /\ decision = [p \in Corr |-> NilValue] + /\ lockedValue = [p \in Corr |-> NilValue] + /\ lockedRound = [p \in Corr |-> NilRound] + /\ validValue = [p \in Corr |-> NilValue] + /\ validRound = [p \in Corr |-> NilRound] + /\ msgsPropose = [r \in Rounds |-> FaultyProposals(r)] + /\ msgsPrevote = [r \in Rounds |-> FaultyPrevotes(r)] + /\ msgsPrecommit = [r \in Rounds |-> FaultyPrecommits(r)] + /\ evidence = EmptyMsgSet + +(************************ MESSAGE PASSING ********************************) +BroadcastProposal(pSrc, pRound, pProposal, pValidRound) == + LET newMsg == + AsMsg([type |-> "PROPOSAL", src |-> pSrc, round |-> pRound, + proposal |-> pProposal, validRound |-> pValidRound]) + IN + msgsPropose' = [msgsPropose EXCEPT ![pRound] = msgsPropose[pRound] \cup {newMsg}] + +BroadcastPrevote(pSrc, pRound, pId) == + LET newMsg == AsMsg([type |-> "PREVOTE", + src |-> pSrc, round |-> pRound, id |-> pId]) + IN + msgsPrevote' = [msgsPrevote EXCEPT ![pRound] = msgsPrevote[pRound] \cup {newMsg}] + +BroadcastPrecommit(pSrc, pRound, pId) == + LET newMsg == AsMsg([type |-> "PRECOMMIT", + src |-> pSrc, round |-> pRound, id |-> pId]) + IN + msgsPrecommit' = [msgsPrecommit EXCEPT ![pRound] = msgsPrecommit[pRound] \cup {newMsg}] + + +(********************* PROTOCOL TRANSITIONS ******************************) +\* lines 12-13 +StartRound(p, r) == + /\ step[p] /= "DECIDED" \* a decided process does not participate in consensus + /\ round' = [round EXCEPT ![p] = r] + /\ step' = [step EXCEPT ![p] = "PROPOSE"] + +\* lines 14-19, a proposal may be sent later +InsertProposal(p) == + LET r == round[p] IN + /\ p = Proposer[r] + /\ step[p] = "PROPOSE" + \* if the proposer is sending a proposal, then there are no other proposals + \* by the correct processes for the same round + /\ \A m \in msgsPropose[r]: m.src /= p + /\ \E v \in ValidValues: + LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN + BroadcastProposal(p, round[p], proposal, validRound[p]) + /\ evidence' = EmptyMsgSet + /\ UNCHANGED <> + +\* lines 22-27 +UponProposalInPropose(p) == + \E v \in Values: + /\ step[p] = "PROPOSE" (* line 22 *) + /\ LET msg == + AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], + round |-> round[p], proposal |-> v, validRound |-> NilRound]) IN + /\ msg \in msgsPropose[round[p]] \* line 22 + /\ evidence' = {msg} + /\ LET mid == (* line 23 *) + IF IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) + THEN Id(v) + ELSE NilValue + IN + BroadcastPrevote(p, round[p], mid) \* lines 24-26 + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 28-33 +UponProposalInProposeAndPrevote(p) == + \E v \in Values, vr \in Rounds: + /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part + /\ LET msg == + AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], + round |-> round[p], proposal |-> v, validRound |-> vr]) + IN + /\ msg \in msgsPropose[round[p]] \* line 28 + /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 28 + /\ evidence' = PV \union {msg} + /\ LET mid == (* line 29 *) + IF IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) + THEN Id(v) + ELSE NilValue + IN + BroadcastPrevote(p, round[p], mid) \* lines 24-26 + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + + \* lines 34-35 + lines 61-64 (onTimeoutPrevote) +UponQuorumOfPrevotesAny(p) == + /\ step[p] = "PREVOTE" \* line 34 and 61 + /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 + \* multiple messages from a process may trigger this rule (no effect on safety) + /\ evidence' = msgsPrevote[round[p]] + /\ BroadcastPrecommit(p, round[p], NilValue) + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + /\ UNCHANGED <> + +\* lines 36-46 +UponProposalInPrevoteOrCommitAndPrevote(p) == + \E v \in ValidValues, vr \in RoundsOrNil: + /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 + /\ LET msg == + AsMsg([type |-> "PROPOSAL", src |-> Proposer[round[p]], + round |-> round[p], proposal |-> v, validRound |-> vr]) IN + /\ msg \in msgsPropose[round[p]] \* line 36 + /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ evidence' = PV \union {msg} + /\ IF step[p] = "PREVOTE" + THEN \* lines 38-41: + /\ lockedValue' = [lockedValue EXCEPT ![p] = v] + /\ lockedRound' = [lockedRound EXCEPT ![p] = round[p]] + /\ BroadcastPrecommit(p, round[p], Id(v)) + /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] + ELSE + UNCHANGED <> + \* lines 42-43 + /\ validValue' = [validValue EXCEPT ![p] = v] + /\ validRound' = [validRound EXCEPT ![p] = round[p]] + /\ UNCHANGED <> + +\* lines 47-48 + 65-67 (onTimeoutPrecommit) +UponQuorumOfPrecommitsAny(p) == + /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 + /\ evidence' = msgsPrecommit[round[p]] + /\ round[p] + 1 \in Rounds + /\ StartRound(p, round[p] + 1) + /\ UNCHANGED <> + +\* lines 49-54 +UponProposalInPrecommitNoDecision(p) == + /\ decision[p] = NilValue \* line 49 + /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in RoundsOrNil: + /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer[r], + round |-> r, proposal |-> v, validRound |-> vr]) IN + /\ msg \in msgsPropose[r] \* line 49 + /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 49 + /\ evidence' = PV \union {msg} + /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 + \* The original algorithm does not have 'DECIDED', but it increments the height. + \* We introduced 'DECIDED' here to prevent the process from changing its decision. + /\ step' = [step EXCEPT ![p] = "DECIDED"] + /\ UNCHANGED <> + +\* the actions below are not essential for safety, but added for completeness +\* TODO: add onTimeoutPropose +\* TODO: add 44-46 +\* TODO: add 55-56 + +(* + * A system transition. In this specificatiom, the system may eventually deadlock, + * e.g., when all processes decide. This is expected behavior, as we focus on safety. + *) +Next == + \E p \in Corr: + \/ InsertProposal(p) + \/ UponProposalInPropose(p) + \/ UponProposalInProposeAndPrevote(p) + \/ UponQuorumOfPrevotesAny(p) + \/ UponProposalInPrevoteOrCommitAndPrevote(p) + \/ UponQuorumOfPrecommitsAny(p) + \/ UponProposalInPrecommitNoDecision(p) + +(**************************** FORK ACCOUNTABILITY ***************************) +\* a state that has equivocation +Equivocation == + \E r \in Rounds: + \/ \E m1, m2 \in msgsPropose[r]: + m1 /= m2 /\ m1.src = m2.src + \/ \E m1, m2 \in msgsPrevote[r]: + m1 /= m2 /\ m1.src = m2.src + \/ \E m1, m2 \in msgsPrecommit[r]: + m1 /= m2 /\ m1.src = m2.src + +\* amnesic behavior by a process p +Amnesia(p) == + \E r1, r2 \in Rounds: + /\ r1 < r2 + /\ \E v1, v2 \in ValidValues: + /\ v1 /= v2 + /\ AsMsg([type |-> "PRECOMMIT", src |-> p, + round |-> r1, id |-> Id(v1)]) \in msgsPrecommit[r1] + /\ AsMsg([type |-> "PREVOTE", src |-> p, + round |-> r2, id |-> Id(v2)]) \in msgsPrecommit[r2] + /\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd <= r2 }: + LET prevotes == + { m \in msgsPrevote[r]: + m.type = "PREVOTE" /\ m.round = r /\ m.id = Id(v2) } + IN + Cardinality(prevotes) < THRESHOLD2 + +\* Exclude the equivocation states, that is, +\* exclude the equivocal messages by the faulty processes +InitNoEquivocation == + Init /\ ~Equivocation + +(******************************** PROPERTIES ***************************************) + +\* the safety property -- agreement +Agreement == \A p, q \in Corr: + \/ decision[p] = NilValue + \/ decision[q] = NilValue + \/ decision[p] = decision[q] + +\* the protocol validity +Validity == + \A p \in Corr: decision[p] \in ValidValues \union {NilValue} + +\* either agreement holds, or the amnesic processes indeed have amnesia +AgreementOrAmnesia == + Agreement \/ (\A p \in Amnesic: Amnesia(p)) + +\* the strong agreement property that also assumes no amnesia +AgreementNoAmnesia == + Agreement /\ \A p \in Amnesic: ~Amnesia(p) + +\* the protocol invariant +AgreementOrEquivocationOrAmnesia == + \/ Agreement + \/ Equivocation + \/ \A p \in Amnesic: Amnesia(p) + +============================================================================= + diff --git a/spec/fork-cases/TendermintAccDebug3.tla b/spec/fork-cases/TendermintAccDebug3.tla new file mode 100644 index 0000000..3b03fd9 --- /dev/null +++ b/spec/fork-cases/TendermintAccDebug3.tla @@ -0,0 +1,100 @@ +----------------------- MODULE TendermintAccDebug3 ----------------------------- +(* + A few definitions that we use for debugging TendermintAcc3, which do not belong + to the specification itself. + + * Version 3. Modular and parameterized definitions. + + Igor Konnov, 2020. + *) + +EXTENDS TendermintAccInv3 + +\* make them parameters? +NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages +NFaultyPrevotes == 6 \* the number of injected faulty PREVOTE messages +NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages + +\* Given a set of allowed messages Msgs, this operator produces a function from +\* rounds to sets of messages. +\* Importantly, there will be exactly k messages in the image of msgFun. +\* We use this action to produce k faults in an initial state. +ProduceFaults(msgFun, From, k) == + \E f \in [1..k -> From]: + msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}] + +\* As TLC explodes with faults, we may have initial states without faults +InitNoFaults == + /\ round = [p \in Corr |-> 0] + /\ step = [p \in Corr |-> "PROPOSE"] + /\ decision = [p \in Corr |-> NilValue] + /\ lockedValue = [p \in Corr |-> NilValue] + /\ lockedRound = [p \in Corr |-> NilRound] + /\ validValue = [p \in Corr |-> NilValue] + /\ validRound = [p \in Corr |-> NilRound] + /\ msgsPropose = [r \in Rounds |-> EmptyMsgSet] + /\ msgsPrevote = [r \in Rounds |-> EmptyMsgSet] + /\ msgsPrecommit = [r \in Rounds |-> EmptyMsgSet] + /\ evidence = EmptyMsgSet + +(* + A specialized version of Init that injects NFaultyProposals proposals, + NFaultyPrevotes prevotes, NFaultyPrecommits precommits by the faulty processes + *) +InitFewFaults == + /\ round = [p \in Corr |-> 0] + /\ step = [p \in Corr |-> "PROPOSE"] + /\ decision = [p \in Corr |-> NilValue] + /\ lockedValue = [p \in Corr |-> NilValue] + /\ lockedRound = [p \in Corr |-> NilRound] + /\ validValue = [p \in Corr |-> NilValue] + /\ validRound = [p \in Corr |-> NilRound] + /\ ProduceFaults(msgsPrevote', + SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), + NFaultyPrevotes) + /\ ProduceFaults(msgsPrecommit', + SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), + NFaultyPrecommits) + /\ ProduceFaults(msgsPropose', + SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, + proposal: Values, validRound: Rounds \cup {NilRound}]), + NFaultyProposals) + /\ evidence = EmptyMsgSet + +\* Add faults incrementally +NextWithFaults == + \* either the protocol makes a step + \/ Next + \* or a faulty process sends a message + \//\ UNCHANGED <> + /\ \E p \in Faulty: + \E r \in Rounds: + \//\ UNCHANGED <> + /\ \E proposal \in ValidValues \union {NilValue}: + \E vr \in RoundsOrNil: + BroadcastProposal(p, r, proposal, vr) + \//\ UNCHANGED <> + /\ \E id \in ValidValues \union {NilValue}: + BroadcastPrevote(p, r, id) + \//\ UNCHANGED <> + /\ \E id \in ValidValues \union {NilValue}: + BroadcastPrecommit(p, r, id) + +(******************************** PROPERTIES ***************************************) +\* simple reachability properties to see that the spec is progressing +NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" + +NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" + +NoValidPrecommit == + \A r \in Rounds: + \A m \in msgsPrecommit[r]: + m.id = NilValue \/ m.src \in Faulty + +NoHigherRounds == \A p \in Corr: round[p] < 1 + +NoDecision == \A p \in Corr: decision[p] = NilValue + +============================================================================= + diff --git a/spec/fork-cases/TendermintAccInv3.tla b/spec/fork-cases/TendermintAccInv3.tla new file mode 100644 index 0000000..c63a3fa --- /dev/null +++ b/spec/fork-cases/TendermintAccInv3.tla @@ -0,0 +1,304 @@ +------------------------- MODULE TendermintAccInv3 ---------------------------- +(* + An inductive invariant for TendermintAcc3, which capture the forked + and non-forked cases. + + * Version 3. Modular and parameterized definitions. + * Version 2. Bugfixes in the spec and an inductive invariant. + + Igor Konnov, 2020. + *) + +EXTENDS TendermintAcc3 + +(************************** TYPE INVARIANT ***********************************) +(* first, we define the sets of all potential messages *) +AllProposals == + [type: {"PROPOSAL"}, + src: AllProcs, + round: Rounds, + proposal: ValuesOrNil, + validRound: RoundsOrNil] <: {MT} + +AllPrevotes == + [type: {"PREVOTE"}, + src: AllProcs, + round: Rounds, + id: ValuesOrNil] <: {MT} + +AllPrecommits == + [type: {"PRECOMMIT"}, + src: AllProcs, + round: Rounds, + id: ValuesOrNil] <: {MT} + +BenignRoundsInMessages(msgfun) == + \* the message function never contains a message for a wrong round + \A r \in Rounds: + \A m \in msgfun[r]: + r = m.round + +(* the standard type invariant -- importantly, it is inductive *) +TypeOK == + /\ round \in [Corr -> Rounds] + /\ step \in [Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" }] + /\ decision \in [Corr -> ValidValues \union {NilValue}] + /\ lockedValue \in [Corr -> ValidValues \union {NilValue}] + /\ lockedRound \in [Corr -> RoundsOrNil] + /\ validValue \in [Corr -> ValidValues \union {NilValue}] + /\ validRound \in [Corr -> RoundsOrNil] + /\ msgsPropose \in [Rounds -> SUBSET AllProposals] + /\ BenignRoundsInMessages(msgsPropose) + /\ msgsPrevote \in [Rounds -> SUBSET AllPrevotes] + /\ BenignRoundsInMessages(msgsPrevote) + /\ msgsPrecommit \in [Rounds -> SUBSET AllPrecommits] + /\ BenignRoundsInMessages(msgsPrecommit) + /\ evidence \in SUBSET (AllProposals \union AllPrevotes \union AllPrecommits) + +(************************** INDUCTIVE INVARIANT *******************************) + +NoFutureMessagesForLargerRounds(p) == + \* a correct process does not send messages for the future rounds + \A r \in { rr \in Rounds: rr > round[p] }: + /\ \A m \in msgsPropose[r]: m.src /= p + /\ \A m \in msgsPrevote[r]: m.src /= p + /\ \A m \in msgsPrecommit[r]: m.src /= p + +NoFutureMessagesForCurrentRound(p) == + \* a correct process does not send messages in the future + LET r == round[p] IN + /\ Proposer[r] = p \/ \A m \in msgsPropose[r]: m.src /= p + /\ \/ step[p] \in {"PREVOTE", "PRECOMMIT", "DECIDED"} + \/ \A m \in msgsPrevote[r]: m.src /= p + /\ \/ step[p] \in {"PRECOMMIT", "DECIDED"} + \/ \A m \in msgsPrecommit[r]: m.src /= p + +\* the correct processes never send future messages +AllNoFutureMessagesSent == + \A p \in Corr: + /\ NoFutureMessagesForCurrentRound(p) + /\ NoFutureMessagesForLargerRounds(p) + +\* a correct process in the PREVOTE state has sent a PREVOTE message +IfInPrevoteThenSentPrevote(p) == + step[p] = "PREVOTE" => + \E m \in msgsPrevote[round[p]]: + /\ m.id \in ValidValues \cup { NilValue } + /\ m.src = p + +AllIfInPrevoteThenSentPrevote == + \A p \in Corr: IfInPrevoteThenSentPrevote(p) + +\* a correct process in the PRECOMMIT state has sent a PRECOMMIT message +IfInPrecommitThenSentPrecommit(p) == + step[p] = "PRECOMMIT" => + \E m \in msgsPrecommit[round[p]]: + /\ m.id \in ValidValues \cup { NilValue } + /\ m.src = p + +AllIfInPrecommitThenSentPrecommit == + \A p \in Corr: IfInPrecommitThenSentPrecommit(p) + +\* a process in the PRECOMMIT state has sent a PRECOMMIT message +IfInDecidedThenValidDecision(p) == + step[p] = "DECIDED" <=> decision[p] \in ValidValues + +AllIfInDecidedThenValidDecision == + \A p \in Corr: IfInDecidedThenValidDecision(p) + +\* a decided process should have received a proposal on its decision +IfInDecidedThenReceivedProposal(p) == + step[p] = "DECIDED" => + \E r \in Rounds: \* r is not necessarily round[p] + /\ \E m \in msgsPropose[r]: + /\ m.src = Proposer[r] + /\ m.proposal = decision[p] + \* not inductive: /\ m.src \in Corr => (m.validRound <= r) + +AllIfInDecidedThenReceivedProposal == + \A p \in Corr: IfInDecidedThenReceivedProposal(p) + +\* a decided process has received two-thirds of precommit messages +IfInDecidedThenReceivedTwoThirds(p) == + step[p] = "DECIDED" => + \E r \in Rounds: + LET PV == { m \in msgsPrecommit[r]: m.id = decision[p] } IN + Cardinality(PV) >= THRESHOLD2 + +AllIfInDecidedThenReceivedTwoThirds == + \A p \in Corr: IfInDecidedThenReceivedTwoThirds(p) + +\* for a round r, there is proposal by the round proposer for a valid round vr +ProposalInRound(r, proposedVal, vr) == + \E m \in msgsPropose[r]: + /\ m.src = Proposer[r] + /\ m.proposal = proposedVal + /\ m.validRound = vr + +TwoThirdsPrevotes(vr, v) == + LET PV == { mm \in msgsPrevote[vr]: mm.id = v } IN + Cardinality(PV) >= THRESHOLD2 + +\* if a process sends a PREVOTE, then there are three possibilities: +\* 1) the process is faulty, 2) the PREVOTE cotains Nil, +\* 3) there is a proposal in an earlier (valid) round and two thirds of PREVOTES +IfSentPrevoteThenReceivedProposalOrTwoThirds(r) == + \A mpv \in msgsPrevote[r]: + \/ mpv.src \in Faulty + \* lockedRound and lockedValue is beyond my comprehension + \/ mpv.id = NilValue + \//\ mpv.src \in Corr + /\ mpv.id /= NilValue + /\ \/ ProposalInRound(r, mpv.id, NilRound) + \/ \E vr \in { rr \in Rounds: rr < r }: + /\ ProposalInRound(r, mpv.id, vr) + /\ TwoThirdsPrevotes(vr, mpv.id) + +AllIfSentPrevoteThenReceivedProposalOrTwoThirds == + \A r \in Rounds: + IfSentPrevoteThenReceivedProposalOrTwoThirds(r) + +\* if a correct process has sent a PRECOMMIT, then there are two thirds, +\* either on a valid value, or a nil value +IfSentPrecommitThenReceivedTwoThirds == + \A r \in Rounds: + \A mpc \in msgsPrecommit[r]: + \/ mpc.src \in Faulty + \/ /\ mpc.src \in Corr + /\ \/ /\ mpc.id \in ValidValues + /\ LET PV == { m \in msgsPrevote[r]: m.id = mpc.id } IN + Cardinality(PV) >= THRESHOLD2 + \/ /\ mpc.id = NilValue + /\ Cardinality(msgsPrevote[r]) >= THRESHOLD2 + +\* there is a locked round if a only if there is a locked value +LockedRoundIffLockedValue(p) == + (lockedRound[p] = NilRound) <=> (lockedValue[p] = NilValue) + +AllLockedRoundIffLockedValue == + \A p \in Corr: LockedRoundIffLockedValue(p) + +\* when a process locked a round, it should have seen a precommit on a locked value +IfLockedRoundThenSentCommit(p) == + lockedRound[p] /= NilRound + => \E r \in { rr \in Rounds: rr <= round[p] }: + \E m \in msgsPrecommit[r]: + m.src = p /\ m.id = lockedValue[p] + +AllIfLockedRoundThenSentCommit == + \A p \in Corr: IfLockedRoundThenSentCommit(p) + +\* a process always locks the latest round, for which it has sent a PRECOMMIT +LatestPrecommitHasLockedRound(p) == + LET pPrecommits == {mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p } IN + pPrecommits /= {} <: {MT} + => LET latest == + CHOOSE m \in pPrecommits: + \A m2 \in pPrecommits: + m2.round < m.round + IN + /\ lockedRound[p] = latest.round + /\ lockedValue[p] = latest.id + +AllLatestPrecommitHasLockedRound == + \A p \in Corr: + LatestPrecommitHasLockedRound(p) + +\* Every correct process sends only one value or NilValue. +\* This test has quantifier alternation -- a threat to all decision procedures. +\* Luckily, the sets Corr and ValidValues are small. +NoEquivocationByCorrect(r, msgs) == + \A p \in Corr: + \E v \in ValidValues \union {NilValue}: + \A m \in msgs[r]: + \/ m.src /= p + \/ m.id = v + +\* a proposer nevers sends two values +ProposalsByProposer(r, msgs) == + \* if the proposer is not faulty, it sends only one value + \E v \in ValidValues: + \A m \in msgs[r]: + \/ m.src \in Faulty + \/ m.src = Proposer[r] /\ m.proposal = v + +AllNoEquivocationByCorrect == + \A r \in Rounds: + /\ ProposalsByProposer(r, msgsPropose) + /\ NoEquivocationByCorrect(r, msgsPrevote) + /\ NoEquivocationByCorrect(r, msgsPrecommit) + +\* construct the set of the message senders +Senders(M) == { m.src: m \in M } + +\* The final piece by Josef Widder: +\* if T + 1 processes precommit on the same value in a round, +\* then in the future rounds there are less than 2T + 1 prevotes for another value +PrecommitsLockValue == + \A r \in Rounds: + \A v \in ValidValues \union {NilValue}: + \/ Cardinality(Senders({m \in msgsPrecommit[r]: m.id = v})) < THRESHOLD1 + \/ \A fr \in { rr \in Rounds: rr > r }: \* future rounds + \A w \in (ValuesOrNil) \ {v}: + Cardinality(Senders({m \in msgsPrevote[fr]: m.id = w})) < THRESHOLD2 + +\* a combination of all lemmas +Inv == + /\ AllNoFutureMessagesSent + /\ AllIfInPrevoteThenSentPrevote + /\ AllIfInPrecommitThenSentPrecommit + /\ AllIfInDecidedThenReceivedProposal + /\ AllIfInDecidedThenReceivedTwoThirds + /\ AllIfInDecidedThenValidDecision + /\ AllLockedRoundIffLockedValue + /\ AllIfLockedRoundThenSentCommit + /\ AllLatestPrecommitHasLockedRound + /\ AllIfSentPrevoteThenReceivedProposalOrTwoThirds + /\ IfSentPrecommitThenReceivedTwoThirds + /\ AllNoEquivocationByCorrect + /\ PrecommitsLockValue + +\* this is the inductive invariant we like to check +TypedInv == TypeOK /\ Inv + +\* UNUSED FOR SAFETY +ValidRoundNotSmallerThanLockedRound(p) == + validRound[p] >= lockedRound[p] + +\* UNUSED FOR SAFETY +ValidRoundIffValidValue(p) == + (validRound[p] = NilRound) <=> (validValue[p] = NilValue) + +\* UNUSED FOR SAFETY +AllValidRoundIffValidValue == + \A p \in Corr: ValidRoundIffValidValue(p) + +\* if validRound is defined, then there are two-thirds of PREVOTEs +IfValidRoundThenTwoThirds(p) == + \/ validRound[p] = NilRound + \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN + Cardinality(PV) >= THRESHOLD2 + +\* UNUSED FOR SAFETY +AllIfValidRoundThenTwoThirds == + \A p \in Corr: IfValidRoundThenTwoThirds(p) + +\* a valid round can be only set to a valid value that was proposed earlier +IfValidRoundThenProposal(p) == + \/ validRound[p] = NilRound + \/ \E m \in msgsPropose[validRound[p]]: + m.proposal = validValue[p] + +\* UNUSED FOR SAFETY +AllIfValidRoundThenProposal == + \A p \in Corr: IfValidRoundThenProposal(p) + +(******************************** PROPERTIES ***************************************) +InvAndNoEquivocation == + Inv /\ ~Equivocation + +\* use this predicate for the initial states +TypedInvNoEquivocationNoAmnesia == + TypeOK /\ Inv /\ ~Equivocation /\ (\A p \in Faulty: ~Amnesia(p)) +============================================================================= + From b33f19fb34d9e835cd5a2ea8404fb0cbb4d35b4f Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 11 Mar 2020 09:56:08 +0100 Subject: [PATCH 23/38] added the few missing transitions (important for liveness) --- spec/fork-cases/MC_n7_f2.tla | 26 +++++++++++++++++++++ spec/fork-cases/MC_n7_f3.tla | 26 +++++++++++++++++++++ spec/fork-cases/TendermintAcc3.tla | 37 +++++++++++++++++++++++++++--- 3 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 spec/fork-cases/MC_n7_f2.tla create mode 100644 spec/fork-cases/MC_n7_f3.tla diff --git a/spec/fork-cases/MC_n7_f2.tla b/spec/fork-cases/MC_n7_f2.tla new file mode 100644 index 0000000..011113e --- /dev/null +++ b/spec/fork-cases/MC_n7_f2.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n7_f2 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3", "c4", "c5"}, + Amnesic <- {}, + Byzantine <- {"f6", "f7"}, + N <- 7, + T <- 2, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/MC_n7_f3.tla b/spec/fork-cases/MC_n7_f3.tla new file mode 100644 index 0000000..b9cb5c9 --- /dev/null +++ b/spec/fork-cases/MC_n7_f3.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n7_f3 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3", "c4"}, + Amnesic <- {}, + Byzantine <- {"f5", "f6", "f7"}, + N <- 7, + T <- 2, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/TendermintAcc3.tla b/spec/fork-cases/TendermintAcc3.tla index 3ad3fb4..c205d9d 100644 --- a/spec/fork-cases/TendermintAcc3.tla +++ b/spec/fork-cases/TendermintAcc3.tla @@ -277,9 +277,36 @@ UponProposalInPrecommitNoDecision(p) == validRound, msgsPropose, msgsPrevote, msgsPrecommit>> \* the actions below are not essential for safety, but added for completeness -\* TODO: add onTimeoutPropose -\* TODO: add 44-46 -\* TODO: add 55-56 + +\* lines 20-21 + 57-60 +OnTimeoutPropose(p) == + /\ step[p] = "PROPOSE" + /\ p /= Proposer[round[p]] + /\ BroadcastPrevote(p, round[p], NilValue) + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 44-46 +OnQuoromOfNilPrevotes(p) == + /\ step[p] = "PREVOTE" + /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(NilValue) } IN + /\ Cardinality(PV) >= THRESHOLD2 \* line 36 + /\ evidence' = PV + /\ BroadcastPrecommit(p, round[p], Id(NilValue)) + /\ step' = [step EXCEPT ![p] = "PREVOTE"] + /\ UNCHANGED <> + +\* lines 55-56 +OnRoundCatchup(p) == + \E r \in {rr \in Rounds: rr > round[p]}: + /\ LET RM == msgsPropose[r] \union msgsPrevote[r] \union msgsPrecommit[r] IN + /\ Cardinality(RM) >= THRESHOLD1 + /\ evidence' = RM + /\ StartRound(p, r) + /\ UNCHANGED <> (* * A system transition. In this specificatiom, the system may eventually deadlock, @@ -294,6 +321,10 @@ Next == \/ UponProposalInPrevoteOrCommitAndPrevote(p) \/ UponQuorumOfPrecommitsAny(p) \/ UponProposalInPrecommitNoDecision(p) + \* the actions below are not essential for safety, but added for completeness + \/ OnTimeoutPropose(p) + \/ OnQuoromOfNilPrevotes(p) + \/ OnRoundCatchup(p) (**************************** FORK ACCOUNTABILITY ***************************) \* a state that has equivocation From 2da59c05f2fdd3300ff4a78f479c8edf7b51c606 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 11 Mar 2020 11:06:00 +0100 Subject: [PATCH 24/38] fixed the input parameters --- spec/fork-cases/MC_n7_f2.tla | 2 +- spec/fork-cases/MC_n7_f3.tla | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/fork-cases/MC_n7_f2.tla b/spec/fork-cases/MC_n7_f2.tla index 011113e..86d8972 100644 --- a/spec/fork-cases/MC_n7_f2.tla +++ b/spec/fork-cases/MC_n7_f2.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4", "c5"}, - Amnesic <- {}, + Amnesic <- {} <: {STRING}, Byzantine <- {"f6", "f7"}, N <- 7, T <- 2, diff --git a/spec/fork-cases/MC_n7_f3.tla b/spec/fork-cases/MC_n7_f3.tla index b9cb5c9..70e004e 100644 --- a/spec/fork-cases/MC_n7_f3.tla +++ b/spec/fork-cases/MC_n7_f3.tla @@ -11,8 +11,8 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4"}, - Amnesic <- {}, - Byzantine <- {"f5", "f6", "f7"}, + Amnesic <- {"f5"}, + Byzantine <- {"f6", "f7"}, N <- 7, T <- 2, ValidValues <- { "v0", "v1" }, From 71921a708a0068e4337048617065edc7ef854693 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 11 Mar 2020 14:53:38 +0100 Subject: [PATCH 25/38] other configurations to check --- spec/fork-cases/MC_n4_f3.tla | 26 ++++++++++++++++++++++++++ spec/fork-cases/MC_n5_f1.tla | 26 ++++++++++++++++++++++++++ spec/fork-cases/MC_n5_f2.tla | 26 ++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) create mode 100644 spec/fork-cases/MC_n4_f3.tla create mode 100644 spec/fork-cases/MC_n5_f1.tla create mode 100644 spec/fork-cases/MC_n5_f2.tla diff --git a/spec/fork-cases/MC_n4_f3.tla b/spec/fork-cases/MC_n4_f3.tla new file mode 100644 index 0000000..9949d9d --- /dev/null +++ b/spec/fork-cases/MC_n4_f3.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n4_f3 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1"}, + Amnesic <- {"a2", "a3"}, + Byzantine <- {"f4"}, + N <- 4, + T <- 1, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/MC_n5_f1.tla b/spec/fork-cases/MC_n5_f1.tla new file mode 100644 index 0000000..149a456 --- /dev/null +++ b/spec/fork-cases/MC_n5_f1.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n5_f1 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3", "c4"}, + Amnesic <- {} <: {STRING}, + Byzantine <- {"f5"}, + N <- 5, + T <- 1, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/MC_n5_f2.tla b/spec/fork-cases/MC_n5_f2.tla new file mode 100644 index 0000000..feb02d1 --- /dev/null +++ b/spec/fork-cases/MC_n5_f2.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n5_f2 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3"}, + Amnesic <- {"a4"}, + Byzantine <- {"f5"}, + N <- 5, + T <- 1, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= From df9683b38a0d4dbcadae3bab653a148f9105ebad Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 18 Mar 2020 10:10:20 +0100 Subject: [PATCH 26/38] removed the old versions of the specs --- spec/fork-cases/TendermintAccountability.tla | 244 ------- .../TendermintAccountabilityApa.tla | 296 --------- .../TendermintAccountabilityApa2.tla | 620 ------------------ spec/fork-cases/TendermintFork.tla | 360 ---------- spec/fork-cases/TendermintForkn_n4t1f2.tla | 40 -- 5 files changed, 1560 deletions(-) delete mode 100644 spec/fork-cases/TendermintAccountability.tla delete mode 100644 spec/fork-cases/TendermintAccountabilityApa.tla delete mode 100644 spec/fork-cases/TendermintAccountabilityApa2.tla delete mode 100644 spec/fork-cases/TendermintFork.tla delete mode 100644 spec/fork-cases/TendermintForkn_n4t1f2.tla diff --git a/spec/fork-cases/TendermintAccountability.tla b/spec/fork-cases/TendermintAccountability.tla deleted file mode 100644 index 04b1696..0000000 --- a/spec/fork-cases/TendermintAccountability.tla +++ /dev/null @@ -1,244 +0,0 @@ ------------------------------ MODULE TendermintAccountability ----------------------------- -(* - A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability - protocol. - - *) - -EXTENDS Integers, FiniteSets - - -N == 4 \* the total number of processes: correct and faulty -T == 1 \* an upper bound on the number of Byzantine processes -F == 2 \* the number of Byzantine processes -Procs == 1..N-F -Faulty == N-F+1..N -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {2} \* e.g., sent by a Byzantine process -Values == ValidValues \cup InvalidValues \* all values -nil == -1 - -\* these are two tresholds that are used in the algorithm -THRESHOLD1 == T + 1 -THRESHOLD2 == 2 * T + 1 - - -\* these variables are exactly as in the pseudo-code -VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound - -\* book-keeping variables -VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Procs -> set of messages - - -\* this is needed for UNCHANGED -vars == <> - -\* A function which gives the proposer for a given round at a given height. -\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, -\* it does not really matter who starts first. -Proposer(rd) == 1 + (rd % N) - -Id(v) == v - -IsValid(v) == v \in ValidValues - - -\* here we start with StartRound(0) -Init == - /\ round = [p \in Procs |-> 0] - /\ step = [p \in Procs |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? - /\ decision = [p \in Procs |-> nil] - /\ lockedValue = [p \in Procs |-> nil] - /\ lockedRound = [p \in Procs |-> -1] - /\ validValue = [p \in Procs |-> nil] - /\ validRound = [p \in Procs |-> -1] - /\ msgsPrevote = [rd \in Rounds |-> {}] - /\ msgsPrecommit = [rd \in Rounds |-> {}] - /\ msgsPropose = [rd \in Rounds |-> {}] - /\ msgsReceived = [p \in Procs |-> {}] - -FaultyMessages == \* the messages that can be sent by the faulty processes - ([type: {"PROPOSAL"}, src: Faulty, - round: Rounds, proposal: Values, validRound: Rounds \cup {-1}]) - \cup - ([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]) - \cup - ([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]) - - -\* lines 22-27 -UponProposalInPropose(p) == - \E v \in Values: - /\ step[p] = "PROPOSE" \* line 22 - /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> -1]) \in msgsPropose[round[p]] \* line 22 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 - LET newMsg == ({[type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil]}) - IN \* lines 24-26 - msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup newMsg] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - - -\* lines 28-33 -UponProposalInProposeAndPrevote(p) == - \E v \in Values, vr \in Rounds: - /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part - /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr]) \in msgsPropose[round[p]] \* line 28 - /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 28 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 - LET newMsg == ({[type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE nil]}) - IN \* lines 30-32 - msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup newMsg] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -\* TODO: Multiple proposal messages will potentially be generated from this rule. We should probably constrain sending multiple propose msgs! -InsertProposal(p) == - \E v \in ValidValues: - LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN - LET newMsg == - IF p = Proposer(round[p]) /\ step[p] = "PROPOSE" - THEN {[type |-> "PROPOSAL", src |-> p, round |-> round[p], - proposal |-> proposal, validRound |-> validRound[p]]} - ELSE {} - IN - msgsPropose' = [msgsPropose EXCEPT ![round[p]] = - msgsPropose[round[p]] \cup newMsg] - /\ UNCHANGED <> - - - \* lines 34-35 -UponQuorumOfPrevotesAny(p) == - /\ step[p] = "PREVOTE" \* line 34 and 61 - /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! - /\ LET newMsg == [type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> nil] IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] - /\ UNCHANGED <> - - -\* lines 36-46 -UponProposalInPrevoteOrCommitAndPrevote(p) == - \E v \in ValidValues, vr \in Rounds \cup {-1}: - /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 - /\ ([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr]) \in msgsPropose[round[p]] \* line 36 - /\ LET PV == { m \in msgsPrevote[round[p]]: m.is = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 36 - /\ lockedValue' = - IF step[p] = "PREVOTE" - THEN [lockedValue EXCEPT ![p] = v] \* line 38 - ELSE lockedValue \* else of line 37 - /\ lockedRound' = - IF step[p] = "PREVOTE" - THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 - ELSE lockedRound \* else of line 37 - /\ LET newMsgs == - IF step[p] = "PREVOTE" - THEN {[type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]} \* line 40 - ELSE {} - IN \* else of line 37 - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup newMsgs] \* line 40, or else of 37 - /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 - /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 - /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 - /\ UNCHANGED <> - - -\* lines 11-21 -StartRound(p, r) == - /\ round' = [round EXCEPT ![p] = r] - /\ step' = [step EXCEPT ![p] = "PROPOSE"] - - -\* lines 47-48 -UponQuorumOfPrecommitsAny(p) == - /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 - /\ round[p] + 1 \in Rounds - /\ StartRound(p, round[p] + 1) - /\ UNCHANGED <> - - -\* lines 49-54 -UponProposalInPrecommitNoDecision(p) == - /\ decision[p] = nil \* line 49 - /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: - /\ ([type |-> "PROPOSAL", src |-> Proposer(r), - round |-> r, proposal |-> v, validRound |-> vr]) \in msgsPropose[r] \* line 49 - /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 49 - /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 - /\ UNCHANGED <> - - - -\** TODO: With this rule we are generating duplicate messages -InsertFaultyPrevoteMessage == - \E msg \in FaultyMessages: msg.type = "PREVOTE" - /\ msgsPrevote' = [msgsPrevote EXCEPT ![msg.round] = msgsPrevote[msg.round] \cup {msg}] - /\ UNCHANGED <> - -InsertFaultyPrecommitMessage == - \E msg \in FaultyMessages: msg.type = "PRECOMMIT" - /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![msg.round] = msgsPrecommit[msg.round] \cup {msg}] - /\ UNCHANGED <> - -InsertFaultyProposalMessage == - \E srcA \in Faulty, r \in Rounds, idV \in Values: - LET newMsg == [type |-> "PROPOSAL", src |-> srcA, round |-> r, id |-> idV] IN - msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] - /\ UNCHANGED <> - - -Next == - \/ \E p \in Procs: - \/ UponProposalInPropose(p) - \/ UponProposalInProposeAndPrevote(p) - \/ InsertProposal(p) - \/ UponQuorumOfPrevotesAny(p) - \/ UponQuorumOfPrecommitsAny(p) - \/ InsertFaultyPrevoteMessage - \/ InsertFaultyPrecommitMessage - \/ UponProposalInPrecommitNoDecision(p) - \/ InsertFaultyProposalMessage - - \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds - \*\/ UNCHANGED vars - -\* simple reachability properties to make sure that the algorithm is doing anything useful -NoPrevote == \A p \in Procs: step[p] /= "PREVOTE" - -NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" - -NoHigherRounds == \A p \in Procs: round[p] < 1 - -NoDecision == \A p \in Procs: decision[p] = nil - - -============================================================================= - - - diff --git a/spec/fork-cases/TendermintAccountabilityApa.tla b/spec/fork-cases/TendermintAccountabilityApa.tla deleted file mode 100644 index a1b01be..0000000 --- a/spec/fork-cases/TendermintAccountabilityApa.tla +++ /dev/null @@ -1,296 +0,0 @@ ------------------------------ MODULE TendermintAccountabilityApa ----------------------------- -(* - A TLA+ specification of subset of Tendermint consensus needed to formalize fork accountability - protocol. - - This is the version for compatibility with Apalache. - *) - -EXTENDS Integers, FiniteSets - -CONSTANTS - PropFun \* the proposer function - -N == 4 \* the total number of processes: correct and faulty -T == 1 \* an upper bound on the number of Byzantine processes -F == 2 \* the number of Byzantine processes -NFaultyMessages == 8 \* the number of injected faulty messages -Corr == 1..N-F -Faulty == N-F+1..N -AllCorr == 1..N -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {"0", "1"} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {"2"} \* e.g., sent by a Byzantine process -Values == ValidValues \cup InvalidValues \* all values -NilRound == -1 -NilValue == "None" - -\* these are two thresholds that are used in the algorithm -THRESHOLD1 == T + 1 -THRESHOLD2 == 2 * T + 1 - -(* APALACHE *) -a <: b == a - -MT == [type |-> STRING, src |-> Int, round |-> Int, - proposal |-> STRING, validRound |-> Int, id |-> STRING] - -AsMsg(m) == m <: MT -SetOfMsgs(S) == S <: {MT} -EmptyMsgSet == SetOfMsgs({}) - -ConstInit == - \*StartId \in 1..N - \* the proposer is arbitrary -- ok for safety - PropFun \in [Rounds -> AllCorr] - -(* END-OF-APALACHE *) - - -\* these variables are exactly as in the pseudo-code -VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound - -\* book-keeping variables -VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsReceived \* set of received messages a process acted on (that triggered some rule), a function p \in Corr -> set of messages - -VARIABLES nfaultsInjected \* the number of faulty messages injected in the system - -\* this is needed for UNCHANGED -vars == <> - -\* A function which gives the proposer for a given round at a given height. -\* Here we use round robin. As Corr and Faulty are assigned non-deterministically, -\* it does not really matter who starts first. -\*Proposer(rd) == 1 + ((StartId + rd) % N) -Proposer(rd) == PropFun[rd] - -Id(v) == v - -IsValid(v) == v \in ValidValues - - -\* here we start with StartRound(0) -Init == - /\ round = [p \in Corr |-> 0] - /\ step = [p \in Corr |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? - /\ decision = [p \in Corr |-> NilValue] - /\ lockedValue = [p \in Corr |-> NilValue] - /\ lockedRound = [p \in Corr |-> NilRound] - /\ validValue = [p \in Corr |-> NilValue] - /\ validRound = [p \in Corr |-> NilRound] - /\ msgsPrevote = [rd \in Rounds |-> EmptyMsgSet] - /\ msgsPrecommit = [rd \in Rounds |-> EmptyMsgSet] - /\ msgsPropose = [rd \in Rounds |-> EmptyMsgSet] - /\ msgsReceived = [p \in Corr |-> EmptyMsgSet] - /\ nfaultsInjected = 0 - -FaultyMessages == \* the messages that can be sent by the faulty processes - (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, - round: Rounds, proposal: Values, validRound: Rounds \cup {NilRound}])) - \cup - (SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values])) - \cup - (SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values])) - - -\* lines 22-27 -UponProposalInPropose(p) == - \E v \in Values: - /\ step[p] = "PROPOSE" \* line 22 - /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> NilRound])) \in msgsPropose[round[p]] \* line 22 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) IN \* line 23 - LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) - IN \* lines 24-26 - /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - - -\* lines 28-33 -UponProposalInProposeAndPrevote(p) == - \E v \in Values, vr \in Rounds: - /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part - /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr])) \in msgsPropose[round[p]] \* line 28 - /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 28 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 - LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) - IN \* lines 30-32 - /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -InsertProposal(p) == - LET r == round[p] IN - /\ p = Proposer(round[p]) - /\ step[p] = "PROPOSE" - \* if the proposer is sending a proposal, then there are no other proposals - \* by the correct processes for the same round - /\ \A m \in msgsPropose[r]: m.src \in Faulty - /\ \E v \in ValidValues: - LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN - LET newMsg == - AsMsg([type |-> "PROPOSAL", src |-> p, round |-> r, - proposal |-> proposal, validRound |-> validRound[p]]) - IN - - \* a correct proposer never sends two proposals - - /\ msgsPropose' = [msgsPropose EXCEPT ![r] = - msgsPropose[r] \cup {newMsg}] - /\ UNCHANGED <> - - - \* lines 34-35 + lines 61-64 -UponQuorumOfPrevotesAny(p) == - /\ step[p] = "PREVOTE" \* line 34 and 61 - /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 TODO: Note that multiple messages from the same (faulty) process will trigger this rule! - /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN - /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] - /\ UNCHANGED <> - - -\* lines 36-46 -UponProposalInPrevoteOrCommitAndPrevote(p) == - \E v \in ValidValues, vr \in Rounds \cup {NilRound}: - /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 - /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr])) \in msgsPropose[round[p]] \* line 36 - /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 36 - /\ lockedValue' = - IF step[p] = "PREVOTE" - THEN [lockedValue EXCEPT ![p] = v] \* line 38 - ELSE lockedValue \* else of line 37 - /\ lockedRound' = - IF step[p] = "PREVOTE" - THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 - ELSE lockedRound \* else of line 37 - /\ IF step[p] = "PREVOTE" - THEN - LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]) - IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup {newMsg}] \* line 40, or else of 37 - ELSE UNCHANGED msgsPrecommit \* line 40 - /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 - /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 - /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 - /\ UNCHANGED <> - - -\* lines 11-21 -StartRound(p, r) == - /\ round' = [round EXCEPT ![p] = r] - /\ step' = [step EXCEPT ![p] = "PROPOSE"] - - -\* lines 47-48 -UponQuorumOfPrecommitsAny(p) == - /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 - /\ round[p] + 1 \in Rounds - /\ StartRound(p, round[p] + 1) - /\ UNCHANGED <> - - -\* lines 49-54 -UponProposalInPrecommitNoDecision(p) == - /\ decision[p] = NilValue \* line 49 - /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {NilRound}: - /\ (AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), - round |-> r, proposal |-> v, validRound |-> vr])) \in msgsPropose[r] \* line 49 - /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 49 - /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 - /\ UNCHANGED <> - -InsertFaultyPrevoteMessage == - \E src \in Faulty: - \E r \in Rounds: - \E id \in Values: - LET msg == AsMsg([type |-> "PREVOTE", src |-> src, round |-> r, id |-> id]) - IN - /\ msgsPrevote' = [msgsPrevote EXCEPT ![r] = msgsPrevote[r] \cup {msg}] - /\ UNCHANGED <> - -InsertFaultyPrecommitMessage == - \E src \in Faulty: - \E r \in Rounds: - \E id \in Values: - LET msg == AsMsg([type |-> "PRECOMMIT", src |-> src, round |-> r, id |-> id]) - IN - /\ msgsPrecommit' = [msgsPrecommit EXCEPT ![r] = msgsPrecommit[r] \cup {msg}] - /\ UNCHANGED <> - -InsertFaultyProposalMessage == - \E srcA \in Faulty, r \in Rounds, vr \in Rounds \cup {NilRound}, idV \in Values: - LET newMsg == AsMsg([type |-> "PROPOSAL", - src |-> srcA, proposal |-> idV, round |-> r, validRound |-> vr]) IN - /\ msgsPropose' = [msgsPropose EXCEPT ![r] = msgsPropose[r] \cup {newMsg}] - /\ UNCHANGED <> - -Next == - IF nfaultsInjected < NFaultyMessages - THEN /\ nfaultsInjected' = nfaultsInjected + 1 - /\ \/ InsertFaultyPrevoteMessage - \/ InsertFaultyPrecommitMessage - \/ InsertFaultyProposalMessage - ELSE - /\ UNCHANGED nfaultsInjected - /\ \E p \in Corr: - \/ UponProposalInPropose(p) - \/ UponProposalInProposeAndPrevote(p) - \/ InsertProposal(p) - \/ UponQuorumOfPrevotesAny(p) - \/ UponProposalInPrevoteOrCommitAndPrevote(p) - \/ UponQuorumOfPrecommitsAny(p) - \/ UponProposalInPrecommitNoDecision(p) - - \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds - \*\/ UNCHANGED vars - -\* simple reachability properties to make sure that the algorithm is doing anything useful -NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" - -NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" - -NoValidPrecommit == - \A r \in Rounds: - \A m \in msgsPrecommit[r]: - m.id = NilValue \/ m.src \in Faulty - -NoHigherRounds == \A p \in Corr: round[p] < 1 - -NoDecision == \A p \in Corr: decision[p] = NilValue - -Agreement == \A p, q \in Corr: - \/ decision[p] = NilValue - \/ decision[q] = NilValue - \/ decision[p] = decision[q] - - -============================================================================= - - - diff --git a/spec/fork-cases/TendermintAccountabilityApa2.tla b/spec/fork-cases/TendermintAccountabilityApa2.tla deleted file mode 100644 index 62dc199..0000000 --- a/spec/fork-cases/TendermintAccountabilityApa2.tla +++ /dev/null @@ -1,620 +0,0 @@ ------------------------------ MODULE TendermintAccountabilityApa2 ----------------------------- -(* - A TLA+ specification of subset of Tendermint consensus needed to formalize - fork accountability protocol. In this version, the faults are injected right - in the initial states. - - This is the version for compatibility with Apalache. - - Zarko Milosevic, Igor Konnov, 2019-2020. - *) - -EXTENDS Integers, FiniteSets - -CONSTANTS PropFun \* the proposer function - -N == 4 \* the total number of processes: correct and faulty -T == 1 \* an upper bound on the number of Byzantine processes -F == 1 \* the number of Byzantine processes -NFaultyProposals == 0 \* the number of injected faulty PROPOSE messages -NFaultyPrevotes == 6 \* the number of injected faulty PREVOTE messages -NFaultyPrecommits == 6 \* the number of injected faulty PRECOMMIT messages -Corr == {"c1", "c2", "c3"} \* 1..N-F -Faulty == {"f1"} \* N-F+1..N -AllProcs == Corr \cup Faulty -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {"v0", "v1"} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {"v2"} \* e.g., sent by a Byzantine process -Values == ValidValues \cup InvalidValues \* all values -NilRound == -1 -NilValue == "None" - -\* these are two thresholds that are used in the algorithm -THRESHOLD1 == T + 1 -THRESHOLD2 == 2 * T + 1 - -(* APALACHE *) -a <: b == a - -MT == [type |-> STRING, src |-> STRING, round |-> Int, - proposal |-> STRING, validRound |-> Int, id |-> STRING] - -AsMsg(m) == m <: MT -SetOfMsgs(S) == S <: {MT} -EmptyMsgSet == SetOfMsgs({}) - -\* the proposer is arbitrary -- works for safety -ConstInit == PropFun \in [Rounds -> AllProcs] -(* END-OF-APALACHE *) - - -\* these variables are exactly as in the pseudo-code -VARIABLES round, step, decision, lockedValue, lockedRound, validValue, validRound - -\* book-keeping variables - -VARIABLES msgsPropose, - \* PROPOSE messages broadcasted in the system, a function Rounds -> set of messages - msgsPrevote, - \* PREVOTE messages broadcasted in the system, a function Rounds -> set of messages - msgsPrecommit, - \* PRECOMMIT messages broadcasted in the system, a function Rounds -> set of messages - evidence - \* at every step, evidence contains the messages that were used by the active process - -\* this is needed for UNCHANGED -vars == <> - -\* A function which gives the proposer for a given round at a given height. -Proposer(rd) == PropFun[rd] - -\* A value hash. The identity is a perfect hash, except it does not shrink the set. -Id(v) == v - -\* The validity predicate -IsValid(v) == v \in ValidValues - -\* Given a set of allowed messages Msgs, this operator produces a function from -\* rounds to sets of messages. -\* Importantly, there will be exactly k messages in the image of msgFun. -\* We use this action to produce k faults in an initial state. -ProduceFaults(msgFun, From, k) == - \E f \in [1..k -> From]: - msgFun = [r \in Rounds |-> {m \in {f[i]: i \in 1..k}: m.round = r}] - -\* here we start with StartRound(0) -Init == - /\ round = [p \in Corr |-> 0] - /\ step = [p \in Corr |-> "PROPOSE"] \* Q: where we define set of possible steps process can be in? - /\ decision = [p \in Corr |-> NilValue] - /\ lockedValue = [p \in Corr |-> NilValue] - /\ lockedRound = [p \in Corr |-> NilRound] - /\ validValue = [p \in Corr |-> NilValue] - /\ validRound = [p \in Corr |-> NilRound] - /\ ProduceFaults(msgsPrevote', - SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]), - NFaultyPrevotes) - /\ ProduceFaults(msgsPrecommit', - SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]), - NFaultyPrecommits) - /\ ProduceFaults(msgsPropose', - SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: Rounds, - proposal: Values, validRound: Rounds \cup {NilRound}]), - NFaultyProposals) - /\ evidence = EmptyMsgSet - -FaultyMessages == \* the messages that can be sent by the faulty processes - (SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, - round: Rounds, proposal: Values, validRound: Rounds \cup {NilRound}])) - \cup - (SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values])) - \cup - (SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values])) - - -\* lines 22-27 -UponProposalInPropose(p) == - \E v \in Values: - /\ step[p] = "PROPOSE" \* line 22 - /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> NilRound]) IN - /\ msg \in msgsPropose[round[p]] \* line 22 - /\ evidence' = {msg} - /\ LET isGood == IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v) IN \* line 23 - LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) - IN \* lines 24-26 - /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -\* lines 28-33 -UponProposalInProposeAndPrevote(p) == - \E v \in Values, vr \in Rounds: - /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part - /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr]) IN - /\ msg \in msgsPropose[round[p]] \* line 28 - /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN - /\ Cardinality(PV) >= THRESHOLD2 \* line 28 - /\ evidence' = PV \union {msg} - /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 - LET newMsg == AsMsg([type |-> "PREVOTE", src |-> p, - round |-> round[p], id |-> IF isGood THEN Id(v) ELSE NilValue]) - IN \* lines 30-32 - /\ msgsPrevote' = [msgsPrevote EXCEPT ![round[p]] = - msgsPrevote[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -InsertProposal(p) == - LET r == round[p] IN - /\ p = Proposer(r) - /\ step[p] = "PROPOSE" - \* if the proposer is sending a proposal, then there are no other proposals - \* by the correct processes for the same round - /\ \A m \in msgsPropose[r]: m.src \in Faulty - /\ \E v \in ValidValues: - LET proposal == IF validValue[p] /= NilValue THEN validValue[p] ELSE v IN - LET newMsg == - AsMsg([type |-> "PROPOSAL", src |-> p, round |-> r, - proposal |-> proposal, validRound |-> validRound[p]]) - IN - \* a correct proposer never sends two proposals - msgsPropose' = [msgsPropose EXCEPT ![r] = - msgsPropose[r] \cup {newMsg}] - /\ evidence' = EmptyMsgSet - /\ UNCHANGED <> - - - \* lines 34-35 + lines 61-64 -UponQuorumOfPrevotesAny(p) == - /\ step[p] = "PREVOTE" \* line 34 and 61 - /\ Cardinality(msgsPrevote[round[p]]) >= THRESHOLD2 \* line 34 - \* TODO: Note that multiple messages from the same (faulty) process may trigger this rule! - /\ evidence' = msgsPrevote[round[p]] - /\ LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> NilValue]) IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup {newMsg}] - /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] - /\ UNCHANGED <> - - -\* lines 36-46 -UponProposalInPrevoteOrCommitAndPrevote(p) == - \E v \in ValidValues, vr \in Rounds \cup {NilRound}: - /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 - /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(round[p]), - round |-> round[p], proposal |-> v, validRound |-> vr]) IN - /\ msg \in msgsPropose[round[p]] \* line 36 - /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN - /\ Cardinality(PV) >= THRESHOLD2 \* line 36 - /\ evidence' = PV \union {msg} - /\ lockedValue' = - IF step[p] = "PREVOTE" - THEN [lockedValue EXCEPT ![p] = v] \* line 38 - ELSE lockedValue \* else of line 37 - /\ lockedRound' = - IF step[p] = "PREVOTE" - THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 - ELSE lockedRound \* else of line 37 - /\ IF step[p] = "PREVOTE" - THEN - LET newMsg == AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> round[p], id |-> Id(v)]) - IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![round[p]] = - msgsPrecommit[round[p]] \cup {newMsg}] \* line 40, or else of 37 - ELSE UNCHANGED msgsPrecommit \* line 40 - /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 - /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 - /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 - /\ UNCHANGED <> - - -\* lines 11-21 -StartRound(p, r) == - /\ step[p] /= "DECIDED" \* when decided, do not switch the round - /\ round' = [round EXCEPT ![p] = r] - /\ step' = [step EXCEPT ![p] = "PROPOSE"] - -\* lines 47-48 -UponQuorumOfPrecommitsAny(p) == - /\ Cardinality(msgsPrecommit[round[p]]) >= THRESHOLD2 \* line 47 - /\ evidence' = msgsPrecommit[round[p]] - /\ round[p] + 1 \in Rounds - /\ StartRound(p, round[p] + 1) - /\ UNCHANGED <> - -\* lines 49-54 -UponProposalInPrecommitNoDecision(p) == - /\ decision[p] = NilValue \* line 49 - \* TODO: a catch-up is going on here, not exactly Algorithm 1 - /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {NilRound}: - /\ LET msg == AsMsg([type |-> "PROPOSAL", src |-> Proposer(r), - round |-> r, proposal |-> v, validRound |-> vr]) IN - /\ msg \in msgsPropose[r] \* line 49 - /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN - /\ Cardinality(PV) >= THRESHOLD2 \* line 49 - /\ evidence' = PV \union {msg} - /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51 - \* The original algorithm does not have the 'DECIDED' step, but it increments the height. - \* Thus, we introduced 'DECIDED' here to prevent the process from changing its decision. - /\ step' = [step EXCEPT ![p] = "DECIDED"] - /\ UNCHANGED <> - - -Next == - /\ \E p \in Corr: - \/ UponProposalInPropose(p) - \/ UponProposalInProposeAndPrevote(p) - \/ InsertProposal(p) - \/ UponQuorumOfPrevotesAny(p) - \/ UponProposalInPrevoteOrCommitAndPrevote(p) - \/ UponQuorumOfPrecommitsAny(p) - \/ UponProposalInPrecommitNoDecision(p) - - \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds - \*\/ UNCHANGED vars - -(******************************** INVARIANTS *********************************) -(* first, we define the sets of all potential messages *) -AllProposals == - [type: {"PROPOSAL"}, - src: AllProcs, - round: Rounds, - proposal: Values \cup {NilValue}, - validRound: Rounds \cup {NilRound}] <: {MT} - -AllPrevotes == - [type: {"PREVOTE"}, - src: AllProcs, - round: Rounds, - id: Values \cup {NilValue}] <: {MT} - -AllPrecommits == - [type: {"PRECOMMIT"}, - src: AllProcs, - round: Rounds, - id: Values \cup {NilValue}] <: {MT} - -BenignRoundsInMessages(msgfun) == - \* the message function never contains a message for a wrong round - \A r \in Rounds: - \A m \in msgfun[r]: - r = m.round - -(* the standard type invariant -- importantly, it is inductive *) -TypeOK == - /\ round \in [Corr -> Rounds] - /\ step \in [Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" }] - /\ decision \in [Corr -> ValidValues \cup {NilValue}] - /\ lockedValue \in [Corr -> ValidValues \cup {NilValue}] - /\ lockedRound \in [Corr -> Rounds \cup {NilRound}] - /\ validValue \in [Corr -> ValidValues \cup {NilValue}] - /\ validRound \in [Corr -> Rounds \cup {NilRound}] - /\ msgsPropose \in [Rounds -> SUBSET AllProposals] - /\ BenignRoundsInMessages(msgsPropose) - /\ msgsPrevote \in [Rounds -> SUBSET AllPrevotes] - /\ BenignRoundsInMessages(msgsPrevote) - /\ msgsPrecommit \in [Rounds -> SUBSET AllPrecommits] - /\ BenignRoundsInMessages(msgsPrecommit) - /\ evidence \in SUBSET (AllProposals \union AllPrevotes \union AllPrecommits) - -NoFutureMessagesForLargerRounds(p) == - \* a correct process does not send messages for the future rounds - \A r \in { rr \in Rounds: rr > round[p] }: - /\ \A m \in msgsPropose[r]: m.src /= p - /\ \A m \in msgsPrevote[r]: m.src /= p - /\ \A m \in msgsPrecommit[r]: m.src /= p - -NoFutureMessagesForCurrentRound(p) == - \* a correct process does not send messages in the future - LET r == round[p] IN - /\ Proposer(r) = p \/ \A m \in msgsPropose[r]: m.src /= p - /\ \/ step[p] \in {"PREVOTE", "PRECOMMIT", "DECIDED"} - \/ \A m \in msgsPrevote[r]: m.src /= p - /\ \/ step[p] \in {"PRECOMMIT", "DECIDED"} - \/ \A m \in msgsPrecommit[r]: m.src /= p - -\* the correct processes never send future messages -AllNoFutureMessagesSent == - \A p \in Corr: - /\ NoFutureMessagesForCurrentRound(p) - /\ NoFutureMessagesForLargerRounds(p) - -\* a correct process in the PREVOTE state has sent a PREVOTE message -IfInPrevoteThenSentPrevote(p) == - step[p] = "PREVOTE" => - \E m \in msgsPrevote[round[p]]: - /\ m.id \in ValidValues \cup { NilValue } - /\ m.src = p - -AllIfInPrevoteThenSentPrevote == - \A p \in Corr: IfInPrevoteThenSentPrevote(p) - -\* a correct process in the PRECOMMIT state has sent a PRECOMMIT message -IfInPrecommitThenSentPrecommit(p) == - step[p] = "PRECOMMIT" => - \E m \in msgsPrecommit[round[p]]: - /\ m.id \in ValidValues \cup { NilValue } - /\ m.src = p - -AllIfInPrecommitThenSentPrecommit == - \A p \in Corr: IfInPrecommitThenSentPrecommit(p) - -\* a process in the PRECOMMIT state has sent a PRECOMMIT message -IfInDecidedThenValidDecision(p) == - step[p] = "DECIDED" <=> decision[p] \in ValidValues - -AllIfInDecidedThenValidDecision == - \A p \in Corr: IfInDecidedThenValidDecision(p) - -\* a decided process should have received a proposal on its decision -IfInDecidedThenReceivedProposal(p) == - step[p] = "DECIDED" => - \E r \in Rounds: \* r is not necessarily round[p] - /\ \E m \in msgsPropose[r]: - /\ m.src = Proposer(r) - /\ m.proposal = decision[p] - \* not inductive: /\ m.src \in Corr => (m.validRound <= r) - -AllIfInDecidedThenReceivedProposal == - \A p \in Corr: IfInDecidedThenReceivedProposal(p) - -\* a decided process has received two-thirds of precommit messages -IfInDecidedThenReceivedTwoThirds(p) == - step[p] = "DECIDED" => - \E r \in Rounds: - LET PV == { m \in msgsPrecommit[r]: m.id = decision[p] } IN - Cardinality(PV) >= THRESHOLD2 - -AllIfInDecidedThenReceivedTwoThirds == - \A p \in Corr: IfInDecidedThenReceivedTwoThirds(p) - -\* for a round r, there is proposal by the round proposer for a valid round vr -ProposalInRound(r, proposedVal, vr) == - \E m \in msgsPropose[r]: - /\ m.src = Proposer(r) - /\ m.proposal = proposedVal - /\ m.validRound = vr - -TwoThirdsPrevotes(vr, v) == - LET PV == { mm \in msgsPrevote[vr]: mm.id = v } IN - Cardinality(PV) >= THRESHOLD2 - -\* if a process sends a PREVOTE, then there are three possibilities: -\* 1) the process is faulty, 2) the PREVOTE cotains Nil, -\* 3) there is a proposal in an earlier (valid) round and two thirds of PREVOTES -IfSentPrevoteThenReceivedProposalOrTwoThirds(r) == - \A mpv \in msgsPrevote[r]: - \/ mpv.src \in Faulty - \* lockedRound and lockedValue is beyond my comprehension - \/ mpv.id = NilValue - \//\ mpv.src \in Corr - /\ mpv.id /= NilValue - /\ \/ ProposalInRound(r, mpv.id, NilRound) - \/ \E vr \in { rr \in Rounds: rr < r }: - /\ ProposalInRound(r, mpv.id, vr) - /\ TwoThirdsPrevotes(vr, mpv.id) - -AllIfSentPrevoteThenReceivedProposalOrTwoThirds == - \A r \in Rounds: - IfSentPrevoteThenReceivedProposalOrTwoThirds(r) - -\* if a correct process has sent a PRECOMMIT, then there are two thirds, -\* either on a valid value, or a nil value -IfSentPrecommitThenReceivedTwoThirds == - \A r \in Rounds: - \A mpc \in msgsPrecommit[r]: - \/ mpc.src \in Faulty - \/ /\ mpc.src \in Corr - /\ \/ /\ mpc.id \in ValidValues - /\ LET PV == { m \in msgsPrevote[r]: m.id = mpc.id } IN - Cardinality(PV) >= THRESHOLD2 - \/ /\ mpc.id = NilValue - /\ Cardinality(msgsPrevote[r]) >= THRESHOLD2 - -\* there is a locked round if a only if there is a locked value -LockedRoundIffLockedValue(p) == - (lockedRound[p] = NilRound) <=> (lockedValue[p] = NilValue) - -AllLockedRoundIffLockedValue == - \A p \in Corr: LockedRoundIffLockedValue(p) - -\* when a process locked a round, it should have seen a precommit on a locked value -IfLockedRoundThenSentCommit(p) == - lockedRound[p] /= NilRound - => \E r \in { rr \in Rounds: rr <= round[p] }: - \E m \in msgsPrecommit[r]: - m.src = p /\ m.id = lockedValue[p] - -AllIfLockedRoundThenSentCommit == - \A p \in Corr: IfLockedRoundThenSentCommit(p) - -\* a process always locks the latest round, for which it has sent a PRECOMMIT -LatestPrecommitHasLockedRound(p) == - LET pPrecommits == {mm \in UNION { msgsPrecommit[r]: r \in Rounds }: mm.src = p } IN - pPrecommits /= {} <: {MT} - => LET latest == - CHOOSE m \in pPrecommits: - \A m2 \in pPrecommits: - m2.round < m.round - IN - /\ lockedRound[p] = latest.round - /\ lockedValue[p] = latest.id - -AllLatestPrecommitHasLockedRound == - \A p \in Corr: - LatestPrecommitHasLockedRound(p) - -\* Every correct process sends only one value or NilValue. -\* This test has quantifier alternation -- a threat to all decision procedures. -\* Luckily, the sets Corr and ValidValues are small. -NoEquivocationByCorrect(r, msgs) == - \A p \in Corr: - \E v \in ValidValues \cup {NilValue}: - \A m \in msgs[r]: - \/ m.src /= p - \/ m.id = v - -\* a proposer nevers sends two values -ProposalsByProposer(r, msgs) == - \* if the proposer is not faulty, it sends only one value - \E v \in ValidValues: - \A m \in msgs[r]: - \/ m.src \in Faulty - \/ m.src = Proposer(r) /\ m.proposal = v - -AllNoEquivocationByCorrect == - \A r \in Rounds: - /\ ProposalsByProposer(r, msgsPropose) - /\ NoEquivocationByCorrect(r, msgsPrevote) - /\ NoEquivocationByCorrect(r, msgsPrecommit) - -\* construct the set of the message senders -Senders(M) == { m.src: m \in M } - -\* The final piece by Josef Widder: -\* if T + 1 processes precommit on the same value in a round, -\* then in the future rounds there are less than 2T + 1 prevotes for another value -PrecommitsLockValue == - \A r \in Rounds: - \A v \in ValidValues \union {NilValue}: - \/ Cardinality(Senders({m \in msgsPrecommit[r]: m.id = v})) < THRESHOLD1 - \/ \A fr \in { rr \in Rounds: rr > r }: \* future rounds - \A w \in (Values \union {NilValue}) \ {v}: - Cardinality(Senders({m \in msgsPrevote[fr]: m.id = w})) < THRESHOLD2 - -Inv == - /\ AllNoFutureMessagesSent - /\ AllIfInPrevoteThenSentPrevote - /\ AllIfInPrecommitThenSentPrecommit - /\ AllIfInDecidedThenReceivedProposal - /\ AllIfInDecidedThenReceivedTwoThirds - /\ AllIfInDecidedThenValidDecision - /\ AllLockedRoundIffLockedValue - /\ AllIfLockedRoundThenSentCommit - /\ AllLatestPrecommitHasLockedRound - \* /\ AllIfValidRoundThenTwoThirds \* no need for safety - \*/\ AllIfValidRoundThenProposal \* no need for safety - /\ AllIfSentPrevoteThenReceivedProposalOrTwoThirds - /\ IfSentPrecommitThenReceivedTwoThirds - /\ AllNoEquivocationByCorrect - /\ PrecommitsLockValue - -TypedInv == TypeOK /\ Inv - -\* UNUSED FOR SAFETY -ValidRoundNotSmallerThanLockedRound(p) == - validRound[p] >= lockedRound[p] - -\* UNUSED FOR SAFETY -ValidRoundIffValidValue(p) == - (validRound[p] = NilRound) <=> (validValue[p] = NilValue) - -\* UNUSED FOR SAFETY -AllValidRoundIffValidValue == - \A p \in Corr: ValidRoundIffValidValue(p) - -\* if validRound is defined, then there are two-thirds of PREVOTEs -IfValidRoundThenTwoThirds(p) == - \/ validRound[p] = NilRound - \/ LET PV == { m \in msgsPrevote[validRound[p]]: m.id = validValue[p] } IN - Cardinality(PV) >= THRESHOLD2 - -\* UNUSED FOR SAFETY -AllIfValidRoundThenTwoThirds == - \A p \in Corr: IfValidRoundThenTwoThirds(p) - -\* a valid round can be only set to a valid value that was proposed earlier -IfValidRoundThenProposal(p) == - \/ validRound[p] = NilRound - \/ \E m \in msgsPropose[validRound[p]]: - m.proposal = validValue[p] - -\* UNUSED FOR SAFETY -AllIfValidRoundThenProposal == - \A p \in Corr: IfValidRoundThenProposal(p) - -(**************************** FORK ACCOUNTABILITY ***************************) -Equivocation == - \E r \in Rounds: - \/ \E m1, m2 \in msgsPropose[r]: - m1 /= m2 /\ m1.src = m2.src - \/ \E m1, m2 \in msgsPrevote[r]: - m1 /= m2 /\ m1.src = m2.src - \/ \E m1, m2 \in msgsPrecommit[r]: - m1 /= m2 /\ m1.src = m2.src - -InitNoEquivocation == - Init /\ ~Equivocation - -\* amnesic behavior by a process p -Amnesia(p) == - \E r1, r2 \in Rounds: - /\ r1 < r2 - /\ \E v1, v2 \in ValidValues: - /\ v1 /= v2 - /\ AsMsg([type |-> "PRECOMMIT", src |-> p, round |-> r1, id |-> Id(v1)]) \in msgsPrecommit[r1] - /\ AsMsg([type |-> "PREVOTE", src |-> p, round |-> r2, id |-> Id(v2)]) \in msgsPrecommit[r2] - /\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd <= r2 }: - LET prevotes == - { m \in msgsPrevote[r]: - m.type = "PREVOTE" /\ m.round = r /\ m.id = Id(v2) } - IN - Cardinality(prevotes) < THRESHOLD2 - -(******************************** PROPERTIES ***************************************) -\* simple reachability properties to see that the spec is progressing -NoPrevote == \A p \in Corr: step[p] /= "PREVOTE" - -NoPrecommit == \A p \in Corr: step[p] /= "PRECOMMIT" - -NoValidPrecommit == - \A r \in Rounds: - \A m \in msgsPrecommit[r]: - m.id = NilValue \/ m.src \in Faulty - -NoHigherRounds == \A p \in Corr: round[p] < 1 - -NoDecision == \A p \in Corr: decision[p] = NilValue - -\* the safety property -- agreement -Agreement == \A p, q \in Corr: - \/ decision[p] = NilValue - \/ decision[q] = NilValue - \/ decision[p] = decision[q] - -Validity == - \A p \in Corr: decision[p] \in ValidValues \union {NilValue} - -\* when agreement is violated, at least one faulty process has amnesia -AgreementOrAmnesia == - Agreement \/ (\E p \in Faulty: Amnesia(p)) - -AgreementNoAmnesia == - Agreement \/ ~(\E p \in Faulty: Amnesia(p)) - -InvAndNoEquivocation == - Inv /\ ~Equivocation - -\* use this predicate for the initial states -TypedInvNoEquivocationNoAmnesia == - TypeOK /\ Inv /\ ~Equivocation /\ (\A p \in Faulty: ~Amnesia(p)) - -\* the invariant to check -AgreementOrEquivocationOrAmnesia == - \/ Agreement - \/ Equivocation - \/ \E p \in Faulty: Amnesia(p) - -============================================================================= - diff --git a/spec/fork-cases/TendermintFork.tla b/spec/fork-cases/TendermintFork.tla deleted file mode 100644 index 87e08a0..0000000 --- a/spec/fork-cases/TendermintFork.tla +++ /dev/null @@ -1,360 +0,0 @@ ------------------------- MODULE TendermintFork -------------------------- -(* - This is a fork of the tendermint-safety/Tendermint that captures various - fork scenarious under the assumption that there are more faults than - the algorithm should be able to handle. - *) - -EXTENDS Integers, FiniteSets - -CONSTANTS - N, \* the total number of processes - T, \* the upper bound on the number of Byzantine processes - F, \* the number of Byzantine processes - Heights, \* the set of consensus instances - Rounds, \* the set of possible rounds - ValidValues, \* the set of values proposed by a correct process or a faulty one - InvalidValues, \* the set of values proposed by a faulty process - PropFun, \* the proposer function - FaultyMessages \* the set of faulty messages that can be sent by the faulty processes - -Procs == 1..N-F -Faulty == N-F+1..N -Values == ValidValues \cup InvalidValues \* all values -nil == -1 - -\* these are two tresholds that are used in the algorithm -THRESHOLD1 == T + 1 -THRESHOLD2 == 2 * T + 1 - -(* Type annotations needed by APALACHE *) -a <: b == a - -MT == [type |-> STRING, src |-> Int, h |-> Int, round |-> Int, - proposal |-> Int, validRound |-> Int, hash |-> Int] -ET == [type |-> STRING, src |-> Int, h |-> Int, round |-> Int, value |-> Int] \* processed events, "for the first time" - -ValueT == Int -RoundT == Int -TimeoutT == <> \* process, height, round -(* APALACHE-END *) - -\* these variables are exactly as in the pseudo-code -VARIABLES h, round, step, decision, lockedValue, lockedRound, validValue, validRound - -\* book-keeping variables -VARIABLES msgsPropose, \* the propose messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrevote, \* the prevote messages broadcasted in the system, a function Heights \X Rounds -> set of messages - msgsPrecommit, \* the precommit messages broadcasted in the system, a function Heights \X Rounds -> set of messages - oldEvents, \* the messages processed once, as expressed by "for the first time" - timeoutPropose, \* a set of proposed timeouts: <> - timeoutPrevote, \* a set of proposed timeouts: <> - timeoutPrecommit \* a set of proposed timeouts: <> - -\* this is needed for UNCHANGED -vars == <> - -\* A function which gives the proposer for a given round at a given height. -\* Here we use round robin. As Procs and Faulty are assigned non-deterministically, -\* it does not really matter who starts first. -Proposer(ht, rd) == PropFun[ht, rd] \*1 + ((ht + rd) % N) - -Id(v) == v - -IsValid(v) == v \in ValidValues - -MixinFaults(ht, rd, type, msgs) == - \* add the messages from the faulty processes, filtered by height, round - msgs \cup {m \in FaultyMessages : m.h = ht /\ m.round = rd /\ m.type = type} - -\* compute the set of initial messages for a height, round, and value -InitProposals(ht, rd, v) == - IF ht = 0 /\ rd = 0 - THEN - LET proposals == - {[type |-> "PROPOSAL", src |-> Proposer(0, 0), - h |-> 0, round |-> 0, proposal |-> v, validRound |-> -1] <: MT} IN - MixinFaults(ht, rd, "PROPOSAL", proposals) - ELSE MixinFaults(ht, rd, "PROPOSAL", {} <: {MT}) - - -\* here we start with StartRound(0) -Init == - /\ h = [p \in Procs |-> 0] - /\ round = [p \in Procs |-> 0] - /\ step = [p \in Procs |-> "PROPOSE"] - /\ decision = [p \in Procs |-> [ht \in Heights |-> nil]] - /\ lockedValue = [p \in Procs |-> nil] - /\ lockedRound = [p \in Procs |-> -1] - /\ validValue = [p \in Procs |-> nil] - /\ validRound = [p \in Procs |-> -1] - /\ \E v \in ValidValues: - msgsPropose = [<> \in Heights \X Rounds |-> InitProposals(ht, rd, v)] - /\ msgsPrevote = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PREVOTE", {} <: {MT})] - /\ msgsPrecommit = [<> \in Heights \X Rounds |-> MixinFaults(ht, rd, "PRECOMMIT", {} <: {MT})] - /\ oldEvents = {} <: {ET} - /\ timeoutPropose = { <> : p \in Procs \ {Proposer(0, 0)}} - /\ timeoutPrevote = {} <: {TimeoutT} \* no PREVOTE timeouts - /\ timeoutPrecommit = {} <: {TimeoutT} \* no PRECOMMIT timeouts - -\* lines 22-27 -UponProposalInPropose(p) == - \E v \in Values: - /\ step[p] = "PROPOSE" \* line 22 - /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], - round |-> round[p], proposal |-> v, validRound |-> -1] <: MT) \in msgsPropose[h[p], round[p]] \* line 22 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] = -1 \/ lockedValue[p] = v) IN \* line 23 - LET newMsgs == ({[type |-> "PREVOTE", src |-> p, h |-> h[p], - round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil] <: MT}) - IN \* lines 24-26 - msgsPrevote' = [msgsPrevote EXCEPT ![h[p], round[p]] = - msgsPrevote[h[p], round[p]] \cup newMsgs] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -\* lines 28-33 -UponProposalInProposeAndPrevote(p) == - \E v \in Values, vr \in Rounds: - /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part - /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], - round |-> round[p], proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], round[p]] \* line 28 - /\ LET PV == { m \in msgsPrevote[h[p], vr]: m.hash = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 28 - /\ LET isGood == IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v) IN \* line 29 - LET newMsgs == ({[type |-> "PREVOTE", src |-> p, h |-> h[p], - round |-> round[p], hash |-> IF isGood THEN Id(v) ELSE nil] <: MT}) - IN \* lines 30-32 - msgsPrevote' = [msgsPrevote EXCEPT ![h[p], round[p]] = - msgsPrevote[h[p], round[p]] \cup newMsgs] - /\ step' = [step EXCEPT ![p] = "PREVOTE"] - /\ UNCHANGED <> - -\* lines 34-35 -UponPrevoteFirstTime(p) == - /\ step[p] = "PREVOTE" \* line 34 - /\ Cardinality(msgsPrevote[h[p], round[p]]) >= THRESHOLD2 \* line 34 - /\ LET event == [type |-> "PREVOTE", src |-> p, h |-> h[p], round |-> round[p], value |-> nil] IN - /\ event \notin oldEvents \* for the first time - /\ oldEvents' = oldEvents \cup {event} \* process it only once - /\ timeoutPrevote' = timeoutPrevote \cup {<>} \* line 35 - /\ UNCHANGED <> - -\* lines 36-46 -UponProposalInPrevoteOrCommitAndPrevote(p) == - \E v \in ValidValues, vr \in Rounds \cup {-1}: - /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36 - /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], round[p]), h |-> h[p], - round |-> round[p], proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], round[p]] \* line 36 - /\ LET event == [type |-> "PREVOTE", src |-> p, h |-> h[p], round |-> round[p], value |-> Id(v)] IN - /\ event \notin oldEvents \* for the first time - /\ oldEvents' = oldEvents \cup {event} \* record that it should not happen again - /\ LET PV == { m \in msgsPrevote[h[p], round[p]]: m.hash = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 36 - /\ lockedValue' = - IF step[p] = "PREVOTE" - THEN [lockedValue EXCEPT ![p] = v] \* line 38 - ELSE lockedValue \* else of line 37 - /\ lockedRound' = - IF step[p] = "PREVOTE" - THEN [lockedRound EXCEPT ![p] = round[p]] \* line 39 - ELSE lockedRound \* else of line 37 - /\ LET newMsgs == - IF step[p] = "PREVOTE" - THEN {[type |-> "PRECOMMIT", src |-> p, h |-> h[p], round |-> round[p], hash |-> Id(v)] <: MT} \* line 40 - ELSE {} <: {MT} - IN \* else of line 37 - msgsPrecommit' = [msgsPrecommit EXCEPT ![h[p], round[p]] = - msgsPrecommit[h[p], round[p]] \cup newMsgs] \* line 40, or else of 37 - /\ step' = IF step[p] = "PREVOTE" THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 41 - /\ validValue' = [validValue EXCEPT ![p] = v] \* line 42 - /\ validRound' = [validRound EXCEPT ![p] = round[p]] \* line 43 - /\ UNCHANGED <> - -\* Apparently, this action is needed to deal with a value proposed by a Byzantine process -\* This action is particularly hard for the model checker -\* lines 44-46 -UponPrevoteNil(p) == - /\ step[p] = "PREVOTE" \* line 44 - /\ LET PV == { m \in msgsPrevote[h[p], round[p]]: m.hash = nil } IN - Cardinality(PV) >= THRESHOLD2 \* line 34 - /\ step' = [step EXCEPT ![p] = "PRECOMMIT"] - /\ LET newMsgs == ({[type |-> "PRECOMMIT", src |-> p, h |-> h[p], - round |-> round[p], hash |-> nil] <: MT}) \* line 45 - IN - msgsPrecommit' = [msgsPrecommit EXCEPT ![h[p], round[p]] = - msgsPrecommit[h[p], round[p]] \cup newMsgs] \* line 45 - /\ UNCHANGED <> - -\* lines 47-48 -UponPrecommitFirstTime(p) == - /\ Cardinality(msgsPrecommit[h[p], round[p]]) >= THRESHOLD2 \* line 47 - /\ LET event == [type |-> "PRECOMMIT", src |-> p, h |-> h[p], round |-> round[p], value |-> nil] IN - /\ event \notin oldEvents \* for the first time - /\ oldEvents' = oldEvents \cup {event} \* process it only once - /\ timeoutPrecommit' = timeoutPrecommit \cup {<>} \* line 48 - /\ UNCHANGED <> - -\* lines 11-21 -StartRound(p, ht, r) == - /\ round' = [round EXCEPT ![p] = r] - /\ step' = [step EXCEPT ![p] = "PROPOSE"] - /\ \E v \in ValidValues: \* lines 14-21 - LET proposal == IF validValue[p] /= nil THEN validValue[p] ELSE v IN - LET newMsgs == - IF p = Proposer(ht, r) - THEN {[type |-> "PROPOSAL", src |-> p, h |-> ht, round |-> r, - proposal |-> proposal, validRound |-> validRound[p]] <: MT} - ELSE {} <: {MT} - IN - msgsPropose' = [msgsPropose EXCEPT ![ht, r] = - msgsPropose[ht, r] \cup newMsgs] \* line 19 - /\ LET newTimeouts == \* line 21 - IF p = Proposer(ht, r) - THEN {} <: {TimeoutT} \* no new timeouts - ELSE { <> } - IN - timeoutPropose' = timeoutPropose \cup newTimeouts - -\* lines 49-54 -UponProposalInPrecommitNoDecision(p) == - /\ h[p] + 1 \in Heights - \* THIS IS NOT PART OF THE ORIGINAL ALGORITHM, - \* BUT A SAFEGUARD TO PREVENT ROUNDS FROM OVERFLOWING - /\ decision[p][h[p]] = nil \* line 49 - /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in Rounds \cup {-1}: - /\ ([type |-> "PROPOSAL", src |-> Proposer(h[p], r), h |-> h[p], - round |-> r, proposal |-> v, validRound |-> vr] <: MT) \in msgsPropose[h[p], r] \* line 49 - /\ LET PV == { m \in msgsPrecommit[h[p], r]: m.hash = Id(v) } IN - Cardinality(PV) >= THRESHOLD2 \* line 49 - /\ decision' = [decision EXCEPT ![p][h[p]] = v] \* update the decision, line 51 - /\ h' = [h EXCEPT ![p] = h[p] + 1] \* line 52 - \* line 53 - /\ lockedRound' = [lockedRound EXCEPT ![p] = -1] - /\ lockedValue' = [lockedValue EXCEPT ![p] = nil] - /\ validRound' = [validRound EXCEPT ![p] = -1] - /\ validValue' = [validValue EXCEPT ![p] = nil] - \* What does it mean to reset the message buffer? Do it for one process only? - /\ StartRound(p, h[p] + 1, 0) - /\ UNCHANGED <> - -\* lines 55-56 -UponCatchupRound(p) == - \E r \in Rounds: - /\ r > round[p] - /\ LET PV == msgsPropose[h[p], r] \cup msgsPrevote[h[p], r] \cup msgsPrecommit[h[p], r] IN - Cardinality(PV) >= THRESHOLD1 \* line 55 - /\ StartRound(p, h[p], r) - /\ UNCHANGED <> - -\* lines 57-60 -OnTimeoutPropose(p) == - \E tm \in timeoutPropose: \* a timeout occurs - /\ tm[1] = p - /\ timeoutPropose' = timeoutPropose \ {tm} \* remove from the future timeouts - /\ LET UpdateNeeded == tm[2] = h[p] /\ tm[3] = round[p] /\ step[p] = "PROPOSE" IN - /\ step' = IF UpdateNeeded THEN [step EXCEPT ![p] = "PREVOTE"] ELSE step \* line 60 - /\ LET newMsgs == - IF UpdateNeeded - THEN {[type |-> "PREVOTE", src |-> tm[1], h |-> tm[2], round |-> tm[3], hash |-> nil] <: MT} \* line 59 - ELSE {} <: {MT} \* else of line 58 - IN \* line 59, or else of 58 - msgsPrevote' = [msgsPrevote EXCEPT ![tm[2], tm[3]] = - msgsPrevote[tm[2], tm[3]] \cup newMsgs] - /\ UNCHANGED <> - -\* lines 61-64 -OnTimeoutPrevote(p) == - \E tm \in timeoutPrevote: \* a timeout occurs - /\ tm[1] = p - /\ timeoutPrevote' = timeoutPrevote \ {tm} \* remove from the future timeouts - /\ LET UpdateNeeded == tm[2] = h[p] /\ tm[3] = round[p] /\ step[p] = "PREVOTE" IN - /\ step' = IF UpdateNeeded THEN [step EXCEPT ![p] = "PRECOMMIT"] ELSE step \* line 64 - /\ LET newMsgs == - IF UpdateNeeded - THEN {[type |-> "PRECOMMIT", src |-> tm[1], h |-> tm[2], round |-> tm[3], hash |-> nil] <: MT} \* line 63 - ELSE {} <: {MT} \* else of line 62 - IN \* line 63, or else of 62 - msgsPrecommit' = [msgsPrecommit EXCEPT ![tm[2], tm[3]] = - msgsPrecommit[tm[2], tm[3]] \cup newMsgs] - /\ UNCHANGED <> - -\* lines 65-67 -OnTimeoutPrecommitOutside(p) == - \E tm \in timeoutPrecommit: \* a timeout occurs - /\ tm[1] = p - /\ (tm[2] /= h[p] \/ tm[3] /= round[p]) \* but we are in another round or height - /\ timeoutPrecommit' = timeoutPrecommit \ {tm} \* remove from the future timeouts - /\ UNCHANGED <> - -\* lines 65-67 -OnTimeoutPrecommitInside(p) == - /\ round[p] + 1 \in Rounds - \* THIS IS NOT PART OF THE ORIGINAL ALGORITHM, - \* BUT A SAFEGUARD TO PREVENT ROUNDS FROM OVERFLOWING - /\ <> \in timeoutPrecommit \* the timeout occurs for the current round and height - /\ timeoutPrecommit' = timeoutPrecommit \ {<>} \* remove from the future timeouts - /\ StartRound(p, h[p], round[p] + 1) - /\ UNCHANGED <> - - -Next == - \/ \E p \in Procs: - \/ UponProposalInPropose(p) - \/ UponProposalInProposeAndPrevote(p) - \/ UponPrevoteFirstTime(p) - \/ UponProposalInPrevoteOrCommitAndPrevote(p) - \/ UponPrevoteNil(p) - \/ UponPrecommitFirstTime(p) - \/ UponProposalInPrecommitNoDecision(p) - \/ UponCatchupRound(p) - \/ OnTimeoutPropose(p) - \/ OnTimeoutPrevote(p) - \/ OnTimeoutPrecommitOutside(p) - \/ OnTimeoutPrecommitInside(p) - \* a safeguard to prevent deadlocks when the algorithm goes to further heights or rounds - \*\/ UNCHANGED vars - -\* safety -Agreement == - \A p, q \in Procs, ht \in Heights: - decision[p][ht] = nil \/ decision[q][ht] = nil \/ decision[p][ht] = decision[q][ht] - -\* simple reachability properties to make sure that the algorithm is doing anything useful -NoDecision == \A p \in Procs, ht \in Heights: decision[p][ht] = nil - -NoPrecommit == \A p \in Procs: step[p] /= "PRECOMMIT" - -NoTwoLockedValues == - \A p, q \in Procs: - h[p] = h[q] => lockedValue[p] = nil \/ lockedValue[q] = nil \/ lockedValue[p] = lockedValue[q] - -NoTwoLockedRounds == - \A p, q \in Procs: - h[p] = h[q] => lockedRound[p] = -1 \/ lockedRound[q] = -1 \/ lockedRound[p] = lockedRound[q] - - -============================================================================= -\* Modification History -\* Last modified Mon Nov 04 12:07:03 CET 2019 by igor -\* Created Fri Mar 15 10:30:17 CET 2019 by igor diff --git a/spec/fork-cases/TendermintForkn_n4t1f2.tla b/spec/fork-cases/TendermintForkn_n4t1f2.tla deleted file mode 100644 index 5b43d9b..0000000 --- a/spec/fork-cases/TendermintForkn_n4t1f2.tla +++ /dev/null @@ -1,40 +0,0 @@ ------------------------ MODULE TendermintForkn_n4t1f2 ----------------------- -EXTENDS Integers - -NInjected == 1 \* the number of injected faulty messages - -N == 4 \* the total number of processes: correct and faulty -T == 1 \* an upper bound on the number of Byzantine processes -F == 1 \* the number of Byzantine processes -Heights == 0..1 \* the set of consensus instances -Rounds == 0..2 \* the set of possible rounds, give a bit more freedom to the solver -ValidValues == {0, 1} \* e.g., picked by a correct process, or a faulty one -InvalidValues == {2} \* e.g., sent by a Byzantine process - -PValues == ValidValues \cup InvalidValues \* all values -PProcs == 1..N-F -PFaulty == N-F+1..N - -FaultyMessages == \* the messages that can be sent by the faulty processes - [type: {"PROPOSAL"}, src: PFaulty, h: Heights, - round: Rounds, proposal: PValues, validRound: Rounds \cup {-1}] - \cup - [type: {"PREVOTE"}, src: PFaulty, h: Heights, round: Rounds, hash: PValues] - \cup - [type: {"PRECOMMIT"}, src: PFaulty, h: Heights, round: Rounds, hash: PValues] - -\* the proposer is rotating -PropFun == [<> \in Heights \X Rounds |-> (ht + rd) % N] - -\* the variables that are declared in TendermintFork -VARIABLES h, round, step, decision, lockedValue, lockedRound, validValue, validRound -VARIABLES msgsPropose, msgsPrevote, msgsPrecommit, - oldEvents, timeoutPropose, timeoutPrevote, timeoutPrecommit - -INSTANCE TendermintFork - - -============================================================================= -\* Modification History -\* Last modified Mon Nov 04 14:59:54 CET 2019 by igor -\* Created Mon Nov 04 11:47:48 CET 2019 by igor From 6db2be33c2184f77d3ec19ee3f161c61450abec6 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 18 Mar 2020 13:32:36 +0100 Subject: [PATCH 27/38] the README --- spec/fork-cases/README.md | 48 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 spec/fork-cases/README.md diff --git a/spec/fork-cases/README.md b/spec/fork-cases/README.md new file mode 100644 index 0000000..0f0d18d --- /dev/null +++ b/spec/fork-cases/README.md @@ -0,0 +1,48 @@ +# Synopsis + + A TLA+ specification of a simplified Tendermint consensus, tuned for + fork accountability. The simplifications are as follows: + + - the procotol runs for one height, that is, one-shot consensus + + - this specification focuses on safety, so timeouts are modelled with + with non-determinism + + - the proposer function is non-determinstic, no fairness is assumed + + - the messages by the faulty processes are injected right in the initial states + + - every process has the voting power of 1 + + - hashes are modelled as identity + + Having the above assumptions in mind, the specification follows the pseudo-code + of the Tendermint paper: https://arxiv.org/abs/1807.04938 + + For the purposes of fork accountability, the faulty processes are partitioned + into two sets: the Byzantine processes and the amnesic processes. + While the Byzantine processes can demonstrate arbitrary behavior, including + no communication, the amnesic processes send their messages but do not hold + to the contract of locked values. + +# TLA+ modules + + - [TendermintAcc3](TendermintAcc3.tla) is the protocol specification, + + - [TendermintAccDebug3](TendermintAccDebug3.tla) contains the useful definitions + for debugging the protocol specification with TLC and Apalache, + + - [TendermintAccInv3](TendermintAccInv3.tla) contains an inductive invariant + for establishing the protocol safety as well as the forking cases, + + - `MC_n_f`, e.g., [MC_n4_f1](MC_n4_f1.tla), contains fixed constants + for model checking with Apalache + +# Model checking results + +TODO + +# Running the experiments + +TODO + From 6cbd6d963dc1276d9b5cce6ef1dbe6a500a9c1b2 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Thu, 19 Mar 2020 21:46:55 +0100 Subject: [PATCH 28/38] more benchmarks --- spec/fork-cases/001indinv-apalache.csv | 28 ++++++++++++++++++++++++++ spec/fork-cases/001indinv-apalache.ini | 8 ++++++++ spec/fork-cases/MC_n10_f3.tla | 26 ++++++++++++++++++++++++ spec/fork-cases/MC_n10_f4.tla | 26 ++++++++++++++++++++++++ spec/fork-cases/run.sh | 8 ++++++++ 5 files changed, 96 insertions(+) create mode 100644 spec/fork-cases/001indinv-apalache.csv create mode 100644 spec/fork-cases/001indinv-apalache.ini create mode 100644 spec/fork-cases/MC_n10_f3.tla create mode 100644 spec/fork-cases/MC_n10_f4.tla create mode 100755 spec/fork-cases/run.sh diff --git a/spec/fork-cases/001indinv-apalache.csv b/spec/fork-cases/001indinv-apalache.csv new file mode 100644 index 0000000..539cb7f --- /dev/null +++ b/spec/fork-cases/001indinv-apalache.csv @@ -0,0 +1,28 @@ +no,filename,tool,timeout,init,inv,next,args +1,MC_n4_f1.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +2,MC_n4_f2.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +3,MC_n4_f3.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +4,MC_n5_f1.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +5,MC_n5_f2.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +6,MC_n7_f2.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +7,MC_n7_f3.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +8,MC_n10_f3.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +9,MC_n10_f4.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +10,MC_n4_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +11,MC_n4_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +12,MC_n4_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +13,MC_n5_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +14,MC_n5_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +15,MC_n7_f2.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +16,MC_n7_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +17,MC_n10_f3.tla,apalache,24h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +18,MC_n10_f4.tla,apalache,24h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +19,MC_n4_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +20,MC_n4_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +21,MC_n4_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +22,MC_n5_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +23,MC_n5_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +24,MC_n7_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +25,MC_n7_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +26,MC_n10_f3.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit +27,MC_n10_f4.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit diff --git a/spec/fork-cases/001indinv-apalache.ini b/spec/fork-cases/001indinv-apalache.ini new file mode 100644 index 0000000..da6c3a3 --- /dev/null +++ b/spec/fork-cases/001indinv-apalache.ini @@ -0,0 +1,8 @@ +# a configuration file for apalache-tests/scripts/mk-run.py + +[apalache-unstable] +more_args= + +[apalache-card] +more_args= + diff --git a/spec/fork-cases/MC_n10_f3.tla b/spec/fork-cases/MC_n10_f3.tla new file mode 100644 index 0000000..7b70f95 --- /dev/null +++ b/spec/fork-cases/MC_n10_f3.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n10_f3 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3", "c4", "c5", "c6", "c7"}, + Amnesic <- {} <: {STRING}, + Byzantine <- {"f8", "f9", "f10"}, + N <- 10, + T <- 3, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/MC_n10_f4.tla b/spec/fork-cases/MC_n10_f4.tla new file mode 100644 index 0000000..0cd4611 --- /dev/null +++ b/spec/fork-cases/MC_n10_f4.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n10_f4 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3", "c4", "c5", "c6"}, + Amnesic <- {"f7"}, + Byzantine <- {"f8", "f9", "f10"}, + N <- 10, + T <- 3, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/run.sh b/spec/fork-cases/run.sh new file mode 100755 index 0000000..ca08f05 --- /dev/null +++ b/spec/fork-cases/run.sh @@ -0,0 +1,8 @@ +#!/bin/sh +# +# The script to run all experiments at once + +SCRIPTS_DIR=~/devl/apalache-tests/scripts \ + BUILDS="unstable card" \ + BENCHMARK=001indinv-apalache \ + make -e -f ~/devl/apalache-tests/Makefile.common From 2e9cff685e8af8a320fb8e72f63d1f2e9bd482c0 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Fri, 20 Mar 2020 11:06:54 +0100 Subject: [PATCH 29/38] updated the script --- spec/fork-cases/run.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/fork-cases/run.sh b/spec/fork-cases/run.sh index ca08f05..8d5ffe7 100755 --- a/spec/fork-cases/run.sh +++ b/spec/fork-cases/run.sh @@ -5,4 +5,5 @@ SCRIPTS_DIR=~/devl/apalache-tests/scripts \ BUILDS="unstable card" \ BENCHMARK=001indinv-apalache \ + RUN_SCRIPT=./run-all.sh \ # alternatively, use ./run-parallel.sh make -e -f ~/devl/apalache-tests/Makefile.common From 493cb219e326cd55601352dec8e98005161c53d9 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sat, 21 Mar 2020 15:26:04 +0100 Subject: [PATCH 30/38] formalized the arguments --- spec/fork-cases/001indinv-apalache.csv | 43 ++++++++++++---------- spec/fork-cases/MC_n10_f3.tla | 2 +- spec/fork-cases/MC_n10_f4.tla | 2 +- spec/fork-cases/MC_n4_f1.tla | 2 +- spec/fork-cases/MC_n4_f2.tla | 2 +- spec/fork-cases/MC_n4_f3.tla | 2 +- spec/fork-cases/MC_n5_f1.tla | 2 +- spec/fork-cases/MC_n5_f2.tla | 2 +- spec/fork-cases/MC_n7_f2.tla | 2 +- spec/fork-cases/MC_n7_f3.tla | 2 +- spec/fork-cases/MC_n7_f4.tla | 26 +++++++++++++ spec/fork-cases/README.md | 51 +++++++++++++++++++++++++- spec/fork-cases/TendermintAcc3.tla | 37 +++++++++++++------ spec/fork-cases/TendermintAccInv3.tla | 39 ++++++++++++++++---- 14 files changed, 166 insertions(+), 48 deletions(-) create mode 100644 spec/fork-cases/MC_n7_f4.tla diff --git a/spec/fork-cases/001indinv-apalache.csv b/spec/fork-cases/001indinv-apalache.csv index 539cb7f..39c3d18 100644 --- a/spec/fork-cases/001indinv-apalache.csv +++ b/spec/fork-cases/001indinv-apalache.csv @@ -6,23 +6,26 @@ no,filename,tool,timeout,init,inv,next,args 5,MC_n5_f2.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit 6,MC_n7_f2.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit 7,MC_n7_f3.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit -8,MC_n10_f3.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit -9,MC_n10_f4.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit -10,MC_n4_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit -11,MC_n4_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit -12,MC_n4_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit -13,MC_n5_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit -14,MC_n5_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit -15,MC_n7_f2.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit -16,MC_n7_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit -17,MC_n10_f3.tla,apalache,24h,TypedInv,Agreement,,--length=0 --cinit=ConstInit -18,MC_n10_f4.tla,apalache,24h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit -19,MC_n4_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -20,MC_n4_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -21,MC_n4_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -22,MC_n5_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -23,MC_n5_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -24,MC_n7_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -25,MC_n7_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit -26,MC_n10_f3.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit -27,MC_n10_f4.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit +8,MC_n7_f4.tla,apalache,10h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +9,MC_n10_f3.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +10,MC_n10_f4.tla,apalache,24h,TypedInv,TypedInv,,--length=1 --cinit=ConstInit +11,MC_n4_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +12,MC_n4_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +13,MC_n4_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +14,MC_n5_f1.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +15,MC_n5_f2.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +16,MC_n7_f2.tla,apalache,10h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +17,MC_n7_f3.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +18,MC_n7_f4.tla,apalache,10h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +19,MC_n10_f3.tla,apalache,24h,TypedInv,Agreement,,--length=0 --cinit=ConstInit +20,MC_n10_f4.tla,apalache,24h,TypedInv,AgreementOrEquivocationOrAmnesia,,--length=0 --cinit=ConstInit +21,MC_n4_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +22,MC_n4_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +23,MC_n4_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +24,MC_n5_f1.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +25,MC_n5_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +26,MC_n7_f2.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +27,MC_n7_f3.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +28,MC_n7_f4.tla,apalache,10h,Init,TypedInv,,--length=0 --cinit=ConstInit +29,MC_n10_f3.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit +30,MC_n10_f4.tla,apalache,24h,Init,TypedInv,,--length=0 --cinit=ConstInit diff --git a/spec/fork-cases/MC_n10_f3.tla b/spec/fork-cases/MC_n10_f3.tla index 7b70f95..0f20f92 100644 --- a/spec/fork-cases/MC_n10_f3.tla +++ b/spec/fork-cases/MC_n10_f3.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4", "c5", "c6", "c7"}, - Amnesic <- {} <: {STRING}, + Defective <- {} <: {STRING}, Byzantine <- {"f8", "f9", "f10"}, N <- 10, T <- 3, diff --git a/spec/fork-cases/MC_n10_f4.tla b/spec/fork-cases/MC_n10_f4.tla index 0cd4611..3271dcd 100644 --- a/spec/fork-cases/MC_n10_f4.tla +++ b/spec/fork-cases/MC_n10_f4.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4", "c5", "c6"}, - Amnesic <- {"f7"}, + Defective <- {"f7"}, Byzantine <- {"f8", "f9", "f10"}, N <- 10, T <- 3, diff --git a/spec/fork-cases/MC_n4_f1.tla b/spec/fork-cases/MC_n4_f1.tla index 6d3072f..94a9558 100644 --- a/spec/fork-cases/MC_n4_f1.tla +++ b/spec/fork-cases/MC_n4_f1.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3"}, - Amnesic <- {} <: {STRING}, + Defective <- {} <: {STRING}, Byzantine <- {"f1"}, N <- 4, T <- 1, diff --git a/spec/fork-cases/MC_n4_f2.tla b/spec/fork-cases/MC_n4_f2.tla index 68d25f1..91eae54 100644 --- a/spec/fork-cases/MC_n4_f2.tla +++ b/spec/fork-cases/MC_n4_f2.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2"}, - Amnesic <- {"f3"}, + Defective <- {"f3"}, Byzantine <- {"f4"}, N <- 4, T <- 1, diff --git a/spec/fork-cases/MC_n4_f3.tla b/spec/fork-cases/MC_n4_f3.tla index 9949d9d..4413817 100644 --- a/spec/fork-cases/MC_n4_f3.tla +++ b/spec/fork-cases/MC_n4_f3.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1"}, - Amnesic <- {"a2", "a3"}, + Defective <- {"a2", "a3"}, Byzantine <- {"f4"}, N <- 4, T <- 1, diff --git a/spec/fork-cases/MC_n5_f1.tla b/spec/fork-cases/MC_n5_f1.tla index 149a456..156675f 100644 --- a/spec/fork-cases/MC_n5_f1.tla +++ b/spec/fork-cases/MC_n5_f1.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4"}, - Amnesic <- {} <: {STRING}, + Defective <- {} <: {STRING}, Byzantine <- {"f5"}, N <- 5, T <- 1, diff --git a/spec/fork-cases/MC_n5_f2.tla b/spec/fork-cases/MC_n5_f2.tla index feb02d1..ef40158 100644 --- a/spec/fork-cases/MC_n5_f2.tla +++ b/spec/fork-cases/MC_n5_f2.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3"}, - Amnesic <- {"a4"}, + Defective <- {"a4"}, Byzantine <- {"f5"}, N <- 5, T <- 1, diff --git a/spec/fork-cases/MC_n7_f2.tla b/spec/fork-cases/MC_n7_f2.tla index 86d8972..ac2de94 100644 --- a/spec/fork-cases/MC_n7_f2.tla +++ b/spec/fork-cases/MC_n7_f2.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4", "c5"}, - Amnesic <- {} <: {STRING}, + Defective <- {} <: {STRING}, Byzantine <- {"f6", "f7"}, N <- 7, T <- 2, diff --git a/spec/fork-cases/MC_n7_f3.tla b/spec/fork-cases/MC_n7_f3.tla index 70e004e..32e4d4a 100644 --- a/spec/fork-cases/MC_n7_f3.tla +++ b/spec/fork-cases/MC_n7_f3.tla @@ -11,7 +11,7 @@ a <: b == a INSTANCE TendermintAccDebug3 WITH Corr <- {"c1", "c2", "c3", "c4"}, - Amnesic <- {"f5"}, + Defective <- {"f5"}, Byzantine <- {"f6", "f7"}, N <- 7, T <- 2, diff --git a/spec/fork-cases/MC_n7_f4.tla b/spec/fork-cases/MC_n7_f4.tla new file mode 100644 index 0000000..8a03235 --- /dev/null +++ b/spec/fork-cases/MC_n7_f4.tla @@ -0,0 +1,26 @@ +----------------------------- MODULE MC_n7_f4 ------------------------------- +CONSTANT Proposer \* the proposer function from 0..NRounds to 1..N + +\* the variables declared in TendermintAcc3 +VARIABLES + round, step, decision, lockedValue, lockedRound, validValue, validRound, + msgsPropose, msgsPrevote, msgsPrecommit, evidence + +\* an operator for type annotations +a <: b == a + +INSTANCE TendermintAccDebug3 WITH + Corr <- {"c1", "c2", "c3"}, + Defective <- {"f4", "f5"}, + Byzantine <- {"f6", "f7"}, + N <- 7, + T <- 2, + ValidValues <- { "v0", "v1" }, + InvalidValues <- {"v2"}, + MaxRound <- 2 + +\* run Apalache with --cinit=ConstInit +ConstInit == \* the proposer is arbitrary -- works for safety + Proposer \in [Rounds -> AllProcs] + +============================================================================= diff --git a/spec/fork-cases/README.md b/spec/fork-cases/README.md index 0f0d18d..3a6a9f7 100644 --- a/spec/fork-cases/README.md +++ b/spec/fork-cases/README.md @@ -38,11 +38,60 @@ - `MC_n_f`, e.g., [MC_n4_f1](MC_n4_f1.tla), contains fixed constants for model checking with Apalache +# Reasoning about fork scenarios + +The theorem statements can be found in +[TendermintAccInv3.tla](TendermintAccInv3.tla). + +First, we would like to show that `TypedInv` is an inductive invariant. +Formally, the statement looks as follows: + +```tla +THEOREM TypedInvIsInductive == + \/ FaultyQuorum + \//\ Init => TypedInv + /\ TypedInv /\ [Next]_vars => TypedInv +``` + +When over two-thirds of processes are faulty, `TypedInv` is not inductive. +However, there is no hope to repair the protocol in this case. We run Apalache +to prove this theorem only for fixed instances of 4 to 10 processes. Apalache +does not parse theorem statements at the moment, so we ran Apalache using a +shell script. To find a parameterized argument, one has to use a theorem +prover, e.g., TLAPS. + +Second, we would like to show that the invariant implies `Agreement`, that is, +no fork, provided that less than one third of processes is faulty. By combining +this theorem with the previous theorem, we conclude that the protocol indeed +satisfies Agreement under the condition `LessThanThirdFaulty`. + +```tla +THEOREM AgreementWhenLessThanThirdFaulty == + LessThanThirdFaulty /\ TypedInv => Agreement +``` + +Third, in the general case, we either have no fork, or two fork scenarios: + +```tla +THEOREM AgreementOrFork == + ~FaultyQuorum /\ TypedInv => AgreementOrEquivocationOrAmnesia +``` + # Model checking results TODO # Running the experiments -TODO +Run the experiments by using the script: + +```console +./run.sh +``` + +This script assumes that apalache builds are available in: + + * `~/devl/apalache-card` contains the build for the branch `ik/card`, + * `~/devl/apalache-unstable` contains the build for branch `unstable`. + diff --git a/spec/fork-cases/TendermintAcc3.tla b/spec/fork-cases/TendermintAcc3.tla index c205d9d..7d36ea5 100644 --- a/spec/fork-cases/TendermintAcc3.tla +++ b/spec/fork-cases/TendermintAcc3.tla @@ -37,7 +37,7 @@ EXTENDS Integers, FiniteSets (********************* PROTOCOL PARAMETERS **********************************) CONSTANTS Corr, \* the set of correct processes - Amnesic, \* the set of amnesic processes, may be empty + Defective, \* the set of processes that show defects, e.g, amnesia or equivocation Byzantine, \* the set of Byzantine processes, may be empty N, \* the total number of processes: correct, amnesic, and Byzantine T, \* an upper bound on the number of Byzantine processes @@ -46,10 +46,10 @@ CONSTANTS MaxRound, \* the maximal round number Proposer \* the proposer function from 0..NRounds to 1..N -ASSUME(N = Cardinality(Corr \union Amnesic \union Byzantine)) +ASSUME(N = Cardinality(Corr \union Defective \union Byzantine)) (*************************** DEFINITIONS ************************************) -Faulty == Amnesic \union Byzantine \* the set of faulty processes +Faulty == Defective \union Byzantine \* the set of faulty processes AllProcs == Corr \union Faulty \* the set of all processes Rounds == 0..MaxRound \* the set of potential rounds NilRound == -1 \* a special value to denote a nil round, outside of Rounds @@ -326,8 +326,8 @@ Next == \/ OnQuoromOfNilPrevotes(p) \/ OnRoundCatchup(p) -(**************************** FORK ACCOUNTABILITY ***************************) -\* a state that has equivocation +(**************************** FORK SCENARIOS ***************************) +\* a state that has at least one equivocation Equivocation == \E r \in Rounds: \/ \E m1, m2 \in msgsPropose[r]: @@ -337,8 +337,18 @@ Equivocation == \/ \E m1, m2 \in msgsPrecommit[r]: m1 /= m2 /\ m1.src = m2.src +\* equivocation by amnesic processes +EquivocationBy(p) == + \E r \in Rounds: + \/ \E m1, m2 \in msgsPropose[r]: + m1.proposal /= m2.proposal /\ m1.src = p /\ m2.src = p + \/ \E m1, m2 \in msgsPrevote[r]: + m1.id /= m2.id /\ m1.src = p /\ m2.src = p + \/ \E m1, m2 \in msgsPrecommit[r]: + m1.id /= m2.id /\ m1.src = p /\ m2.src = p + \* amnesic behavior by a process p -Amnesia(p) == +AmnesiaBy(p) == \E r1, r2 \in Rounds: /\ r1 < r2 /\ \E v1, v2 \in ValidValues: @@ -373,17 +383,22 @@ Validity == \* either agreement holds, or the amnesic processes indeed have amnesia AgreementOrAmnesia == - Agreement \/ (\A p \in Amnesic: Amnesia(p)) + Agreement \/ (\A p \in Defective: AmnesiaBy(p)) \* the strong agreement property that also assumes no amnesia AgreementNoAmnesia == - Agreement /\ \A p \in Amnesic: ~Amnesia(p) + Agreement /\ \A p \in Defective: ~AmnesiaBy(p) -\* the protocol invariant +(* + The protocol safety. Three cases are possible: + 1. There is no fork, that is, Agreement holds true. + 2. The amnesic processes have sent equivocal messages. + 3. The amnesic processes have sent messages that prove amnesia. + *) AgreementOrEquivocationOrAmnesia == \/ Agreement - \/ Equivocation - \/ \A p \in Amnesic: Amnesia(p) + \/ \A p \in Defective: EquivocationBy(p) + \/ \A p \in Defective: AmnesiaBy(p) ============================================================================= diff --git a/spec/fork-cases/TendermintAccInv3.tla b/spec/fork-cases/TendermintAccInv3.tla index c63a3fa..cebca78 100644 --- a/spec/fork-cases/TendermintAccInv3.tla +++ b/spec/fork-cases/TendermintAccInv3.tla @@ -293,12 +293,37 @@ IfValidRoundThenProposal(p) == AllIfValidRoundThenProposal == \A p \in Corr: IfValidRoundThenProposal(p) -(******************************** PROPERTIES ***************************************) -InvAndNoEquivocation == - Inv /\ ~Equivocation - -\* use this predicate for the initial states -TypedInvNoEquivocationNoAmnesia == - TypeOK /\ Inv /\ ~Equivocation /\ (\A p \in Faulty: ~Amnesia(p)) +(******************************** THEOREMS ***************************************) +(* Under this condition, the faulty processes can decide alone *) +FaultyQuorum == Cardinality(Faulty) >= THRESHOLD2 + +(* The standard condition of the Tendermint security model *) +LessThanThirdFaulty == N > 3 * T /\ Cardinality(Faulty) <= T + +(* + TypedInv is an inductive invariant, provided that there is no faulty quorum. + We run Apalache to prove this theorem only for fixed instances of 4 to 10 processes. + (We run Apalache manually, as it does not parse theorem statements at the moment.) + To get a parameterized argument, one has to use a theorem prover, e.g., TLAPS. + *) +THEOREM TypedInvIsInductive == + \/ FaultyQuorum \* if there are 2 * T + 1 faulty processes, we give up + \//\ Init => TypedInv + /\ TypedInv /\ [Next]_vars => TypedInv + +(* + There should be no fork, when there are less than 1/3 faulty processes. + *) +THEOREM AgreementWhenLessThanThirdFaulty == + LessThanThirdFaulty /\ TypedInv => Agreement + +(* + In a more general case, when there are less than 2/3 faulty processes, + there is either no fork, or two scenarios exist: + equivocation by Amnesic, or amnesia by Amnesic. + *) +THEOREM AgreementOrFork == + ~FaultyQuorum /\ TypedInv => AgreementOrEquivocationOrAmnesia + ============================================================================= From fbf4eb7737ebe69bcd21777d1fec4f96f50b7f44 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 29 Mar 2020 11:28:24 +0200 Subject: [PATCH 31/38] fixed the description a bit --- spec/fork-cases/TendermintAcc3.tla | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/spec/fork-cases/TendermintAcc3.tla b/spec/fork-cases/TendermintAcc3.tla index 7d36ea5..0a12490 100644 --- a/spec/fork-cases/TendermintAcc3.tla +++ b/spec/fork-cases/TendermintAcc3.tla @@ -3,7 +3,7 @@ A TLA+ specification of a simplified Tendermint consensus, tuned for fork accountability. The simplifications are as follows: - - the procotol runs for one height, that is, one-shot consensus + - the protocol runs for one height, that is, it is one-shot consensus - this specification focuses on safety, so timeouts are modelled with with non-determinism @@ -20,10 +20,16 @@ of the Tendermint paper: https://arxiv.org/abs/1807.04938 For the purposes of fork accountability, the faulty processes are partitioned - into two sets: the Byzantine processes and the amnesic processes. + into two sets: the Byzantine processes and the defective processes. While the Byzantine processes can demonstrate arbitrary behavior, including - no communication, the amnesic processes send their messages but do not hold - to the contract of locked values. + no communication, the defective processes send their messages but deviate + from the protocol in two ways: + + - Equivocation: a defective process may send two different values + in the same round. + + - Amnesia: a defective process may lock a value, although it has locked + another value in the past. * Version 3. Modular and parameterized definitions. * Version 2. Bugfixes in the spec and an inductive invariant. From e66652241acb357b5be3f43060b90432d33bdf85 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 29 Mar 2020 11:29:18 +0200 Subject: [PATCH 32/38] fixed README too --- spec/fork-cases/README.md | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/spec/fork-cases/README.md b/spec/fork-cases/README.md index 3a6a9f7..bad68cd 100644 --- a/spec/fork-cases/README.md +++ b/spec/fork-cases/README.md @@ -20,10 +20,16 @@ of the Tendermint paper: https://arxiv.org/abs/1807.04938 For the purposes of fork accountability, the faulty processes are partitioned - into two sets: the Byzantine processes and the amnesic processes. + into two sets: the Byzantine processes and the defective processes. While the Byzantine processes can demonstrate arbitrary behavior, including - no communication, the amnesic processes send their messages but do not hold - to the contract of locked values. + no communication, the defective processes send their messages but deviate + from the protocol in two ways: + + - Equivocation: a defective process may send two different values + in the same round. + + - Amnesia: a defective process may lock a value, although it has locked + another value in the past. # TLA+ modules From ccaf2f0236f769adf7524c6e258750276f15b5ff Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 29 Mar 2020 18:13:17 +0000 Subject: [PATCH 33/38] copied the results --- .../results/001indinv-apalache-card.csv | 31 + .../results/001indinv-apalache-mem-log.svg | 1278 ++++++++++++++++ .../results/001indinv-apalache-mem.svg | 1216 +++++++++++++++ .../results/001indinv-apalache-ncells.svg | 1227 +++++++++++++++ .../results/001indinv-apalache-nclauses.svg | 1237 +++++++++++++++ .../results/001indinv-apalache-report.md | 133 ++ .../results/001indinv-apalache-time-log.svg | 1326 +++++++++++++++++ .../results/001indinv-apalache-time.svg | 1185 +++++++++++++++ .../results/001indinv-apalache-unstable.csv | 31 + 9 files changed, 7664 insertions(+) create mode 100644 spec/fork-cases/results/001indinv-apalache-card.csv create mode 100644 spec/fork-cases/results/001indinv-apalache-mem-log.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-mem.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-ncells.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-nclauses.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-report.md create mode 100644 spec/fork-cases/results/001indinv-apalache-time-log.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-time.svg create mode 100644 spec/fork-cases/results/001indinv-apalache-unstable.csv diff --git a/spec/fork-cases/results/001indinv-apalache-card.csv b/spec/fork-cases/results/001indinv-apalache-card.csv new file mode 100644 index 0000000..a6edfc8 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-card.csv @@ -0,0 +1,31 @@ +01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len +1,apalache,NoError,745,1,7575976,0,0,255850,2916339,20 +2,apalache,NoError,703,1,7328636,0,0,242262,2881034,21 +3,apalache,NoError,593,1,8040896,0,0,227457,2854217,21 +4,apalache,NoError,1420,1,10484644,0,0,346159,4446361,21 +5,apalache,NoError,1401,1,11135196,0,0,329305,4393688,21 +6,apalache,NoError,3481,1,13823704,0,0,561290,9239413,20 +7,apalache,NoError,3461,1,13866816,0,0,531012,9216485,21 +8,apalache,NoError,3171,1,13627120,0,0,508797,9012976,21 +9,apalache,NoError,10993,1,17585212,0,0,999840,20562209,20 +10,apalache,NoError,9766,1,17456608,0,0,938833,20059531,20 +11,apalache,NoError,295,0,4634444,0,0,148963,2161260,17 +12,apalache,NoError,439,0,8616344,0,0,1019658,4179230,13 +13,apalache,NoError,410,0,8625148,0,0,1008704,4133605,13 +14,apalache,NoError,483,0,7152440,0,0,212357,3324811,17 +15,apalache,NoError,758,0,13256700,0,0,1571212,6464897,13 +16,apalache,NoError,1183,0,13292156,0,0,374517,7397022,16 +17,apalache,NoError,1732,0,19419708,0,0,3034896,13545586,13 +18,apalache,NoError,1975,0,20509864,0,0,3010964,13451475,13 +19,apalache,NoError,3205,0,16831228,0,0,734069,17072460,16 +20,apalache,NoError,4561,0,29809472,0,0,6158724,29595562,13 +21,apalache,NoError,10,0,657616,0,0,3478,19896,27 +22,apalache,NoError,13,0,701208,0,0,5899,37943,29 +23,apalache,Error,18,0,991180,0,0,7572,54445,30 +24,apalache,NoError,10,0,624420,0,0,3627,22678,29 +25,apalache,NoError,15,0,977908,0,0,6038,42659,31 +26,apalache,NoError,18,0,972612,0,0,6366,52286,34 +27,apalache,NoError,23,0,1152052,0,0,8739,76788,34 +28,apalache,NoError,30,0,1257044,0,0,10931,100893,34 +29,apalache,NoError,30,0,1337804,0,0,9156,97270,37 +30,apalache,NoError,39,0,1511844,0,0,11475,128242,37 diff --git a/spec/fork-cases/results/001indinv-apalache-mem-log.svg b/spec/fork-cases/results/001indinv-apalache-mem-log.svg new file mode 100644 index 0000000..3d993e2 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-mem-log.svg @@ -0,0 +1,1278 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-mem.svg b/spec/fork-cases/results/001indinv-apalache-mem.svg new file mode 100644 index 0000000..51eb45e --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-mem.svg @@ -0,0 +1,1216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-ncells.svg b/spec/fork-cases/results/001indinv-apalache-ncells.svg new file mode 100644 index 0000000..8b24e52 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-ncells.svg @@ -0,0 +1,1227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-nclauses.svg b/spec/fork-cases/results/001indinv-apalache-nclauses.svg new file mode 100644 index 0000000..2f3f273 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-nclauses.svg @@ -0,0 +1,1237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-report.md b/spec/fork-cases/results/001indinv-apalache-report.md new file mode 100644 index 0000000..313c802 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-report.md @@ -0,0 +1,133 @@ +# Results of 001indinv-apalache + + +## 1. Awesome plots + +### 1.1. Time (logarithmic scale) + +![time-log](001indinv-apalache-time-log.svg "Time Log") + +### 1.2. Time (linear) + +![time-log](001indinv-apalache-time.svg "Time Log") + +### 1.3. Memory (logarithmic scale) + +![mem-log](001indinv-apalache-mem-log.svg "Memory Log") + +### 1.4. Memory (linear) + +![mem](001indinv-apalache-mem.svg "Memory Log") + +### 1.5. Number of arena cells (linear) + +![ncells](001indinv-apalache-ncells.svg "Number of arena cells") + +### 1.6. Number of SMT clauses (linear) + +![nclauses](001indinv-apalache-nclauses.svg "Number of SMT clauses") + +## 2. Input parameters + +no | filename | tool | timeout | init | inv | next | args +----|-----------------|------------|-----------|------------|------------------------------------|--------|------------------------------ +1 | MC_n4_f1.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +2 | MC_n4_f2.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +3 | MC_n4_f3.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +4 | MC_n5_f1.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +5 | MC_n5_f2.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +6 | MC_n7_f2.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +7 | MC_n7_f3.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +8 | MC_n7_f4.tla | apalache | 10h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +9 | MC_n10_f3.tla | apalache | 24h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +10 | MC_n10_f4.tla | apalache | 24h | TypedInv | TypedInv | | --length=1 --cinit=ConstInit +11 | MC_n4_f1.tla | apalache | 10h | TypedInv | Agreement | | --length=0 --cinit=ConstInit +12 | MC_n4_f2.tla | apalache | 10h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +13 | MC_n4_f3.tla | apalache | 10h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +14 | MC_n5_f1.tla | apalache | 10h | TypedInv | Agreement | | --length=0 --cinit=ConstInit +15 | MC_n5_f2.tla | apalache | 10h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +16 | MC_n7_f2.tla | apalache | 10h | TypedInv | Agreement | | --length=0 --cinit=ConstInit +17 | MC_n7_f3.tla | apalache | 10h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +18 | MC_n7_f4.tla | apalache | 10h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +19 | MC_n10_f3.tla | apalache | 24h | TypedInv | Agreement | | --length=0 --cinit=ConstInit +20 | MC_n10_f4.tla | apalache | 24h | TypedInv | AgreementOrEquivocationOrAmnesia | | --length=0 --cinit=ConstInit +21 | MC_n4_f1.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +22 | MC_n4_f2.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +23 | MC_n4_f3.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +24 | MC_n5_f1.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +25 | MC_n5_f2.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +26 | MC_n7_f2.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +27 | MC_n7_f3.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +28 | MC_n7_f4.tla | apalache | 10h | Init | TypedInv | | --length=0 --cinit=ConstInit +29 | MC_n10_f3.tla | apalache | 24h | Init | TypedInv | | --length=0 --cinit=ConstInit +30 | MC_n10_f4.tla | apalache | 24h | Init | TypedInv | | --length=0 --cinit=ConstInit + +## 3. Detailed results: 001indinv-apalache-unstable.csv + +01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len +-------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- +1 | apalache | NoError | 12m | 1 | 7.0GB | 0 | 0 | 255K | 2.0M | 20 +2 | apalache | NoError | 11m | 1 | 7.0GB | 0 | 0 | 242K | 2.0M | 21 +3 | apalache | NoError | 9m09s | 1 | 7.0GB | 0 | 0 | 227K | 2.0M | 21 +4 | apalache | NoError | 26m | 1 | 10GB | 0 | 0 | 346K | 4.0M | 21 +5 | apalache | NoError | 21m | 1 | 10GB | 0 | 0 | 329K | 4.0M | 21 +6 | apalache | NoError | 56m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 +7 | apalache | NoError | 53m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 +8 | apalache | NoError | 50m | 1 | 13GB | 0 | 0 | 508K | 9.0M | 21 +9 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 +10 | apalache | NoError | 3h03m | 1 | 16GB | 0 | 0 | 938K | 20M | 20 +11 | apalache | NoError | 4m04s | 0 | 4.0GB | 0 | 0 | 148K | 2.0M | 17 +12 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 +13 | apalache | NoError | 6m06s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 +14 | apalache | NoError | 8m08s | 0 | 6.0GB | 0 | 0 | 212K | 3.0M | 17 +15 | apalache | NoError | 12m | 0 | 13GB | 0 | 0 | 1.0M | 6.0M | 13 +16 | apalache | NoError | 20m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 +17 | apalache | NoError | 28m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 +18 | apalache | NoError | 32m | 0 | 20GB | 0 | 0 | 3.0M | 13M | 13 +19 | apalache | NoError | 53m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 +20 | apalache | NoError | 1h01m | 0 | 28GB | 0 | 0 | 6.0M | 29M | 13 +21 | apalache | NoError | 10s | 0 | 622MB | 0 | 0 | 3.0K | 19K | 27 +22 | apalache | NoError | 13s | 0 | 778MB | 0 | 0 | 5.0K | 37K | 29 +23 | apalache | Error | 18s | 0 | 888MB | 0 | 0 | 7.0K | 54K | 30 +24 | apalache | NoError | 10s | 0 | 651MB | 0 | 0 | 3.0K | 22K | 29 +25 | apalache | NoError | 15s | 0 | 929MB | 0 | 0 | 6.0K | 42K | 31 +26 | apalache | NoError | 18s | 0 | 930MB | 0 | 0 | 6.0K | 52K | 34 +27 | apalache | NoError | 24s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 +28 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 +29 | apalache | NoError | 32s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 +30 | apalache | NoError | 39s | 0 | 1.0GB | 0 | 0 | 11K | 128K | 37 + +## 4. Detailed results: 001indinv-apalache-card.csv + +01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len +-------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- +1 | apalache | NoError | 12m | 1 | 7.0GB | 0 | 0 | 255K | 2.0M | 20 +2 | apalache | NoError | 11m | 1 | 6.0GB | 0 | 0 | 242K | 2.0M | 21 +3 | apalache | NoError | 9m09s | 1 | 7.0GB | 0 | 0 | 227K | 2.0M | 21 +4 | apalache | NoError | 23m | 1 | 9.0GB | 0 | 0 | 346K | 4.0M | 21 +5 | apalache | NoError | 23m | 1 | 10GB | 0 | 0 | 329K | 4.0M | 21 +6 | apalache | NoError | 58m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 +7 | apalache | NoError | 57m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 +8 | apalache | NoError | 52m | 1 | 12GB | 0 | 0 | 508K | 9.0M | 21 +9 | apalache | NoError | 3h03m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 +10 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 938K | 20M | 20 +11 | apalache | NoError | 4m04s | 0 | 4.0GB | 0 | 0 | 148K | 2.0M | 17 +12 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 +13 | apalache | NoError | 6m06s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 +14 | apalache | NoError | 8m08s | 0 | 6.0GB | 0 | 0 | 212K | 3.0M | 17 +15 | apalache | NoError | 12m | 0 | 12GB | 0 | 0 | 1.0M | 6.0M | 13 +16 | apalache | NoError | 19m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 +17 | apalache | NoError | 28m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 +18 | apalache | NoError | 32m | 0 | 19GB | 0 | 0 | 3.0M | 13M | 13 +19 | apalache | NoError | 53m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 +20 | apalache | NoError | 1h01m | 0 | 28GB | 0 | 0 | 6.0M | 29M | 13 +21 | apalache | NoError | 10s | 0 | 642MB | 0 | 0 | 3.0K | 19K | 27 +22 | apalache | NoError | 13s | 0 | 684MB | 0 | 0 | 5.0K | 37K | 29 +23 | apalache | Error | 18s | 0 | 967MB | 0 | 0 | 7.0K | 54K | 30 +24 | apalache | NoError | 10s | 0 | 609MB | 0 | 0 | 3.0K | 22K | 29 +25 | apalache | NoError | 15s | 0 | 954MB | 0 | 0 | 6.0K | 42K | 31 +26 | apalache | NoError | 18s | 0 | 949MB | 0 | 0 | 6.0K | 52K | 34 +27 | apalache | NoError | 23s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 +28 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 +29 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 +30 | apalache | NoError | 39s | 0 | 1.0GB | 0 | 0 | 11K | 128K | 37 diff --git a/spec/fork-cases/results/001indinv-apalache-time-log.svg b/spec/fork-cases/results/001indinv-apalache-time-log.svg new file mode 100644 index 0000000..4c4704c --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-time-log.svg @@ -0,0 +1,1326 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-time.svg b/spec/fork-cases/results/001indinv-apalache-time.svg new file mode 100644 index 0000000..2646607 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-time.svg @@ -0,0 +1,1185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spec/fork-cases/results/001indinv-apalache-unstable.csv b/spec/fork-cases/results/001indinv-apalache-unstable.csv new file mode 100644 index 0000000..6de18d0 --- /dev/null +++ b/spec/fork-cases/results/001indinv-apalache-unstable.csv @@ -0,0 +1,31 @@ +01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len +1,apalache,NoError,765,1,7449524,0,0,255850,2916339,20 +2,apalache,NoError,712,1,7953452,0,0,242262,2881034,21 +3,apalache,NoError,573,1,8108036,0,0,227457,2854217,21 +4,apalache,NoError,1611,1,11197492,0,0,346159,4446361,21 +5,apalache,NoError,1294,1,11006544,0,0,329305,4393688,21 +6,apalache,NoError,3366,1,13817260,0,0,561290,9239413,20 +7,apalache,NoError,3190,1,13851528,0,0,531012,9216485,21 +8,apalache,NoError,3052,1,13713040,0,0,508797,9012976,21 +9,apalache,NoError,9752,1,17815528,0,0,999840,20562209,20 +10,apalache,NoError,11005,1,17483056,0,0,938833,20059531,20 +11,apalache,NoError,295,0,5232284,0,0,148963,2161260,17 +12,apalache,NoError,441,0,9130148,0,0,1019658,4179230,13 +13,apalache,NoError,412,0,8666908,0,0,1008704,4133605,13 +14,apalache,NoError,484,0,7093124,0,0,212357,3324811,17 +15,apalache,NoError,768,0,13713572,0,0,1571212,6464897,13 +16,apalache,NoError,1219,0,13155580,0,0,374517,7397022,16 +17,apalache,NoError,1732,0,19315084,0,0,3034896,13545586,13 +18,apalache,NoError,1943,0,21061592,0,0,3010964,13451475,13 +19,apalache,NoError,3210,0,17087572,0,0,734069,17072460,16 +20,apalache,NoError,4591,0,29649044,0,0,6158724,29595562,13 +21,apalache,NoError,10,0,637636,0,0,3478,19896,27 +22,apalache,NoError,13,0,796672,0,0,5899,37943,29 +23,apalache,Error,18,0,910176,0,0,7572,54445,30 +24,apalache,NoError,10,0,667284,0,0,3627,22678,29 +25,apalache,NoError,15,0,952296,0,0,6038,42659,31 +26,apalache,NoError,18,0,952360,0,0,6366,52286,34 +27,apalache,NoError,24,0,1094008,0,0,8739,76788,34 +28,apalache,NoError,30,0,1347008,0,0,10931,100893,34 +29,apalache,NoError,32,0,1211532,0,0,9156,97270,37 +30,apalache,NoError,39,0,1603728,0,0,11475,128242,37 From 5bd15542b91e5dd4ca45b168283731f16a217570 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Sun, 29 Mar 2020 18:13:44 +0000 Subject: [PATCH 34/38] fixed the script --- spec/fork-cases/run.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/fork-cases/run.sh b/spec/fork-cases/run.sh index 8d5ffe7..929d7ba 100755 --- a/spec/fork-cases/run.sh +++ b/spec/fork-cases/run.sh @@ -2,8 +2,8 @@ # # The script to run all experiments at once -SCRIPTS_DIR=~/devl/apalache-tests/scripts \ - BUILDS="unstable card" \ - BENCHMARK=001indinv-apalache \ - RUN_SCRIPT=./run-all.sh \ # alternatively, use ./run-parallel.sh - make -e -f ~/devl/apalache-tests/Makefile.common +export SCRIPTS_DIR=~/devl/apalache-tests/scripts +export BUILDS="unstable card" +export BENCHMARK=001indinv-apalache +export RUN_SCRIPT=./run-all.sh # alternatively, use ./run-parallel.sh +make -e -f ~/devl/apalache-tests/Makefile.common From 3febe31919cdc547f2e3a1a9e8613a360f0393b6 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Tue, 31 Mar 2020 11:50:43 +0200 Subject: [PATCH 35/38] fixes after the dev session --- spec/fork-cases/TendermintAcc3.tla | 49 +++++++++++++++++++++++---- spec/fork-cases/TendermintAccInv3.tla | 8 +---- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/spec/fork-cases/TendermintAcc3.tla b/spec/fork-cases/TendermintAcc3.tla index 0a12490..7082600 100644 --- a/spec/fork-cases/TendermintAcc3.tla +++ b/spec/fork-cases/TendermintAcc3.tla @@ -31,6 +31,9 @@ - Amnesia: a defective process may lock a value, although it has locked another value in the past. + TODO: as there is no clear difference between Byzantine and Defective processes anymore, + we will probably merge them together. + * Version 3. Modular and parameterized definitions. * Version 2. Bugfixes in the spec and an inductive invariant. * Version 1. A preliminary specification. @@ -45,7 +48,7 @@ CONSTANTS Corr, \* the set of correct processes Defective, \* the set of processes that show defects, e.g, amnesia or equivocation Byzantine, \* the set of Byzantine processes, may be empty - N, \* the total number of processes: correct, amnesic, and Byzantine + N, \* the total number of processes: correct, defective, and Byzantine T, \* an upper bound on the number of Byzantine processes ValidValues, \* the set of valid values, proposed both by correct and faulty InvalidValues, \* the set of invalid values, never proposed by the correct ones @@ -117,13 +120,29 @@ FaultyProposals(r) == SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, round: {r}, proposal: Values, validRound: RoundsOrNil]) +AllFaultyProposals == + SetOfMsgs([type: {"PROPOSAL"}, src: Faulty, + round: Rounds, proposal: Values, validRound: RoundsOrNil]) + FaultyPrevotes(r) == SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: {r}, id: Values]) +AllFaultyPrevotes == + SetOfMsgs([type: {"PREVOTE"}, src: Faulty, round: Rounds, id: Values]) + FaultyPrecommits(r) == SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: {r}, id: Values]) -\* The initial states of the protocol. The faults can be in the system already. +AllFaultyPrecommits == + SetOfMsgs([type: {"PRECOMMIT"}, src: Faulty, round: Rounds, id: Values]) + +BenignRoundsInMessages(msgfun) == + \* the message function never contains a message for a wrong round + \A r \in Rounds: + \A m \in msgfun[r]: + r = m.round + +\* The initial states of the protocol. Some faults can be in the system already. Init == /\ round = [p \in Corr |-> 0] /\ step = [p \in Corr |-> "PROPOSE"] @@ -132,9 +151,12 @@ Init == /\ lockedRound = [p \in Corr |-> NilRound] /\ validValue = [p \in Corr |-> NilValue] /\ validRound = [p \in Corr |-> NilRound] - /\ msgsPropose = [r \in Rounds |-> FaultyProposals(r)] - /\ msgsPrevote = [r \in Rounds |-> FaultyPrevotes(r)] - /\ msgsPrecommit = [r \in Rounds |-> FaultyPrecommits(r)] + /\ msgsPropose \in [Rounds -> SUBSET AllFaultyProposals] + /\ msgsPrevote \in [Rounds -> SUBSET AllFaultyPrevotes] + /\ msgsPrecommit \in [Rounds -> SUBSET AllFaultyPrecommits] + /\ BenignRoundsInMessages(msgsPropose) + /\ BenignRoundsInMessages(msgsPrevote) + /\ BenignRoundsInMessages(msgsPrecommit) /\ evidence = EmptyMsgSet (************************ MESSAGE PASSING ********************************) @@ -400,11 +422,26 @@ AgreementNoAmnesia == 1. There is no fork, that is, Agreement holds true. 2. The amnesic processes have sent equivocal messages. 3. The amnesic processes have sent messages that prove amnesia. + + This property seems to be unrealistic. See AgreementOrEquivocationOrAmnesia. *) -AgreementOrEquivocationOrAmnesia == +AgreementOrEquivocationOrAmnesiaOld == \/ Agreement \/ \A p \in Defective: EquivocationBy(p) \/ \A p \in Defective: AmnesiaBy(p) +(* + The protocol safety. Two cases are possible: + 1. There is no fork, that is, Agreement holds true. + 2. A subset of faulty processes demonstrates equivocation or amnesia. + *) +AgreementOrEquivocationOrAmnesia == + \/ Agreement + \/ \E Detectable \in SUBSET Faulty: + /\ Cardinality(Detectable) >= THRESHOLD1 + /\ \A p \in Detectable: + EquivocationBy(p) \/ AmnesiaBy(p) + + ============================================================================= diff --git a/spec/fork-cases/TendermintAccInv3.tla b/spec/fork-cases/TendermintAccInv3.tla index cebca78..8acd266 100644 --- a/spec/fork-cases/TendermintAccInv3.tla +++ b/spec/fork-cases/TendermintAccInv3.tla @@ -31,12 +31,6 @@ AllPrecommits == src: AllProcs, round: Rounds, id: ValuesOrNil] <: {MT} - -BenignRoundsInMessages(msgfun) == - \* the message function never contains a message for a wrong round - \A r \in Rounds: - \A m \in msgfun[r]: - r = m.round (* the standard type invariant -- importantly, it is inductive *) TypeOK == @@ -309,7 +303,7 @@ LessThanThirdFaulty == N > 3 * T /\ Cardinality(Faulty) <= T THEOREM TypedInvIsInductive == \/ FaultyQuorum \* if there are 2 * T + 1 faulty processes, we give up \//\ Init => TypedInv - /\ TypedInv /\ [Next]_vars => TypedInv + /\ TypedInv /\ [Next]_vars => TypedInv' (* There should be no fork, when there are less than 1/3 faulty processes. From 9f0241caac4316c97a69e48a56f119bd78a0596e Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 1 Apr 2020 07:59:09 +0000 Subject: [PATCH 36/38] new updates --- .../results/001indinv-apalache-card.csv | 60 +-- .../results/001indinv-apalache-mem-log.svg | 362 ++++++------- .../results/001indinv-apalache-mem.svg | 342 ++++++------ .../results/001indinv-apalache-ncells.svg | 256 ++++----- .../results/001indinv-apalache-nclauses.svg | 336 ++++++------ .../results/001indinv-apalache-report.md | 102 ++-- .../results/001indinv-apalache-time-log.svg | 426 ++++++++------- .../results/001indinv-apalache-time.svg | 485 +++++++----------- .../results/001indinv-apalache-unstable.csv | 60 +-- 9 files changed, 1190 insertions(+), 1239 deletions(-) diff --git a/spec/fork-cases/results/001indinv-apalache-card.csv b/spec/fork-cases/results/001indinv-apalache-card.csv index a6edfc8..d9d9be9 100644 --- a/spec/fork-cases/results/001indinv-apalache-card.csv +++ b/spec/fork-cases/results/001indinv-apalache-card.csv @@ -1,31 +1,31 @@ 01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len -1,apalache,NoError,745,1,7575976,0,0,255850,2916339,20 -2,apalache,NoError,703,1,7328636,0,0,242262,2881034,21 -3,apalache,NoError,593,1,8040896,0,0,227457,2854217,21 -4,apalache,NoError,1420,1,10484644,0,0,346159,4446361,21 -5,apalache,NoError,1401,1,11135196,0,0,329305,4393688,21 -6,apalache,NoError,3481,1,13823704,0,0,561290,9239413,20 -7,apalache,NoError,3461,1,13866816,0,0,531012,9216485,21 -8,apalache,NoError,3171,1,13627120,0,0,508797,9012976,21 -9,apalache,NoError,10993,1,17585212,0,0,999840,20562209,20 -10,apalache,NoError,9766,1,17456608,0,0,938833,20059531,20 -11,apalache,NoError,295,0,4634444,0,0,148963,2161260,17 -12,apalache,NoError,439,0,8616344,0,0,1019658,4179230,13 -13,apalache,NoError,410,0,8625148,0,0,1008704,4133605,13 -14,apalache,NoError,483,0,7152440,0,0,212357,3324811,17 -15,apalache,NoError,758,0,13256700,0,0,1571212,6464897,13 -16,apalache,NoError,1183,0,13292156,0,0,374517,7397022,16 -17,apalache,NoError,1732,0,19419708,0,0,3034896,13545586,13 -18,apalache,NoError,1975,0,20509864,0,0,3010964,13451475,13 -19,apalache,NoError,3205,0,16831228,0,0,734069,17072460,16 -20,apalache,NoError,4561,0,29809472,0,0,6158724,29595562,13 -21,apalache,NoError,10,0,657616,0,0,3478,19896,27 -22,apalache,NoError,13,0,701208,0,0,5899,37943,29 -23,apalache,Error,18,0,991180,0,0,7572,54445,30 -24,apalache,NoError,10,0,624420,0,0,3627,22678,29 -25,apalache,NoError,15,0,977908,0,0,6038,42659,31 -26,apalache,NoError,18,0,972612,0,0,6366,52286,34 -27,apalache,NoError,23,0,1152052,0,0,8739,76788,34 -28,apalache,NoError,30,0,1257044,0,0,10931,100893,34 -29,apalache,NoError,30,0,1337804,0,0,9156,97270,37 -30,apalache,NoError,39,0,1511844,0,0,11475,128242,37 +1,apalache,NoError,757,1,7259492,0,0,255850,2916339,20 +2,apalache,NoError,678,1,7000688,0,0,242262,2881034,21 +3,apalache,NoError,619,1,8492688,0,0,227457,2854217,21 +4,apalache,NoError,1701,1,10996996,0,0,346159,4446361,21 +5,apalache,NoError,1248,1,10416676,0,0,329305,4393688,21 +6,apalache,NoError,3446,1,13774864,0,0,561290,9239413,20 +7,apalache,NoError,3462,1,13811440,0,0,531012,9216485,21 +8,apalache,NoError,3232,1,13043816,0,0,508797,9012976,21 +9,apalache,NoError,10230,1,17646764,0,0,999840,20562209,20 +10,apalache,NoError,10016,1,17720944,0,0,938833,20059531,20 +11,apalache,NoError,287,0,5226932,0,0,148963,2161260,17 +12,apalache,NoError,442,0,8893236,0,0,1019658,4179230,13 +13,apalache,NoError,435,0,8888560,0,0,1008704,4133605,13 +14,apalache,NoError,477,0,7516512,0,0,212357,3324811,17 +15,apalache,NoError,761,0,13470172,0,0,1571212,6464897,13 +16,apalache,NoError,1201,0,12939096,0,0,374517,7397022,16 +17,apalache,NoError,1753,0,19291512,0,0,3034896,13545586,13 +18,apalache,NoError,1960,0,20616432,0,0,3010964,13451475,13 +19,apalache,NoError,3105,0,17102116,0,0,734069,17072460,16 +20,apalache,NoError,4581,0,29702796,0,0,6158724,29595562,13 +21,apalache,NoError,9,0,632024,0,0,3478,19896,27 +22,apalache,NoError,13,0,734020,0,0,5899,37943,29 +23,apalache,Error,17,0,961152,0,0,7572,54445,30 +24,apalache,NoError,10,0,660140,0,0,3627,22678,29 +25,apalache,NoError,15,0,950852,0,0,6038,42659,31 +26,apalache,NoError,18,0,916716,0,0,6366,52286,34 +27,apalache,NoError,24,0,1069808,0,0,8739,76788,34 +28,apalache,NoError,31,0,1266180,0,0,10931,100893,34 +29,apalache,NoError,31,0,1302436,0,0,9156,97270,37 +30,apalache,NoError,39,0,1543612,0,0,11475,128242,37 diff --git a/spec/fork-cases/results/001indinv-apalache-mem-log.svg b/spec/fork-cases/results/001indinv-apalache-mem-log.svg index 3d993e2..9873023 100644 --- a/spec/fork-cases/results/001indinv-apalache-mem-log.svg +++ b/spec/fork-cases/results/001indinv-apalache-mem-log.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m039139ba17" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="m6c32001f98" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -554,7 +554,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -563,13 +563,13 @@ z - - + @@ -585,7 +585,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -597,101 +597,101 @@ z +" id="md32f6290b2" style="stroke:#000000;stroke-width:0.6;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -882,36 +882,36 @@ z - +" id="m6c33bfd5e8" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="m586313e175" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1054,26 +1054,26 @@ L 414.72 41.472 - - - + @@ -1199,7 +1199,7 @@ L 9.28125 70.21875 z " id="DejaVuSans-74"/> - + @@ -1230,18 +1230,18 @@ z - - + - + @@ -1271,7 +1271,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-mem.svg b/spec/fork-cases/results/001indinv-apalache-mem.svg index 51eb45e..b0c6a9c 100644 --- a/spec/fork-cases/results/001indinv-apalache-mem.svg +++ b/spec/fork-cases/results/001indinv-apalache-mem.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m450618c4ab" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="m1d0050902b" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -530,7 +530,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -539,18 +539,18 @@ z - - + - + @@ -559,18 +559,18 @@ L 414.72 259.218052 - - + - + @@ -579,18 +579,18 @@ L 414.72 217.772178 - - + - + @@ -599,18 +599,18 @@ L 414.72 176.326303 - - + - + @@ -619,18 +619,18 @@ L 414.72 134.880429 - - + - + @@ -639,18 +639,18 @@ L 414.72 93.434555 - - + - + @@ -783,36 +783,36 @@ z - +" id="m8765b99f92" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="m76f9779b83" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -974,7 +974,7 @@ L 86.6 54.570437 - + @@ -1174,7 +1174,7 @@ L 86.6 69.248562 - + @@ -1209,7 +1209,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-ncells.svg b/spec/fork-cases/results/001indinv-apalache-ncells.svg index 8b24e52..95272ef 100644 --- a/spec/fork-cases/results/001indinv-apalache-ncells.svg +++ b/spec/fork-cases/results/001indinv-apalache-ncells.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="me3cfc41533" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,7 +506,7 @@ z - @@ -514,10 +514,10 @@ L 414.72 295.624696 +" id="m5bb26e85c6" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -529,13 +529,13 @@ L -3.5 0 - - + @@ -553,13 +553,13 @@ L 414.72 256.321637 - - + @@ -577,13 +577,13 @@ L 414.72 217.018577 - - + @@ -601,13 +601,13 @@ L 414.72 177.715517 - - + @@ -644,13 +644,13 @@ z - - + @@ -668,13 +668,13 @@ L 414.72 99.109398 - - + @@ -852,36 +852,36 @@ z - +" id="mf92ead8c91" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="m721ad2ea2f" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1043,7 +1043,7 @@ L 86.6 54.570437 - + @@ -1185,7 +1185,7 @@ L 86.6 69.248562 - + @@ -1220,7 +1220,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-nclauses.svg b/spec/fork-cases/results/001indinv-apalache-nclauses.svg index 2f3f273..30a39ae 100644 --- a/spec/fork-cases/results/001indinv-apalache-nclauses.svg +++ b/spec/fork-cases/results/001indinv-apalache-nclauses.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="mabee5ede2e" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="m2bc0f77fc9" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -530,7 +530,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -539,18 +539,18 @@ z - - + - + @@ -559,18 +559,18 @@ L 414.72 254.752256 - - + - + @@ -579,18 +579,18 @@ L 414.72 213.853769 - - + - + @@ -599,18 +599,18 @@ L 414.72 172.955281 - - + - + @@ -619,18 +619,18 @@ L 414.72 132.056794 - - + - + @@ -639,18 +639,18 @@ L 414.72 91.158307 - - + - + @@ -862,36 +862,36 @@ z - +" id="ma20819ae98" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="mdaa82524a8" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1053,7 +1053,7 @@ L 86.6 54.570437 - + @@ -1195,7 +1195,7 @@ L 86.6 69.248562 - + @@ -1230,7 +1230,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-report.md b/spec/fork-cases/results/001indinv-apalache-report.md index 313c802..45e493d 100644 --- a/spec/fork-cases/results/001indinv-apalache-report.md +++ b/spec/fork-cases/results/001indinv-apalache-report.md @@ -66,68 +66,68 @@ no | filename | tool | timeout | init | inv 01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len -------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- -1 | apalache | NoError | 12m | 1 | 7.0GB | 0 | 0 | 255K | 2.0M | 20 -2 | apalache | NoError | 11m | 1 | 7.0GB | 0 | 0 | 242K | 2.0M | 21 -3 | apalache | NoError | 9m09s | 1 | 7.0GB | 0 | 0 | 227K | 2.0M | 21 -4 | apalache | NoError | 26m | 1 | 10GB | 0 | 0 | 346K | 4.0M | 21 -5 | apalache | NoError | 21m | 1 | 10GB | 0 | 0 | 329K | 4.0M | 21 -6 | apalache | NoError | 56m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 -7 | apalache | NoError | 53m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 -8 | apalache | NoError | 50m | 1 | 13GB | 0 | 0 | 508K | 9.0M | 21 -9 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 -10 | apalache | NoError | 3h03m | 1 | 16GB | 0 | 0 | 938K | 20M | 20 -11 | apalache | NoError | 4m04s | 0 | 4.0GB | 0 | 0 | 148K | 2.0M | 17 -12 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -13 | apalache | NoError | 6m06s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -14 | apalache | NoError | 8m08s | 0 | 6.0GB | 0 | 0 | 212K | 3.0M | 17 -15 | apalache | NoError | 12m | 0 | 13GB | 0 | 0 | 1.0M | 6.0M | 13 -16 | apalache | NoError | 20m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 -17 | apalache | NoError | 28m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 -18 | apalache | NoError | 32m | 0 | 20GB | 0 | 0 | 3.0M | 13M | 13 -19 | apalache | NoError | 53m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 -20 | apalache | NoError | 1h01m | 0 | 28GB | 0 | 0 | 6.0M | 29M | 13 -21 | apalache | NoError | 10s | 0 | 622MB | 0 | 0 | 3.0K | 19K | 27 -22 | apalache | NoError | 13s | 0 | 778MB | 0 | 0 | 5.0K | 37K | 29 -23 | apalache | Error | 18s | 0 | 888MB | 0 | 0 | 7.0K | 54K | 30 -24 | apalache | NoError | 10s | 0 | 651MB | 0 | 0 | 3.0K | 22K | 29 -25 | apalache | NoError | 15s | 0 | 929MB | 0 | 0 | 6.0K | 42K | 31 -26 | apalache | NoError | 18s | 0 | 930MB | 0 | 0 | 6.0K | 52K | 34 -27 | apalache | NoError | 24s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 -28 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 -29 | apalache | NoError | 32s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 -30 | apalache | NoError | 39s | 0 | 1.0GB | 0 | 0 | 11K | 128K | 37 +1 | apalache | NoError | 20m | 1 | 9.0GB | 0 | 0 | 330K | 2.0M | 25 +2 | apalache | NoError | 20m | 1 | 8.0GB | 0 | 0 | 313K | 2.0M | 25 +3 | apalache | NoError | 19m | 1 | 8.0GB | 0 | 0 | 299K | 2.0M | 25 +4 | apalache | NoError | 40m | 1 | 12GB | 0 | 0 | 461K | 3.0M | 26 +5 | apalache | NoError | 38m | 1 | 11GB | 0 | 0 | 445K | 3.0M | 27 +6 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 780K | 6.0M | 28 +7 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 756K | 6.0M | 29 +8 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 728K | 6.0M | 29 +9 | apalache | NoError | 11h | 1 | 20GB | 0 | 0 | 1.0M | 13M | 31 +10 | apalache | NoError | 11h | 1 | 20GB | 0 | 0 | 1.0M | 13M | 32 +11 | apalache | NoError | 6m06s | 0 | 5.0GB | 0 | 0 | 219K | 1.0M | 20 +12 | apalache | NoError | 10m | 0 | 9.0GB | 0 | 0 | 1.0M | 3.0M | 14 +13 | apalache | NoError | 9m09s | 0 | 8.0GB | 0 | 0 | 1.0M | 3.0M | 14 +14 | apalache | NoError | 12m | 0 | 9.0GB | 0 | 0 | 326K | 2.0M | 22 +15 | apalache | NoError | 17m | 0 | 14GB | 0 | 0 | 1.0M | 5.0M | 14 +16 | apalache | NoError | 39m | 0 | 14GB | 0 | 0 | 592K | 5.0M | 24 +17 | apalache | NoError | 52m | 0 | 20GB | 0 | 0 | 3.0M | 11M | 15 +18 | apalache | NoError | 53m | 0 | 20GB | 0 | 0 | 3.0M | 11M | 16 +19 | apalache | NoError | 2h02m | 0 | 19GB | 0 | 0 | 1.0M | 10M | 27 +20 | apalache | Fail | 2h02m | 0 | 30GB | 0 | 0 | 564K | 4.0M | 28 +21 | apalache | NoError | 10s | 0 | 644MB | 0 | 0 | 3.0K | 19K | 27 +22 | apalache | NoError | 13s | 0 | 695MB | 0 | 0 | 5.0K | 37K | 29 +23 | apalache | Error | 18s | 0 | 1021MB | 0 | 0 | 7.0K | 54K | 30 +24 | apalache | NoError | 10s | 0 | 657MB | 0 | 0 | 3.0K | 22K | 29 +25 | apalache | NoError | 15s | 0 | 956MB | 0 | 0 | 6.0K | 42K | 31 +26 | apalache | NoError | 18s | 0 | 908MB | 0 | 0 | 6.0K | 52K | 34 +27 | apalache | NoError | 23s | 0 | 1012MB | 0 | 0 | 8.0K | 76K | 34 +28 | apalache | NoError | 28s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 +29 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 +30 | apalache | NoError | 38s | 0 | 1.0GB | 0 | 0 | 11K | 127K | 37 ## 4. Detailed results: 001indinv-apalache-card.csv 01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len -------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- -1 | apalache | NoError | 12m | 1 | 7.0GB | 0 | 0 | 255K | 2.0M | 20 +1 | apalache | NoError | 12m | 1 | 6.0GB | 0 | 0 | 255K | 2.0M | 20 2 | apalache | NoError | 11m | 1 | 6.0GB | 0 | 0 | 242K | 2.0M | 21 -3 | apalache | NoError | 9m09s | 1 | 7.0GB | 0 | 0 | 227K | 2.0M | 21 -4 | apalache | NoError | 23m | 1 | 9.0GB | 0 | 0 | 346K | 4.0M | 21 -5 | apalache | NoError | 23m | 1 | 10GB | 0 | 0 | 329K | 4.0M | 21 -6 | apalache | NoError | 58m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 +3 | apalache | NoError | 10m | 1 | 8.0GB | 0 | 0 | 227K | 2.0M | 21 +4 | apalache | NoError | 28m | 1 | 10GB | 0 | 0 | 346K | 4.0M | 21 +5 | apalache | NoError | 20m | 1 | 9.0GB | 0 | 0 | 329K | 4.0M | 21 +6 | apalache | NoError | 57m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 7 | apalache | NoError | 57m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 -8 | apalache | NoError | 52m | 1 | 12GB | 0 | 0 | 508K | 9.0M | 21 -9 | apalache | NoError | 3h03m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 +8 | apalache | NoError | 53m | 1 | 12GB | 0 | 0 | 508K | 9.0M | 21 +9 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 10 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 938K | 20M | 20 11 | apalache | NoError | 4m04s | 0 | 4.0GB | 0 | 0 | 148K | 2.0M | 17 12 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -13 | apalache | NoError | 6m06s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -14 | apalache | NoError | 8m08s | 0 | 6.0GB | 0 | 0 | 212K | 3.0M | 17 +13 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 +14 | apalache | NoError | 7m07s | 0 | 7.0GB | 0 | 0 | 212K | 3.0M | 17 15 | apalache | NoError | 12m | 0 | 12GB | 0 | 0 | 1.0M | 6.0M | 13 -16 | apalache | NoError | 19m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 -17 | apalache | NoError | 28m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 +16 | apalache | NoError | 20m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 +17 | apalache | NoError | 29m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 18 | apalache | NoError | 32m | 0 | 19GB | 0 | 0 | 3.0M | 13M | 13 -19 | apalache | NoError | 53m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 +19 | apalache | NoError | 51m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 20 | apalache | NoError | 1h01m | 0 | 28GB | 0 | 0 | 6.0M | 29M | 13 -21 | apalache | NoError | 10s | 0 | 642MB | 0 | 0 | 3.0K | 19K | 27 -22 | apalache | NoError | 13s | 0 | 684MB | 0 | 0 | 5.0K | 37K | 29 -23 | apalache | Error | 18s | 0 | 967MB | 0 | 0 | 7.0K | 54K | 30 -24 | apalache | NoError | 10s | 0 | 609MB | 0 | 0 | 3.0K | 22K | 29 -25 | apalache | NoError | 15s | 0 | 954MB | 0 | 0 | 6.0K | 42K | 31 -26 | apalache | NoError | 18s | 0 | 949MB | 0 | 0 | 6.0K | 52K | 34 -27 | apalache | NoError | 23s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 -28 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 -29 | apalache | NoError | 30s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 +21 | apalache | NoError | 9s | 0 | 617MB | 0 | 0 | 3.0K | 19K | 27 +22 | apalache | NoError | 13s | 0 | 716MB | 0 | 0 | 5.0K | 37K | 29 +23 | apalache | Error | 17s | 0 | 938MB | 0 | 0 | 7.0K | 54K | 30 +24 | apalache | NoError | 10s | 0 | 644MB | 0 | 0 | 3.0K | 22K | 29 +25 | apalache | NoError | 15s | 0 | 928MB | 0 | 0 | 6.0K | 42K | 31 +26 | apalache | NoError | 18s | 0 | 895MB | 0 | 0 | 6.0K | 52K | 34 +27 | apalache | NoError | 24s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 +28 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 +29 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 30 | apalache | NoError | 39s | 0 | 1.0GB | 0 | 0 | 11K | 128K | 37 diff --git a/spec/fork-cases/results/001indinv-apalache-time-log.svg b/spec/fork-cases/results/001indinv-apalache-time-log.svg index 4c4704c..4dc352e 100644 --- a/spec/fork-cases/results/001indinv-apalache-time-log.svg +++ b/spec/fork-cases/results/001indinv-apalache-time-log.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="md09fdb25ec" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,23 +506,23 @@ z - +" id="m6b739387ef" style="stroke:#000000;stroke-width:0.8;"/> - + - + @@ -531,18 +531,18 @@ L -3.5 0 - - + - + @@ -551,18 +551,18 @@ L 414.72 215.950654 - - + - + @@ -571,13 +571,13 @@ L 414.72 136.413308 - - + @@ -601,7 +601,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -613,192 +613,234 @@ z +" id="m3bbbdcacfa" style="stroke:#000000;stroke-width:0.6;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -960,37 +1002,37 @@ z - - + +" id="m084fb54101" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" id="md923ec9729" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1145,14 +1187,14 @@ Q 228.209062 78.82825 230.209062 78.82825 z " style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/> - + - + - + @@ -1277,14 +1319,14 @@ z - + - + - + @@ -1319,7 +1361,7 @@ L 252.209062 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-time.svg b/spec/fork-cases/results/001indinv-apalache-time.svg index 2646607..116bb58 100644 --- a/spec/fork-cases/results/001indinv-apalache-time.svg +++ b/spec/fork-cases/results/001indinv-apalache-time.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="mace6c30a4e" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,218 +506,127 @@ z - +" id="m25992ced1c" style="stroke:#000000;stroke-width:0.8;"/> - + - + - - + - - - + + + + - - + - - - - - - + + + + - - + - - - - - - + + + + - - + - + - +" id="DejaVuSans-34"/> - - - - - - - - - - - - - - - - - - - - - + + @@ -725,7 +634,7 @@ L 414.72 75.680742 - + - + +" id="m7c836b05ae" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" id="m15cc48268b" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -998,17 +907,17 @@ Q 228.209062 78.82825 230.209062 78.82825 z " style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/> - + - + - + - + - + - + - + - + @@ -1178,7 +1087,7 @@ L 252.209062 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-unstable.csv b/spec/fork-cases/results/001indinv-apalache-unstable.csv index 6de18d0..8db9de2 100644 --- a/spec/fork-cases/results/001indinv-apalache-unstable.csv +++ b/spec/fork-cases/results/001indinv-apalache-unstable.csv @@ -1,31 +1,31 @@ 01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len -1,apalache,NoError,765,1,7449524,0,0,255850,2916339,20 -2,apalache,NoError,712,1,7953452,0,0,242262,2881034,21 -3,apalache,NoError,573,1,8108036,0,0,227457,2854217,21 -4,apalache,NoError,1611,1,11197492,0,0,346159,4446361,21 -5,apalache,NoError,1294,1,11006544,0,0,329305,4393688,21 -6,apalache,NoError,3366,1,13817260,0,0,561290,9239413,20 -7,apalache,NoError,3190,1,13851528,0,0,531012,9216485,21 -8,apalache,NoError,3052,1,13713040,0,0,508797,9012976,21 -9,apalache,NoError,9752,1,17815528,0,0,999840,20562209,20 -10,apalache,NoError,11005,1,17483056,0,0,938833,20059531,20 -11,apalache,NoError,295,0,5232284,0,0,148963,2161260,17 -12,apalache,NoError,441,0,9130148,0,0,1019658,4179230,13 -13,apalache,NoError,412,0,8666908,0,0,1008704,4133605,13 -14,apalache,NoError,484,0,7093124,0,0,212357,3324811,17 -15,apalache,NoError,768,0,13713572,0,0,1571212,6464897,13 -16,apalache,NoError,1219,0,13155580,0,0,374517,7397022,16 -17,apalache,NoError,1732,0,19315084,0,0,3034896,13545586,13 -18,apalache,NoError,1943,0,21061592,0,0,3010964,13451475,13 -19,apalache,NoError,3210,0,17087572,0,0,734069,17072460,16 -20,apalache,NoError,4591,0,29649044,0,0,6158724,29595562,13 -21,apalache,NoError,10,0,637636,0,0,3478,19896,27 -22,apalache,NoError,13,0,796672,0,0,5899,37943,29 -23,apalache,Error,18,0,910176,0,0,7572,54445,30 -24,apalache,NoError,10,0,667284,0,0,3627,22678,29 -25,apalache,NoError,15,0,952296,0,0,6038,42659,31 -26,apalache,NoError,18,0,952360,0,0,6366,52286,34 -27,apalache,NoError,24,0,1094008,0,0,8739,76788,34 -28,apalache,NoError,30,0,1347008,0,0,10931,100893,34 -29,apalache,NoError,32,0,1211532,0,0,9156,97270,37 -30,apalache,NoError,39,0,1603728,0,0,11475,128242,37 +1,apalache,NoError,1255,1,9542160,0,0,330701,2507564,25 +2,apalache,NoError,1238,1,8935616,0,0,313983,2424451,25 +3,apalache,NoError,1159,1,9094360,0,0,299480,2379006,25 +4,apalache,NoError,2420,1,12632632,0,0,461690,3765633,26 +5,apalache,NoError,2311,1,12533920,0,0,445753,3695822,27 +6,apalache,NoError,9747,1,15322452,0,0,780909,6900742,28 +7,apalache,NoError,9790,1,15556308,0,0,756257,6935204,29 +8,apalache,NoError,9405,1,15239236,0,0,728499,6679564,29 +9,apalache,NoError,42452,1,21365376,0,0,1449412,13551573,31 +10,apalache,NoError,40977,1,21199800,0,0,1391910,13310244,32 +11,apalache,NoError,390,0,6022364,0,0,219433,1722072,20 +12,apalache,NoError,631,0,9797272,0,0,1092882,3722702,14 +13,apalache,NoError,546,0,8661600,0,0,1081766,3678097,14 +14,apalache,NoError,768,0,9542188,0,0,326597,2646451,22 +15,apalache,NoError,1077,0,14964756,0,0,1689022,5765117,14 +16,apalache,NoError,2374,0,14718576,0,0,592163,5025992,24 +17,apalache,NoError,3140,0,21281836,0,0,3257404,11121278,15 +18,apalache,NoError,3210,0,21715468,0,0,3233186,11030301,16 +19,apalache,NoError,9147,0,20206236,0,0,1179739,10135524,27 +20,apalache,Fail,9438,0,32188224,0,0,564069,4984843,28 +21,apalache,NoError,10,0,659828,0,0,3486,19875,27 +22,apalache,NoError,13,0,711984,0,0,5912,37834,29 +23,apalache,Error,18,0,1046452,0,0,7594,54240,30 +24,apalache,NoError,10,0,673520,0,0,3635,22657,29 +25,apalache,NoError,15,0,979072,0,0,6052,42572,31 +26,apalache,NoError,18,0,930304,0,0,6376,52176,34 +27,apalache,NoError,23,0,1037284,0,0,8759,76583,34 +28,apalache,NoError,28,0,1237168,0,0,10960,100556,34 +29,apalache,NoError,31,0,1218064,0,0,9172,97055,37 +30,apalache,NoError,38,0,1501664,0,0,11501,127902,37 From 01268e2c9c5c6b35ea2e530ed4b1f81517d11b54 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Wed, 1 Apr 2020 10:06:02 +0200 Subject: [PATCH 37/38] link to the report --- spec/fork-cases/README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/fork-cases/README.md b/spec/fork-cases/README.md index bad68cd..2e0447d 100644 --- a/spec/fork-cases/README.md +++ b/spec/fork-cases/README.md @@ -56,7 +56,7 @@ Formally, the statement looks as follows: THEOREM TypedInvIsInductive == \/ FaultyQuorum \//\ Init => TypedInv - /\ TypedInv /\ [Next]_vars => TypedInv + /\ TypedInv /\ [Next]_vars => TypedInv' ``` When over two-thirds of processes are faulty, `TypedInv` is not inductive. @@ -85,7 +85,9 @@ THEOREM AgreementOrFork == # Model checking results -TODO +Check the report on [model checking with Apalache](./results/001indinv-apalache-report.md). + +TODO: re-run the experiments with the updates introduced during the dev session. # Running the experiments From 54dd97634f9450a4ea32e1f611fa37f3fafe83c3 Mon Sep 17 00:00:00 2001 From: Igor Konnov Date: Fri, 3 Apr 2020 21:02:51 +0000 Subject: [PATCH 38/38] updated results --- .../results/001indinv-apalache-card.csv | 60 +-- .../results/001indinv-apalache-mem-log.svg | 347 ++++++++------- .../results/001indinv-apalache-mem.svg | 338 +++++++------- .../results/001indinv-apalache-ncells.svg | 370 ++++++++-------- .../results/001indinv-apalache-nclauses.svg | 414 ++++++++---------- .../results/001indinv-apalache-report.md | 102 ++--- .../results/001indinv-apalache-time-log.svg | 404 +++++++++-------- .../results/001indinv-apalache-time.svg | 322 +++++++------- .../results/001indinv-apalache-unstable.csv | 60 +-- 9 files changed, 1185 insertions(+), 1232 deletions(-) diff --git a/spec/fork-cases/results/001indinv-apalache-card.csv b/spec/fork-cases/results/001indinv-apalache-card.csv index d9d9be9..9f10f57 100644 --- a/spec/fork-cases/results/001indinv-apalache-card.csv +++ b/spec/fork-cases/results/001indinv-apalache-card.csv @@ -1,31 +1,31 @@ 01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len -1,apalache,NoError,757,1,7259492,0,0,255850,2916339,20 -2,apalache,NoError,678,1,7000688,0,0,242262,2881034,21 -3,apalache,NoError,619,1,8492688,0,0,227457,2854217,21 -4,apalache,NoError,1701,1,10996996,0,0,346159,4446361,21 -5,apalache,NoError,1248,1,10416676,0,0,329305,4393688,21 -6,apalache,NoError,3446,1,13774864,0,0,561290,9239413,20 -7,apalache,NoError,3462,1,13811440,0,0,531012,9216485,21 -8,apalache,NoError,3232,1,13043816,0,0,508797,9012976,21 -9,apalache,NoError,10230,1,17646764,0,0,999840,20562209,20 -10,apalache,NoError,10016,1,17720944,0,0,938833,20059531,20 -11,apalache,NoError,287,0,5226932,0,0,148963,2161260,17 -12,apalache,NoError,442,0,8893236,0,0,1019658,4179230,13 -13,apalache,NoError,435,0,8888560,0,0,1008704,4133605,13 -14,apalache,NoError,477,0,7516512,0,0,212357,3324811,17 -15,apalache,NoError,761,0,13470172,0,0,1571212,6464897,13 -16,apalache,NoError,1201,0,12939096,0,0,374517,7397022,16 -17,apalache,NoError,1753,0,19291512,0,0,3034896,13545586,13 -18,apalache,NoError,1960,0,20616432,0,0,3010964,13451475,13 -19,apalache,NoError,3105,0,17102116,0,0,734069,17072460,16 -20,apalache,NoError,4581,0,29702796,0,0,6158724,29595562,13 -21,apalache,NoError,9,0,632024,0,0,3478,19896,27 -22,apalache,NoError,13,0,734020,0,0,5899,37943,29 -23,apalache,Error,17,0,961152,0,0,7572,54445,30 -24,apalache,NoError,10,0,660140,0,0,3627,22678,29 -25,apalache,NoError,15,0,950852,0,0,6038,42659,31 -26,apalache,NoError,18,0,916716,0,0,6366,52286,34 -27,apalache,NoError,24,0,1069808,0,0,8739,76788,34 -28,apalache,NoError,31,0,1266180,0,0,10931,100893,34 -29,apalache,NoError,31,0,1302436,0,0,9156,97270,37 -30,apalache,NoError,39,0,1543612,0,0,11475,128242,37 +1,apalache,NoError,757,1,6689920,0,0,255850,2916339,20 +2,apalache,NoError,715,1,7178440,0,0,241134,2878496,21 +3,apalache,NoError,580,1,7177956,0,0,227772,2854964,21 +4,apalache,NoError,1492,1,10086952,0,0,346159,4446361,21 +5,apalache,NoError,1258,1,10645452,0,0,329305,4393688,21 +6,apalache,NoError,3344,1,13707364,0,0,560069,9236533,20 +7,apalache,NoError,3288,1,13834196,0,0,531012,9216485,21 +8,apalache,NoError,2927,1,13611468,0,0,508797,9012976,21 +9,apalache,NoError,11050,1,17577780,0,0,1002795,20568977,20 +10,apalache,NoError,9356,1,17517792,0,0,940114,20062564,20 +11,apalache,NoError,286,0,4137892,0,0,148963,2161260,17 +12,apalache,NoError,849,0,17076088,0,0,2789102,8311118,10 +13,apalache,NoError,1435,0,23125812,0,0,6317013,16529247,10 +14,apalache,NoError,468,0,5417264,0,0,212357,3324811,17 +15,apalache,NoError,1397,0,21924168,0,0,4326480,12872129,10 +16,apalache,NoError,1242,0,9435876,0,0,374517,7397022,16 +17,apalache,Fail,2612,0,27951008,0,0,172254,3647560,17 +18,apalache,Fail,2625,0,28354920,0,0,160287,3600511,17 +19,apalache,NoError,3172,0,16896056,0,0,734069,17072460,16 +20,apalache,Fail,3873,0,30181108,0,0,341439,8450107,16 +21,apalache,NoError,12,0,594592,0,0,5622,33845,23 +22,apalache,NoError,17,0,860608,0,0,9690,68116,23 +23,apalache,NoError,25,0,1062868,0,0,12757,104964,22 +24,apalache,NoError,13,0,766340,0,0,5784,36940,25 +25,apalache,NoError,20,0,810136,0,0,9852,73824,25 +26,apalache,NoError,24,0,1034140,0,0,10220,85339,28 +27,apalache,NoError,33,0,1105020,0,0,14267,131054,28 +28,apalache,NoError,44,0,1365304,0,0,18352,180731,27 +29,apalache,NoError,45,0,1349440,0,0,14797,155936,32 +30,apalache,NoError,55,0,1755904,0,0,18844,213416,31 diff --git a/spec/fork-cases/results/001indinv-apalache-mem-log.svg b/spec/fork-cases/results/001indinv-apalache-mem-log.svg index 9873023..9864b56 100644 --- a/spec/fork-cases/results/001indinv-apalache-mem-log.svg +++ b/spec/fork-cases/results/001indinv-apalache-mem-log.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m3cf5ae2b6e" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="mc596d7e99e" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -554,7 +554,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -563,13 +563,13 @@ z - - + @@ -585,7 +585,7 @@ L 8.203125 64.59375 z " id="DejaVuSans-37"/> - + @@ -597,101 +597,108 @@ z +" id="m8894847c4b" style="stroke:#000000;stroke-width:0.6;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + @@ -881,37 +888,37 @@ z - - + +" id="m891411d55d" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" id="m5edc4157a9" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1066,14 +1073,14 @@ Q 62.6 302.584 64.6 302.584 z " style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/> - + - + - + @@ -1229,14 +1236,14 @@ z - + - + - + @@ -1271,7 +1278,7 @@ L 86.6 293.004312 - + diff --git a/spec/fork-cases/results/001indinv-apalache-mem.svg b/spec/fork-cases/results/001indinv-apalache-mem.svg index b0c6a9c..b4b5b4b 100644 --- a/spec/fork-cases/results/001indinv-apalache-mem.svg +++ b/spec/fork-cases/results/001indinv-apalache-mem.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m4156316fc9" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="m6256ac7755" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -530,7 +530,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -539,18 +539,18 @@ z - - + - + @@ -559,18 +559,18 @@ L 414.72 262.001688 - - + - + @@ -579,18 +579,18 @@ L 414.72 223.670077 - - + - + @@ -599,18 +599,18 @@ L 414.72 185.338465 - - + - + @@ -619,18 +619,18 @@ L 414.72 147.006854 - - + - + @@ -639,18 +639,18 @@ L 414.72 108.675242 - - + - + @@ -783,36 +783,36 @@ z - +" id="m30e98023d4" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="mac1ff82722" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -974,7 +974,7 @@ L 86.6 54.570437 - + @@ -1174,7 +1174,7 @@ L 86.6 69.248562 - + @@ -1209,7 +1209,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-ncells.svg b/spec/fork-cases/results/001indinv-apalache-ncells.svg index 95272ef..184a5f8 100644 --- a/spec/fork-cases/results/001indinv-apalache-ncells.svg +++ b/spec/fork-cases/results/001indinv-apalache-ncells.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="med40f42f76" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,41 +506,41 @@ z - +" id="me36ae73ad0" style="stroke:#000000;stroke-width:0.8;"/> - + - + - - + - + @@ -553,18 +553,18 @@ L 414.72 256.321637 - - + - + @@ -577,18 +577,18 @@ L 414.72 217.018577 - - + - + @@ -601,13 +601,13 @@ L 414.72 177.715517 - - + @@ -631,7 +631,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -644,18 +644,18 @@ z - - + - + @@ -668,13 +668,13 @@ L 414.72 99.109398 - - + @@ -711,7 +711,7 @@ Q 48.484375 72.75 52.59375 71.296875 z " id="DejaVuSans-36"/> - + @@ -852,36 +852,36 @@ z - +" id="m12d7f92912" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="m8e6576dace" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1024,26 +1024,26 @@ L 414.72 41.472 - - - + @@ -1148,7 +1148,7 @@ L 9.28125 70.21875 z " id="DejaVuSans-74"/> - + @@ -1179,18 +1179,18 @@ z - - + - + @@ -1220,7 +1220,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-nclauses.svg b/spec/fork-cases/results/001indinv-apalache-nclauses.svg index 30a39ae..67e655f 100644 --- a/spec/fork-cases/results/001indinv-apalache-nclauses.svg +++ b/spec/fork-cases/results/001indinv-apalache-nclauses.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m56a3ba3ac8" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,18 +506,18 @@ z - +" id="m8a71828d2a" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -530,7 +530,7 @@ L 10.6875 0 z " id="DejaVuSans-2e"/> - + @@ -539,18 +539,18 @@ z - - + - + @@ -559,18 +559,18 @@ L 414.72 254.752113 - - + - + @@ -579,18 +579,18 @@ L 414.72 213.853655 - - + - + @@ -599,65 +599,25 @@ L 414.72 172.955197 - - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + +" id="m7bf399d924" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" id="m409a4b43f6" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1034,29 +994,29 @@ L 414.72 41.472 - - - + - + - + - + - + @@ -1188,19 +1148,19 @@ z - - + - + - + - + - + @@ -1230,7 +1190,7 @@ L 86.6 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-report.md b/spec/fork-cases/results/001indinv-apalache-report.md index 45e493d..5bad2b0 100644 --- a/spec/fork-cases/results/001indinv-apalache-report.md +++ b/spec/fork-cases/results/001indinv-apalache-report.md @@ -66,68 +66,68 @@ no | filename | tool | timeout | init | inv 01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len -------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- -1 | apalache | NoError | 20m | 1 | 9.0GB | 0 | 0 | 330K | 2.0M | 25 +1 | apalache | NoError | 21m | 1 | 8.0GB | 0 | 0 | 330K | 2.0M | 25 2 | apalache | NoError | 20m | 1 | 8.0GB | 0 | 0 | 313K | 2.0M | 25 -3 | apalache | NoError | 19m | 1 | 8.0GB | 0 | 0 | 299K | 2.0M | 25 -4 | apalache | NoError | 40m | 1 | 12GB | 0 | 0 | 461K | 3.0M | 26 -5 | apalache | NoError | 38m | 1 | 11GB | 0 | 0 | 445K | 3.0M | 27 +3 | apalache | NoError | 19m | 1 | 7.0GB | 0 | 0 | 299K | 2.0M | 25 +4 | apalache | NoError | 39m | 1 | 11GB | 0 | 0 | 461K | 3.0M | 26 +5 | apalache | NoError | 40m | 1 | 11GB | 0 | 0 | 445K | 3.0M | 27 6 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 780K | 6.0M | 28 -7 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 756K | 6.0M | 29 +7 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 754K | 6.0M | 29 8 | apalache | NoError | 2h02m | 1 | 14GB | 0 | 0 | 728K | 6.0M | 29 9 | apalache | NoError | 11h | 1 | 20GB | 0 | 0 | 1.0M | 13M | 31 10 | apalache | NoError | 11h | 1 | 20GB | 0 | 0 | 1.0M | 13M | 32 11 | apalache | NoError | 6m06s | 0 | 5.0GB | 0 | 0 | 219K | 1.0M | 20 -12 | apalache | NoError | 10m | 0 | 9.0GB | 0 | 0 | 1.0M | 3.0M | 14 -13 | apalache | NoError | 9m09s | 0 | 8.0GB | 0 | 0 | 1.0M | 3.0M | 14 -14 | apalache | NoError | 12m | 0 | 9.0GB | 0 | 0 | 326K | 2.0M | 22 -15 | apalache | NoError | 17m | 0 | 14GB | 0 | 0 | 1.0M | 5.0M | 14 -16 | apalache | NoError | 39m | 0 | 14GB | 0 | 0 | 592K | 5.0M | 24 -17 | apalache | NoError | 52m | 0 | 20GB | 0 | 0 | 3.0M | 11M | 15 -18 | apalache | NoError | 53m | 0 | 20GB | 0 | 0 | 3.0M | 11M | 16 +12 | apalache | NoError | 17m | 0 | 17GB | 0 | 0 | 2.0M | 7.0M | 11 +13 | apalache | NoError | 25m | 0 | 22GB | 0 | 0 | 6.0M | 15M | 10 +14 | apalache | NoError | 11m | 0 | 7.0GB | 0 | 0 | 326K | 2.0M | 22 +15 | apalache | NoError | 31m | 0 | 21GB | 0 | 0 | 4.0M | 12M | 11 +16 | apalache | NoError | 39m | 0 | 13GB | 0 | 0 | 592K | 5.0M | 24 +17 | apalache | Fail | 1h01m | 0 | 27GB | 0 | 0 | 280K | 2.0M | 24 +18 | apalache | Fail | 1h01m | 0 | 27GB | 0 | 0 | 268K | 2.0M | 24 19 | apalache | NoError | 2h02m | 0 | 19GB | 0 | 0 | 1.0M | 10M | 27 20 | apalache | Fail | 2h02m | 0 | 30GB | 0 | 0 | 564K | 4.0M | 28 -21 | apalache | NoError | 10s | 0 | 644MB | 0 | 0 | 3.0K | 19K | 27 -22 | apalache | NoError | 13s | 0 | 695MB | 0 | 0 | 5.0K | 37K | 29 -23 | apalache | Error | 18s | 0 | 1021MB | 0 | 0 | 7.0K | 54K | 30 -24 | apalache | NoError | 10s | 0 | 657MB | 0 | 0 | 3.0K | 22K | 29 -25 | apalache | NoError | 15s | 0 | 956MB | 0 | 0 | 6.0K | 42K | 31 -26 | apalache | NoError | 18s | 0 | 908MB | 0 | 0 | 6.0K | 52K | 34 -27 | apalache | NoError | 23s | 0 | 1012MB | 0 | 0 | 8.0K | 76K | 34 -28 | apalache | NoError | 28s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 -29 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 -30 | apalache | NoError | 38s | 0 | 1.0GB | 0 | 0 | 11K | 127K | 37 +21 | apalache | NoError | 12s | 0 | 588MB | 0 | 0 | 5.0K | 33K | 23 +22 | apalache | NoError | 18s | 0 | 816MB | 0 | 0 | 9.0K | 67K | 23 +23 | apalache | NoError | 25s | 0 | 937MB | 0 | 0 | 12K | 103K | 22 +24 | apalache | NoError | 13s | 0 | 776MB | 0 | 0 | 5.0K | 36K | 25 +25 | apalache | NoError | 19s | 0 | 807MB | 0 | 0 | 9.0K | 73K | 25 +26 | apalache | NoError | 23s | 0 | 976MB | 0 | 0 | 10K | 84K | 29 +27 | apalache | NoError | 34s | 0 | 1.0GB | 0 | 0 | 14K | 129K | 28 +28 | apalache | NoError | 42s | 0 | 1.0GB | 0 | 0 | 18K | 179K | 27 +29 | apalache | NoError | 43s | 0 | 1.0GB | 0 | 0 | 14K | 154K | 32 +30 | apalache | NoError | 59s | 0 | 1.0GB | 0 | 0 | 18K | 211K | 31 ## 4. Detailed results: 001indinv-apalache-card.csv 01:no | 02:tool | 03:status | 04:time_sec | 05:depth | 05:mem_kb | 10:ninit_trans | 11:ninit_trans | 12:ncells | 13:nclauses | 14:navg_clause_len -------|------------|-------------|---------------|------------|-------------|------------------|------------------|-------------|---------------|-------------------- 1 | apalache | NoError | 12m | 1 | 6.0GB | 0 | 0 | 255K | 2.0M | 20 -2 | apalache | NoError | 11m | 1 | 6.0GB | 0 | 0 | 242K | 2.0M | 21 -3 | apalache | NoError | 10m | 1 | 8.0GB | 0 | 0 | 227K | 2.0M | 21 -4 | apalache | NoError | 28m | 1 | 10GB | 0 | 0 | 346K | 4.0M | 21 -5 | apalache | NoError | 20m | 1 | 9.0GB | 0 | 0 | 329K | 4.0M | 21 -6 | apalache | NoError | 57m | 1 | 13GB | 0 | 0 | 561K | 9.0M | 20 -7 | apalache | NoError | 57m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 -8 | apalache | NoError | 53m | 1 | 12GB | 0 | 0 | 508K | 9.0M | 21 -9 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 999K | 20M | 20 -10 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 938K | 20M | 20 -11 | apalache | NoError | 4m04s | 0 | 4.0GB | 0 | 0 | 148K | 2.0M | 17 -12 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -13 | apalache | NoError | 7m07s | 0 | 8.0GB | 0 | 0 | 1.0M | 4.0M | 13 -14 | apalache | NoError | 7m07s | 0 | 7.0GB | 0 | 0 | 212K | 3.0M | 17 -15 | apalache | NoError | 12m | 0 | 12GB | 0 | 0 | 1.0M | 6.0M | 13 -16 | apalache | NoError | 20m | 0 | 12GB | 0 | 0 | 374K | 7.0M | 16 -17 | apalache | NoError | 29m | 0 | 18GB | 0 | 0 | 3.0M | 13M | 13 -18 | apalache | NoError | 32m | 0 | 19GB | 0 | 0 | 3.0M | 13M | 13 -19 | apalache | NoError | 51m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 -20 | apalache | NoError | 1h01m | 0 | 28GB | 0 | 0 | 6.0M | 29M | 13 -21 | apalache | NoError | 9s | 0 | 617MB | 0 | 0 | 3.0K | 19K | 27 -22 | apalache | NoError | 13s | 0 | 716MB | 0 | 0 | 5.0K | 37K | 29 -23 | apalache | Error | 17s | 0 | 938MB | 0 | 0 | 7.0K | 54K | 30 -24 | apalache | NoError | 10s | 0 | 644MB | 0 | 0 | 3.0K | 22K | 29 -25 | apalache | NoError | 15s | 0 | 928MB | 0 | 0 | 6.0K | 42K | 31 -26 | apalache | NoError | 18s | 0 | 895MB | 0 | 0 | 6.0K | 52K | 34 -27 | apalache | NoError | 24s | 0 | 1.0GB | 0 | 0 | 8.0K | 76K | 34 -28 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 10K | 100K | 34 -29 | apalache | NoError | 31s | 0 | 1.0GB | 0 | 0 | 9.0K | 97K | 37 -30 | apalache | NoError | 39s | 0 | 1.0GB | 0 | 0 | 11K | 128K | 37 +2 | apalache | NoError | 11m | 1 | 6.0GB | 0 | 0 | 241K | 2.0M | 21 +3 | apalache | NoError | 9m09s | 1 | 6.0GB | 0 | 0 | 227K | 2.0M | 21 +4 | apalache | NoError | 24m | 1 | 9.0GB | 0 | 0 | 346K | 4.0M | 21 +5 | apalache | NoError | 20m | 1 | 10GB | 0 | 0 | 329K | 4.0M | 21 +6 | apalache | NoError | 55m | 1 | 13GB | 0 | 0 | 560K | 9.0M | 20 +7 | apalache | NoError | 54m | 1 | 13GB | 0 | 0 | 531K | 9.0M | 21 +8 | apalache | NoError | 48m | 1 | 12GB | 0 | 0 | 508K | 9.0M | 21 +9 | apalache | NoError | 3h03m | 1 | 16GB | 0 | 0 | 1.0M | 20M | 20 +10 | apalache | NoError | 2h02m | 1 | 16GB | 0 | 0 | 940K | 20M | 20 +11 | apalache | NoError | 4m04s | 0 | 3.0GB | 0 | 0 | 148K | 2.0M | 17 +12 | apalache | NoError | 14m | 0 | 16GB | 0 | 0 | 2.0M | 8.0M | 10 +13 | apalache | NoError | 23m | 0 | 22GB | 0 | 0 | 6.0M | 16M | 10 +14 | apalache | NoError | 7m07s | 0 | 5.0GB | 0 | 0 | 212K | 3.0M | 17 +15 | apalache | NoError | 23m | 0 | 20GB | 0 | 0 | 4.0M | 12M | 10 +16 | apalache | NoError | 20m | 0 | 8.0GB | 0 | 0 | 374K | 7.0M | 16 +17 | apalache | Fail | 43m | 0 | 26GB | 0 | 0 | 172K | 3.0M | 17 +18 | apalache | Fail | 43m | 0 | 27GB | 0 | 0 | 160K | 3.0M | 17 +19 | apalache | NoError | 52m | 0 | 16GB | 0 | 0 | 734K | 17M | 16 +20 | apalache | Fail | 1h01m | 0 | 28GB | 0 | 0 | 341K | 8.0M | 16 +21 | apalache | NoError | 12s | 0 | 580MB | 0 | 0 | 5.0K | 33K | 23 +22 | apalache | NoError | 17s | 0 | 840MB | 0 | 0 | 9.0K | 68K | 23 +23 | apalache | NoError | 25s | 0 | 1.0GB | 0 | 0 | 12K | 104K | 22 +24 | apalache | NoError | 13s | 0 | 748MB | 0 | 0 | 5.0K | 36K | 25 +25 | apalache | NoError | 20s | 0 | 791MB | 0 | 0 | 9.0K | 73K | 25 +26 | apalache | NoError | 24s | 0 | 1009MB | 0 | 0 | 10K | 85K | 28 +27 | apalache | NoError | 33s | 0 | 1.0GB | 0 | 0 | 14K | 131K | 28 +28 | apalache | NoError | 44s | 0 | 1.0GB | 0 | 0 | 18K | 180K | 27 +29 | apalache | NoError | 45s | 0 | 1.0GB | 0 | 0 | 14K | 155K | 32 +30 | apalache | NoError | 55s | 0 | 1.0GB | 0 | 0 | 18K | 213K | 31 diff --git a/spec/fork-cases/results/001indinv-apalache-time-log.svg b/spec/fork-cases/results/001indinv-apalache-time-log.svg index 4dc352e..b4d69ba 100644 --- a/spec/fork-cases/results/001indinv-apalache-time-log.svg +++ b/spec/fork-cases/results/001indinv-apalache-time-log.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="maf222ef69f" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,23 +506,23 @@ z - +" id="mf3f74c5a7b" style="stroke:#000000;stroke-width:0.8;"/> - + - + @@ -531,18 +531,18 @@ L -3.5 0 - - + - + @@ -551,18 +551,18 @@ L 414.72 226.622086 - - + - + @@ -571,13 +571,13 @@ L 414.72 160.769425 - - + @@ -601,7 +601,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -613,234 +613,220 @@ z +" id="m163af7a352" style="stroke:#000000;stroke-width:0.6;"/> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - - - - - - - - - - - - - - - + @@ -1002,37 +988,37 @@ z - - + +" id="m8b6071f1b3" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + +" id="m7ccf6d2c0e" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1187,14 +1173,14 @@ Q 228.209062 78.82825 230.209062 78.82825 z " style="fill:#ffffff;opacity:0.8;stroke:#cccccc;stroke-linejoin:miter;"/> - + - + - + @@ -1319,14 +1305,14 @@ z - + - + - + @@ -1361,7 +1347,7 @@ L 252.209062 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-time.svg b/spec/fork-cases/results/001indinv-apalache-time.svg index 116bb58..20d5e1a 100644 --- a/spec/fork-cases/results/001indinv-apalache-time.svg +++ b/spec/fork-cases/results/001indinv-apalache-time.svg @@ -29,7 +29,7 @@ z - @@ -37,10 +37,10 @@ L 62.637743 41.472 +" id="m07e11130a7" style="stroke:#000000;stroke-width:0.8;"/> - + @@ -75,13 +75,13 @@ z - - + @@ -119,13 +119,13 @@ z - - + @@ -153,13 +153,13 @@ z - - + @@ -172,13 +172,13 @@ L 230.562508 41.472 - - + @@ -217,13 +217,13 @@ z - - + @@ -236,13 +236,13 @@ L 342.512351 41.472 - - + @@ -506,41 +506,41 @@ z - +" id="mbff5fb4a69" style="stroke:#000000;stroke-width:0.8;"/> - + - + - - + - + @@ -551,18 +551,18 @@ L 414.72 238.540501 - - + - + @@ -573,18 +573,18 @@ L 414.72 181.541702 - - + - + @@ -595,13 +595,13 @@ L 414.72 124.542904 - - + @@ -625,7 +625,7 @@ L 4.890625 26.703125 z " id="DejaVuSans-34"/> - + @@ -723,36 +723,36 @@ z - +" id="mb2140b6ad0" style="stroke:#ff0000;stroke-linejoin:miter;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - +" id="m5ea1edcaa3" style="stroke:#0000ff;"/> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -914,7 +914,7 @@ L 252.209062 54.570437 - + @@ -1052,7 +1052,7 @@ L 252.209062 69.248562 - + @@ -1087,7 +1087,7 @@ L 252.209062 69.248562 - + diff --git a/spec/fork-cases/results/001indinv-apalache-unstable.csv b/spec/fork-cases/results/001indinv-apalache-unstable.csv index 8db9de2..badd243 100644 --- a/spec/fork-cases/results/001indinv-apalache-unstable.csv +++ b/spec/fork-cases/results/001indinv-apalache-unstable.csv @@ -1,31 +1,31 @@ 01:no,02:tool,03:status,04:time_sec,05:depth,05:mem_kb,10:ninit_trans,11:ninit_trans,12:ncells,13:nclauses,14:navg_clause_len -1,apalache,NoError,1255,1,9542160,0,0,330701,2507564,25 -2,apalache,NoError,1238,1,8935616,0,0,313983,2424451,25 -3,apalache,NoError,1159,1,9094360,0,0,299480,2379006,25 -4,apalache,NoError,2420,1,12632632,0,0,461690,3765633,26 -5,apalache,NoError,2311,1,12533920,0,0,445753,3695822,27 -6,apalache,NoError,9747,1,15322452,0,0,780909,6900742,28 -7,apalache,NoError,9790,1,15556308,0,0,756257,6935204,29 -8,apalache,NoError,9405,1,15239236,0,0,728499,6679564,29 -9,apalache,NoError,42452,1,21365376,0,0,1449412,13551573,31 -10,apalache,NoError,40977,1,21199800,0,0,1391910,13310244,32 -11,apalache,NoError,390,0,6022364,0,0,219433,1722072,20 -12,apalache,NoError,631,0,9797272,0,0,1092882,3722702,14 -13,apalache,NoError,546,0,8661600,0,0,1081766,3678097,14 -14,apalache,NoError,768,0,9542188,0,0,326597,2646451,22 -15,apalache,NoError,1077,0,14964756,0,0,1689022,5765117,14 -16,apalache,NoError,2374,0,14718576,0,0,592163,5025992,24 -17,apalache,NoError,3140,0,21281836,0,0,3257404,11121278,15 -18,apalache,NoError,3210,0,21715468,0,0,3233186,11030301,16 -19,apalache,NoError,9147,0,20206236,0,0,1179739,10135524,27 -20,apalache,Fail,9438,0,32188224,0,0,564069,4984843,28 -21,apalache,NoError,10,0,659828,0,0,3486,19875,27 -22,apalache,NoError,13,0,711984,0,0,5912,37834,29 -23,apalache,Error,18,0,1046452,0,0,7594,54240,30 -24,apalache,NoError,10,0,673520,0,0,3635,22657,29 -25,apalache,NoError,15,0,979072,0,0,6052,42572,31 -26,apalache,NoError,18,0,930304,0,0,6376,52176,34 -27,apalache,NoError,23,0,1037284,0,0,8759,76583,34 -28,apalache,NoError,28,0,1237168,0,0,10960,100556,34 -29,apalache,NoError,31,0,1218064,0,0,9172,97055,37 -30,apalache,NoError,38,0,1501664,0,0,11501,127902,37 +1,apalache,NoError,1301,1,8497892,0,0,330488,2507123,25 +2,apalache,NoError,1225,1,8787920,0,0,313983,2424451,25 +3,apalache,NoError,1146,1,8284308,0,0,299480,2379006,25 +4,apalache,NoError,2391,1,12547740,0,0,461690,3765633,26 +5,apalache,NoError,2450,1,12471680,0,0,445753,3695822,27 +6,apalache,NoError,9971,1,15420532,0,0,780909,6900742,28 +7,apalache,NoError,9698,1,15702048,0,0,754742,6931775,29 +8,apalache,NoError,9300,1,15049568,0,0,728499,6679564,29 +9,apalache,NoError,42307,1,21681092,0,0,1451761,13556928,31 +10,apalache,NoError,41464,1,21471424,0,0,1391910,13310244,32 +11,apalache,NoError,398,0,5541800,0,0,219433,1722072,20 +12,apalache,NoError,1071,0,18618796,0,0,2868158,7817870,11 +13,apalache,NoError,1528,0,23933300,0,0,6407571,15963579,10 +14,apalache,NoError,694,0,8226532,0,0,326597,2646451,22 +15,apalache,NoError,1864,0,22907328,0,0,4451850,12126989,11 +16,apalache,NoError,2377,0,14444576,0,0,592163,5025992,24 +17,apalache,Fail,3738,0,28982004,0,0,280934,2463612,24 +18,apalache,Fail,3721,0,28828436,0,0,268824,2418130,24 +19,apalache,NoError,9216,0,20139480,0,0,1179739,10135524,27 +20,apalache,Fail,9712,0,31466408,0,0,564069,4984843,28 +21,apalache,NoError,12,0,602944,0,0,5649,33693,23 +22,apalache,NoError,18,0,836080,0,0,9739,67569,23 +23,apalache,NoError,25,0,959564,0,0,12833,103994,22 +24,apalache,NoError,13,0,795316,0,0,5812,36807,25 +25,apalache,NoError,19,0,826572,0,0,9902,73287,25 +26,apalache,NoError,23,0,999580,0,0,10267,84711,29 +27,apalache,NoError,34,0,1133032,0,0,14341,129922,28 +28,apalache,NoError,42,0,1319784,0,0,18453,179014,27 +29,apalache,NoError,43,0,1334480,0,0,14868,154666,32 +30,apalache,NoError,59,0,1575408,0,0,18942,211480,31