github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/spec/abci++/v4.md (about)

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