You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

596 lines
21 KiB

  1. -------------------- MODULE TendermintAcc_004_draft ---------------------------
  2. (*
  3. A TLA+ specification of a simplified Tendermint consensus, tuned for
  4. fork accountability. The simplifications are as follows:
  5. - the protocol runs for one height, that is, it is one-shot consensus
  6. - this specification focuses on safety, so timeouts are modelled
  7. with non-determinism
  8. - the proposer function is non-determinstic, no fairness is assumed
  9. - the messages by the faulty processes are injected right in the initial states
  10. - every process has the voting power of 1
  11. - hashes are modelled as identity
  12. Having the above assumptions in mind, the specification follows the pseudo-code
  13. of the Tendermint paper: https://arxiv.org/abs/1807.04938
  14. Byzantine processes can demonstrate arbitrary behavior, including
  15. no communication. We show that if agreement is violated, then the Byzantine
  16. processes demonstrate one of the two behaviours:
  17. - Equivocation: a Byzantine process may send two different values
  18. in the same round.
  19. - Amnesia: a Byzantine process may lock a value without unlocking
  20. the previous value that it has locked in the past.
  21. * Version 4. Remove defective processes, fix bugs, collect global evidence.
  22. * Version 3. Modular and parameterized definitions.
  23. * Version 2. Bugfixes in the spec and an inductive invariant.
  24. * Version 1. A preliminary specification.
  25. Zarko Milosevic, Igor Konnov, Informal Systems, 2019-2020.
  26. *)
  27. EXTENDS Integers, FiniteSets, typedefs
  28. (********************* PROTOCOL PARAMETERS **********************************)
  29. CONSTANTS
  30. \* @type: Set(PROCESS);
  31. Corr, \* the set of correct processes
  32. \* @type: Set(PROCESS);
  33. Faulty, \* the set of Byzantine processes, may be empty
  34. \* @type: Int;
  35. N, \* the total number of processes: correct, defective, and Byzantine
  36. \* @type: Int;
  37. T, \* an upper bound on the number of Byzantine processes
  38. \* @type: Set(VALUE);
  39. ValidValues, \* the set of valid values, proposed both by correct and faulty
  40. \* @type: Set(VALUE);
  41. InvalidValues, \* the set of invalid values, never proposed by the correct ones
  42. \* @type: ROUND;
  43. MaxRound, \* the maximal round number
  44. \* @type: ROUND -> PROCESS;
  45. Proposer \* the proposer function from Rounds to AllProcs
  46. ASSUME(N = Cardinality(Corr \union Faulty))
  47. (*************************** DEFINITIONS ************************************)
  48. AllProcs == Corr \union Faulty \* the set of all processes
  49. \* @type: Set(ROUND);
  50. Rounds == 0..MaxRound \* the set of potential rounds
  51. \* @type: ROUND;
  52. NilRound == -1 \* a special value to denote a nil round, outside of Rounds
  53. RoundsOrNil == Rounds \union {NilRound}
  54. Values == ValidValues \union InvalidValues \* the set of all values
  55. \* @type: VALUE;
  56. NilValue == "None" \* a special value for a nil round, outside of Values
  57. ValuesOrNil == Values \union {NilValue}
  58. \* a value hash is modeled as identity
  59. \* @type: (t) => t;
  60. Id(v) == v
  61. \* The validity predicate
  62. IsValid(v) == v \in ValidValues
  63. \* the two thresholds that are used in the algorithm
  64. THRESHOLD1 == T + 1 \* at least one process is not faulty
  65. THRESHOLD2 == 2 * T + 1 \* a quorum when having N > 3 * T
  66. (********************* TYPE ANNOTATIONS FOR APALACHE **************************)
  67. \* An empty set of messages
  68. \* @type: Set(MESSAGE);
  69. EmptyMsgSet == {}
  70. (********************* PROTOCOL STATE VARIABLES ******************************)
  71. VARIABLES
  72. \* @type: PROCESS -> ROUND;
  73. round, \* a process round number: Corr -> Rounds
  74. \* @type: PROCESS -> STEP;
  75. step, \* a process step: Corr -> { "PROPOSE", "PREVOTE", "PRECOMMIT", "DECIDED" }
  76. \* @type: PROCESS -> VALUE;
  77. decision, \* process decision: Corr -> ValuesOrNil
  78. \* @type: PROCESS -> VALUE;
  79. lockedValue, \* a locked value: Corr -> ValuesOrNil
  80. \* @type: PROCESS -> ROUND;
  81. lockedRound, \* a locked round: Corr -> RoundsOrNil
  82. \* @type: PROCESS -> VALUE;
  83. validValue, \* a valid value: Corr -> ValuesOrNil
  84. \* @type: PROCESS -> ROUND;
  85. validRound \* a valid round: Corr -> RoundsOrNil
  86. \* book-keeping variables
  87. VARIABLES
  88. \* @type: ROUND -> Set(PROPMESSAGE);
  89. msgsPropose, \* PROPOSE messages broadcast in the system, Rounds -> Messages
  90. \* @type: ROUND -> Set(PREMESSAGE);
  91. msgsPrevote, \* PREVOTE messages broadcast in the system, Rounds -> Messages
  92. \* @type: ROUND -> Set(PREMESSAGE);
  93. msgsPrecommit, \* PRECOMMIT messages broadcast in the system, Rounds -> Messages
  94. \* @type: Set(MESSAGE);
  95. evidence, \* the messages that were used by the correct processes to make transitions
  96. \* @type: ACTION;
  97. action \* we use this variable to see which action was taken
  98. (* to see a type invariant, check TendermintAccInv3 *)
  99. \* a handy definition used in UNCHANGED
  100. vars == <<round, step, decision, lockedValue, lockedRound,
  101. validValue, validRound, evidence, msgsPropose, msgsPrevote, msgsPrecommit>>
  102. (********************* PROTOCOL INITIALIZATION ******************************)
  103. \* @type: (ROUND) => Set(PROPMESSAGE);
  104. FaultyProposals(r) ==
  105. [
  106. type : {"PROPOSAL"},
  107. src : Faulty,
  108. round : {r},
  109. proposal : Values,
  110. validRound: RoundsOrNil
  111. ]
  112. \* @type: Set(PROPMESSAGE);
  113. AllFaultyProposals ==
  114. [
  115. type : {"PROPOSAL"},
  116. src : Faulty,
  117. round : Rounds,
  118. proposal : Values,
  119. validRound: RoundsOrNil
  120. ]
  121. \* @type: (ROUND) => Set(PREMESSAGE);
  122. FaultyPrevotes(r) ==
  123. [
  124. type : {"PREVOTE"},
  125. src : Faulty,
  126. round: {r},
  127. id : Values
  128. ]
  129. \* @type: Set(PREMESSAGE);
  130. AllFaultyPrevotes ==
  131. [
  132. type : {"PREVOTE"},
  133. src : Faulty,
  134. round: Rounds,
  135. id : Values
  136. ]
  137. \* @type: (ROUND) => Set(PREMESSAGE);
  138. FaultyPrecommits(r) ==
  139. [
  140. type : {"PRECOMMIT"},
  141. src : Faulty,
  142. round: {r},
  143. id : Values
  144. ]
  145. \* @type: Set(PREMESSAGE);
  146. AllFaultyPrecommits ==
  147. [
  148. type : {"PRECOMMIT"},
  149. src : Faulty,
  150. round: Rounds,
  151. id : Values
  152. ]
  153. \* @type: (ROUND -> Set(MESSAGE)) => Bool;
  154. BenignRoundsInMessages(msgfun) ==
  155. \* the message function never contains a message for a wrong round
  156. \A r \in Rounds:
  157. \A m \in msgfun[r]:
  158. r = m.round
  159. \* The initial states of the protocol. Some faults can be in the system already.
  160. Init ==
  161. /\ round = [p \in Corr |-> 0]
  162. /\ step = [p \in Corr |-> "PROPOSE"]
  163. /\ decision = [p \in Corr |-> NilValue]
  164. /\ lockedValue = [p \in Corr |-> NilValue]
  165. /\ lockedRound = [p \in Corr |-> NilRound]
  166. /\ validValue = [p \in Corr |-> NilValue]
  167. /\ validRound = [p \in Corr |-> NilRound]
  168. /\ msgsPropose \in [Rounds -> SUBSET AllFaultyProposals]
  169. /\ msgsPrevote \in [Rounds -> SUBSET AllFaultyPrevotes]
  170. /\ msgsPrecommit \in [Rounds -> SUBSET AllFaultyPrecommits]
  171. /\ BenignRoundsInMessages(msgsPropose)
  172. /\ BenignRoundsInMessages(msgsPrevote)
  173. /\ BenignRoundsInMessages(msgsPrecommit)
  174. /\ evidence = EmptyMsgSet
  175. /\ action = "Init"
  176. (************************ MESSAGE PASSING ********************************)
  177. \* @type: (PROCESS, ROUND, VALUE, ROUND) => Bool;
  178. BroadcastProposal(pSrc, pRound, pProposal, pValidRound) ==
  179. LET
  180. \* @type: PROPMESSAGE;
  181. newMsg ==
  182. [
  183. type |-> "PROPOSAL",
  184. src |-> pSrc,
  185. round |-> pRound,
  186. proposal |-> pProposal,
  187. validRound |-> pValidRound
  188. ]
  189. IN
  190. msgsPropose' = [msgsPropose EXCEPT ![pRound] = msgsPropose[pRound] \union {newMsg}]
  191. \* @type: (PROCESS, ROUND, VALUE) => Bool;
  192. BroadcastPrevote(pSrc, pRound, pId) ==
  193. LET
  194. \* @type: PREMESSAGE;
  195. newMsg ==
  196. [
  197. type |-> "PREVOTE",
  198. src |-> pSrc,
  199. round |-> pRound,
  200. id |-> pId
  201. ]
  202. IN
  203. msgsPrevote' = [msgsPrevote EXCEPT ![pRound] = msgsPrevote[pRound] \union {newMsg}]
  204. \* @type: (PROCESS, ROUND, VALUE) => Bool;
  205. BroadcastPrecommit(pSrc, pRound, pId) ==
  206. LET
  207. \* @type: PREMESSAGE;
  208. newMsg ==
  209. [
  210. type |-> "PRECOMMIT",
  211. src |-> pSrc,
  212. round |-> pRound,
  213. id |-> pId
  214. ]
  215. IN
  216. msgsPrecommit' = [msgsPrecommit EXCEPT ![pRound] = msgsPrecommit[pRound] \union {newMsg}]
  217. (********************* PROTOCOL TRANSITIONS ******************************)
  218. \* lines 12-13
  219. StartRound(p, r) ==
  220. /\ step[p] /= "DECIDED" \* a decided process does not participate in consensus
  221. /\ round' = [round EXCEPT ![p] = r]
  222. /\ step' = [step EXCEPT ![p] = "PROPOSE"]
  223. \* lines 14-19, a proposal may be sent later
  224. \* @type: (PROCESS) => Bool;
  225. InsertProposal(p) ==
  226. LET r == round[p] IN
  227. /\ p = Proposer[r]
  228. /\ step[p] = "PROPOSE"
  229. \* if the proposer is sending a proposal, then there are no other proposals
  230. \* by the correct processes for the same round
  231. /\ \A m \in msgsPropose[r]: m.src /= p
  232. /\ \E v \in ValidValues:
  233. LET
  234. \* @type: VALUE;
  235. proposal ==
  236. IF validValue[p] /= NilValue
  237. THEN validValue[p]
  238. ELSE v
  239. IN BroadcastProposal(p, round[p], proposal, validRound[p])
  240. /\ UNCHANGED <<evidence, round, decision, lockedValue, lockedRound,
  241. validValue, step, validRound, msgsPrevote, msgsPrecommit>>
  242. /\ action' = "InsertProposal"
  243. \* lines 22-27
  244. UponProposalInPropose(p) ==
  245. \E v \in Values:
  246. /\ step[p] = "PROPOSE" (* line 22 *)
  247. /\ LET
  248. \* @type: PROPMESSAGE;
  249. msg ==
  250. [
  251. type |-> "PROPOSAL",
  252. src |-> Proposer[round[p]],
  253. round |-> round[p],
  254. proposal |-> v,
  255. validRound |-> NilRound
  256. ]
  257. IN
  258. /\ msg \in msgsPropose[round[p]] \* line 22
  259. /\ evidence' = {msg} \union evidence
  260. /\ LET mid == (* line 23 *)
  261. IF IsValid(v) /\ (lockedRound[p] = NilRound \/ lockedValue[p] = v)
  262. THEN Id(v)
  263. ELSE NilValue
  264. IN
  265. BroadcastPrevote(p, round[p], mid) \* lines 24-26
  266. /\ step' = [step EXCEPT ![p] = "PREVOTE"]
  267. /\ UNCHANGED <<round, decision, lockedValue, lockedRound,
  268. validValue, validRound, msgsPropose, msgsPrecommit>>
  269. /\ action' = "UponProposalInPropose"
  270. \* lines 28-33
  271. UponProposalInProposeAndPrevote(p) ==
  272. \E v \in Values, vr \in Rounds:
  273. /\ step[p] = "PROPOSE" /\ 0 <= vr /\ vr < round[p] \* line 28, the while part
  274. /\ LET
  275. \* @type: PROPMESSAGE;
  276. msg ==
  277. [
  278. type |-> "PROPOSAL",
  279. src |-> Proposer[round[p]],
  280. round |-> round[p],
  281. proposal |-> v,
  282. validRound |-> vr
  283. ]
  284. IN
  285. /\ msg \in msgsPropose[round[p]] \* line 28
  286. /\ LET PV == { m \in msgsPrevote[vr]: m.id = Id(v) } IN
  287. /\ Cardinality(PV) >= THRESHOLD2 \* line 28
  288. /\ evidence' = PV \union {msg} \union evidence
  289. /\ LET mid == (* line 29 *)
  290. IF IsValid(v) /\ (lockedRound[p] <= vr \/ lockedValue[p] = v)
  291. THEN Id(v)
  292. ELSE NilValue
  293. IN
  294. BroadcastPrevote(p, round[p], mid) \* lines 24-26
  295. /\ step' = [step EXCEPT ![p] = "PREVOTE"]
  296. /\ UNCHANGED <<round, decision, lockedValue, lockedRound,
  297. validValue, validRound, msgsPropose, msgsPrecommit>>
  298. /\ action' = "UponProposalInProposeAndPrevote"
  299. \* lines 34-35 + lines 61-64 (onTimeoutPrevote)
  300. UponQuorumOfPrevotesAny(p) ==
  301. /\ step[p] = "PREVOTE" \* line 34 and 61
  302. /\ \E MyEvidence \in SUBSET msgsPrevote[round[p]]:
  303. \* find the unique voters in the evidence
  304. LET Voters == { m.src: m \in MyEvidence } IN
  305. \* compare the number of the unique voters against the threshold
  306. /\ Cardinality(Voters) >= THRESHOLD2 \* line 34
  307. /\ evidence' = MyEvidence \union evidence
  308. /\ BroadcastPrecommit(p, round[p], NilValue)
  309. /\ step' = [step EXCEPT ![p] = "PRECOMMIT"]
  310. /\ UNCHANGED <<round, decision, lockedValue, lockedRound,
  311. validValue, validRound, msgsPropose, msgsPrevote>>
  312. /\ action' = "UponQuorumOfPrevotesAny"
  313. \* lines 36-46
  314. UponProposalInPrevoteOrCommitAndPrevote(p) ==
  315. \E v \in ValidValues, vr \in RoundsOrNil:
  316. /\ step[p] \in {"PREVOTE", "PRECOMMIT"} \* line 36
  317. /\ LET
  318. \* @type: PROPMESSAGE;
  319. msg ==
  320. [
  321. type |-> "PROPOSAL",
  322. src |-> Proposer[round[p]],
  323. round |-> round[p],
  324. proposal |-> v,
  325. validRound |-> vr
  326. ]
  327. IN
  328. /\ msg \in msgsPropose[round[p]] \* line 36
  329. /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(v) } IN
  330. /\ Cardinality(PV) >= THRESHOLD2 \* line 36
  331. /\ evidence' = PV \union {msg} \union evidence
  332. /\ IF step[p] = "PREVOTE"
  333. THEN \* lines 38-41:
  334. /\ lockedValue' = [lockedValue EXCEPT ![p] = v]
  335. /\ lockedRound' = [lockedRound EXCEPT ![p] = round[p]]
  336. /\ BroadcastPrecommit(p, round[p], Id(v))
  337. /\ step' = [step EXCEPT ![p] = "PRECOMMIT"]
  338. ELSE
  339. UNCHANGED <<lockedValue, lockedRound, msgsPrecommit, step>>
  340. \* lines 42-43
  341. /\ validValue' = [validValue EXCEPT ![p] = v]
  342. /\ validRound' = [validRound EXCEPT ![p] = round[p]]
  343. /\ UNCHANGED <<round, decision, msgsPropose, msgsPrevote>>
  344. /\ action' = "UponProposalInPrevoteOrCommitAndPrevote"
  345. \* lines 47-48 + 65-67 (onTimeoutPrecommit)
  346. UponQuorumOfPrecommitsAny(p) ==
  347. /\ \E MyEvidence \in SUBSET msgsPrecommit[round[p]]:
  348. \* find the unique committers in the evidence
  349. LET Committers == { m.src: m \in MyEvidence } IN
  350. \* compare the number of the unique committers against the threshold
  351. /\ Cardinality(Committers) >= THRESHOLD2 \* line 47
  352. /\ evidence' = MyEvidence \union evidence
  353. /\ round[p] + 1 \in Rounds
  354. /\ StartRound(p, round[p] + 1)
  355. /\ UNCHANGED <<decision, lockedValue, lockedRound, validValue,
  356. validRound, msgsPropose, msgsPrevote, msgsPrecommit>>
  357. /\ action' = "UponQuorumOfPrecommitsAny"
  358. \* lines 49-54
  359. UponProposalInPrecommitNoDecision(p) ==
  360. /\ decision[p] = NilValue \* line 49
  361. /\ \E v \in ValidValues (* line 50*) , r \in Rounds, vr \in RoundsOrNil:
  362. /\ LET
  363. \* @type: PROPMESSAGE;
  364. msg ==
  365. [
  366. type |-> "PROPOSAL",
  367. src |-> Proposer[r],
  368. round |-> r,
  369. proposal |-> v,
  370. validRound |-> vr
  371. ]
  372. IN
  373. /\ msg \in msgsPropose[r] \* line 49
  374. /\ LET PV == { m \in msgsPrecommit[r]: m.id = Id(v) } IN
  375. /\ Cardinality(PV) >= THRESHOLD2 \* line 49
  376. /\ evidence' = PV \union {msg} \union evidence
  377. /\ decision' = [decision EXCEPT ![p] = v] \* update the decision, line 51
  378. \* The original algorithm does not have 'DECIDED', but it increments the height.
  379. \* We introduced 'DECIDED' here to prevent the process from changing its decision.
  380. /\ step' = [step EXCEPT ![p] = "DECIDED"]
  381. /\ UNCHANGED <<round, lockedValue, lockedRound, validValue,
  382. validRound, msgsPropose, msgsPrevote, msgsPrecommit>>
  383. /\ action' = "UponProposalInPrecommitNoDecision"
  384. \* the actions below are not essential for safety, but added for completeness
  385. \* lines 20-21 + 57-60
  386. OnTimeoutPropose(p) ==
  387. /\ step[p] = "PROPOSE"
  388. /\ p /= Proposer[round[p]]
  389. /\ BroadcastPrevote(p, round[p], NilValue)
  390. /\ step' = [step EXCEPT ![p] = "PREVOTE"]
  391. /\ UNCHANGED <<round, lockedValue, lockedRound, validValue,
  392. validRound, decision, evidence, msgsPropose, msgsPrecommit>>
  393. /\ action' = "OnTimeoutPropose"
  394. \* lines 44-46
  395. OnQuorumOfNilPrevotes(p) ==
  396. /\ step[p] = "PREVOTE"
  397. /\ LET PV == { m \in msgsPrevote[round[p]]: m.id = Id(NilValue) } IN
  398. /\ Cardinality(PV) >= THRESHOLD2 \* line 36
  399. /\ evidence' = PV \union evidence
  400. /\ BroadcastPrecommit(p, round[p], Id(NilValue))
  401. /\ step' = [step EXCEPT ![p] = "PRECOMMIT"]
  402. /\ UNCHANGED <<round, lockedValue, lockedRound, validValue,
  403. validRound, decision, msgsPropose, msgsPrevote>>
  404. /\ action' = "OnQuorumOfNilPrevotes"
  405. \* lines 55-56
  406. OnRoundCatchup(p) ==
  407. \E r \in {rr \in Rounds: rr > round[p]}:
  408. LET RoundMsgs == msgsPropose[r] \union msgsPrevote[r] \union msgsPrecommit[r] IN
  409. \E MyEvidence \in SUBSET RoundMsgs:
  410. LET Faster == { m.src: m \in MyEvidence } IN
  411. /\ Cardinality(Faster) >= THRESHOLD1
  412. /\ evidence' = MyEvidence \union evidence
  413. /\ StartRound(p, r)
  414. /\ UNCHANGED <<decision, lockedValue, lockedRound, validValue,
  415. validRound, msgsPropose, msgsPrevote, msgsPrecommit>>
  416. /\ action' = "OnRoundCatchup"
  417. (*
  418. * A system transition. In this specificatiom, the system may eventually deadlock,
  419. * e.g., when all processes decide. This is expected behavior, as we focus on safety.
  420. *)
  421. Next ==
  422. \E p \in Corr:
  423. \/ InsertProposal(p)
  424. \/ UponProposalInPropose(p)
  425. \/ UponProposalInProposeAndPrevote(p)
  426. \/ UponQuorumOfPrevotesAny(p)
  427. \/ UponProposalInPrevoteOrCommitAndPrevote(p)
  428. \/ UponQuorumOfPrecommitsAny(p)
  429. \/ UponProposalInPrecommitNoDecision(p)
  430. \* the actions below are not essential for safety, but added for completeness
  431. \/ OnTimeoutPropose(p)
  432. \/ OnQuorumOfNilPrevotes(p)
  433. \/ OnRoundCatchup(p)
  434. (**************************** FORK SCENARIOS ***************************)
  435. \* equivocation by a process p
  436. EquivocationBy(p) ==
  437. \E m1, m2 \in evidence:
  438. /\ m1 /= m2
  439. /\ m1.src = p
  440. /\ m2.src = p
  441. /\ m1.round = m2.round
  442. /\ m1.type = m2.type
  443. \* amnesic behavior by a process p
  444. AmnesiaBy(p) ==
  445. \E r1, r2 \in Rounds:
  446. /\ r1 < r2
  447. /\ \E v1, v2 \in ValidValues:
  448. /\ v1 /= v2
  449. /\ [
  450. type |-> "PRECOMMIT",
  451. src |-> p,
  452. round |-> r1,
  453. id |-> Id(v1)
  454. ] \in evidence
  455. /\ [
  456. type |-> "PREVOTE",
  457. src |-> p,
  458. round |-> r2,
  459. id |-> Id(v2)
  460. ] \in evidence
  461. /\ \A r \in { rnd \in Rounds: r1 <= rnd /\ rnd < r2 }:
  462. LET prevotes ==
  463. { m \in evidence:
  464. m.type = "PREVOTE" /\ m.round = r /\ m.id = Id(v2) }
  465. IN
  466. Cardinality(prevotes) < THRESHOLD2
  467. (******************************** PROPERTIES ***************************************)
  468. \* the safety property -- agreement
  469. Agreement ==
  470. \A p, q \in Corr:
  471. \/ decision[p] = NilValue
  472. \/ decision[q] = NilValue
  473. \/ decision[p] = decision[q]
  474. \* the protocol validity
  475. Validity ==
  476. \A p \in Corr: decision[p] \in ValidValues \union {NilValue}
  477. (*
  478. The protocol safety. Two cases are possible:
  479. 1. There is no fork, that is, Agreement holds true.
  480. 2. A subset of faulty processes demonstrates equivocation or amnesia.
  481. *)
  482. Accountability ==
  483. \/ Agreement
  484. \/ \E Detectable \in SUBSET Faulty:
  485. /\ Cardinality(Detectable) >= THRESHOLD1
  486. /\ \A p \in Detectable:
  487. EquivocationBy(p) \/ AmnesiaBy(p)
  488. (****************** FALSE INVARIANTS TO PRODUCE EXAMPLES ***********************)
  489. \* This property is violated. You can check it to see how amnesic behavior
  490. \* appears in the evidence variable.
  491. NoAmnesia ==
  492. \A p \in Faulty: ~AmnesiaBy(p)
  493. \* This property is violated. You can check it to see an example of equivocation.
  494. NoEquivocation ==
  495. \A p \in Faulty: ~EquivocationBy(p)
  496. \* This property is violated. You can check it to see an example of agreement.
  497. \* It is not exactly ~Agreement, as we do not want to see the states where
  498. \* decision[p] = NilValue
  499. NoAgreement ==
  500. \A p, q \in Corr:
  501. (p /= q /\ decision[p] /= NilValue /\ decision[q] /= NilValue)
  502. => decision[p] /= decision[q]
  503. \* Either agreement holds, or the faulty processes indeed demonstrate amnesia.
  504. \* This property is violated. A counterexample should demonstrate equivocation.
  505. AgreementOrAmnesia ==
  506. Agreement \/ (\A p \in Faulty: AmnesiaBy(p))
  507. \* We expect this property to be violated. It shows us a protocol run,
  508. \* where one faulty process demonstrates amnesia without equivocation.
  509. \* However, the absence of amnesia
  510. \* is a tough constraint for Apalache. It has not reported a counterexample
  511. \* for n=4,f=2, length <= 5.
  512. ShowMeAmnesiaWithoutEquivocation ==
  513. (~Agreement /\ \E p \in Faulty: ~EquivocationBy(p))
  514. => \A p \in Faulty: ~AmnesiaBy(p)
  515. \* This property is violated on n=4,f=2, length=4 in less than 10 min.
  516. \* Two faulty processes may demonstrate amnesia without equivocation.
  517. AmnesiaImpliesEquivocation ==
  518. (\E p \in Faulty: AmnesiaBy(p)) => (\E q \in Faulty: EquivocationBy(q))
  519. (*
  520. This property is violated. You can check it to see that all correct processes
  521. may reach MaxRound without making a decision.
  522. *)
  523. NeverUndecidedInMaxRound ==
  524. LET AllInMax == \A p \in Corr: round[p] = MaxRound
  525. AllDecided == \A p \in Corr: decision[p] /= NilValue
  526. IN
  527. AllInMax => AllDecided
  528. =============================================================================