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.

198 lines
6.0 KiB

  1. # Tendermint v4 Markdown pseudocode
  2. This is a multi-threaded implementation of ABCI++,
  3. where ProcessProposal starts when the proposal is received, but ends before precommitting.
  4. ### Initialization
  5. ```go
  6. h_p ← 0
  7. round_p ← 0
  8. step_p is one of {propose, prevote, precommit}
  9. decision_p ← Vector()
  10. lockedValue_p ← nil
  11. validValue_p ← nil
  12. validRound_p ← -1
  13. ```
  14. ### StartRound(round)
  15. ```go
  16. function startRound(round) {
  17. round_p ← round
  18. step_p ← propose
  19. if proposer(h_p, round_p) = p {
  20. if validValue_p != nil {
  21. proposal ← validValue_p
  22. } else {
  23. txdata ← mempool.GetBlock()
  24. // getUnpreparedBlockProposal fills in header
  25. unpreparedProposal ← getUnpreparedBlockProposal(txdata)
  26. proposal ← ABCI.PrepareProposal(unpreparedProposal)
  27. }
  28. broadcast ⟨PROPOSAL, h_p, round_p, proposal, validRound_p⟩
  29. } else {
  30. schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p)
  31. }
  32. }
  33. ```
  34. ### ReceiveProposal
  35. In the case where the local node is not locked on any round, the following is ran:
  36. ```go
  37. upon ⟨PROPOSAL, h_p, round_p, v, −1) from proposer(h_p, round_p) while step_p = propose do {
  38. if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) {
  39. // We fork process proposal into a parallel process
  40. Fork ABCI.ProcessProposal(h_p, v)
  41. broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩
  42. } else {
  43. broadcast ⟨PREVOTE, h_p, round_p, nil⟩
  44. }
  45. step_p ← prevote
  46. }
  47. ```
  48. In the case where the node is locked on a round, the following is ran:
  49. ```go
  50. upon ⟨PROPOSAL, h_p, round_p, v, vr⟩
  51. from proposer(h_p, round_p)
  52. AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩
  53. while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do {
  54. if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) {
  55. // We fork process proposal into a parallel process
  56. Fork ABCI.ProcessProposal(h_p, v)
  57. broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩
  58. } else {
  59. broadcast ⟨PREVOTE, h_p, round_p, nil⟩
  60. }
  61. step_p ← prevote
  62. }
  63. ```
  64. ### Prevote timeout
  65. Upon receiving 2f + 1 prevotes, setup a timeout.
  66. ```go
  67. upon 2f + 1 ⟨PREVOTE, h_p, vr, -1⟩
  68. with step_p = prevote for the first time, do {
  69. schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p)
  70. }
  71. ```
  72. with OnTimeoutPrevote defined as:
  73. ```go
  74. def OnTimeoutPrevote(height, round) {
  75. if (height = h_p && round = round_p && step_p = prevote) {
  76. // Join the ProcessProposal, and output any evidence in case it has some.
  77. processProposalOutput ← Join ABCI.ProcessProposal(h_p, v)
  78. for evidence in processProposalOutput.evidence_list {
  79. broadcast ⟨EVIDENCE, evidence⟩
  80. }
  81. precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil)
  82. broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩
  83. step_p ← precommit
  84. }
  85. }
  86. ```
  87. ### Receiving enough prevotes to precommit
  88. The following code is ran upon receiving 2f + 1 prevotes for the same block
  89. ```go
  90. upon ⟨PROPOSAL, h_p, round_p, v, *⟩
  91. from proposer(h_p, round_p)
  92. AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩
  93. while valid(v) ∧ step_p >= prevote for the first time do {
  94. if (step_p = prevote) {
  95. lockedValue_p ← v
  96. lockedRound_p ← round_p
  97. processProposalOutput ← Join ABCI.ProcessProposal(h_p, v)
  98. // If the proposal is valid precommit as before.
  99. // If it was invalid, precommit nil.
  100. // Note that ABCI.ProcessProposal(h_p, v).accept is deterministic for all honest nodes.
  101. precommit_value ← nil
  102. if processProposalOutput.accept {
  103. precommit_value ← id(v)
  104. }
  105. precommit_extension ← ABCI.ExtendVote(h_p, round_p, precommit_value)
  106. broadcast ⟨PRECOMMIT, h_p, round_p, precommit_value, precommit_extension⟩
  107. for evidence in processProposalOutput.evidence_list {
  108. broadcast ⟨EVIDENCE, evidence⟩
  109. }
  110. step_p ← precommit
  111. }
  112. validValue_p ← v
  113. validRound_p ← round_p
  114. }
  115. ```
  116. And upon receiving 2f + 1 prevotes for nil:
  117. ```go
  118. upon 2f + 1 ⟨PREVOTE, h_p, round_p, nil⟩
  119. while step_p = prevote do {
  120. // Join ABCI.ProcessProposal, and broadcast any evidence if it exists.
  121. processProposalOutput ← Join ABCI.ProcessProposal(h_p, v)
  122. for evidence in processProposalOutput.evidence_list {
  123. broadcast ⟨EVIDENCE, evidence⟩
  124. }
  125. precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil)
  126. broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩
  127. step_p ← precommit
  128. }
  129. ```
  130. ### Upon receiving a precommit
  131. Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true`
  132. before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped
  133. in the syntax of methods from the paper.
  134. ### Precommit timeout
  135. Upon receiving 2f + 1 precommits, setup a timeout.
  136. ```go
  137. upon 2f + 1 ⟨PRECOMMIT, h_p, vr, *⟩ for the first time, do {
  138. schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p)
  139. }
  140. ```
  141. with OnTimeoutPrecommit defined as:
  142. ```go
  143. def OnTimeoutPrecommit(height, round) {
  144. if (height = h_p && round = round_p) {
  145. StartRound(round_p + 1)
  146. }
  147. }
  148. ```
  149. ### Upon Receiving 2f + 1 precommits
  150. The following code is ran upon receiving 2f + 1 precommits for the same block
  151. ```go
  152. upon ⟨PROPOSAL, h_p, r, v, *⟩
  153. from proposer(h_p, r)
  154. AND 2f + 1 ⟨ PRECOMMIT, h_p, r, id(v)⟩
  155. while decision_p[h_p] = nil do {
  156. if (valid(v)) {
  157. decision_p[h_p] ← v
  158. h_p ← h_p + 1
  159. reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values
  160. ABCI.FinalizeBlock(id(v))
  161. StartRound(0)
  162. }
  163. }
  164. ```
  165. If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.