github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/spec/abci++/v2.md (about) 1 # Tendermint v2 Markdown pseudocode 2 3 This adds a single-threaded implementation of ABCI++, 4 with no optimization for splitting out verifying the header and verifying the proposal. 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 takes tx data, and fills in the unprepared header data 30 unpreparedProposal ← getUnpreparedBlockProposal(txdata) 31 // ABCI++: the proposer may reorder/update transactions in `unpreparedProposal` 32 proposal ← ABCI.PrepareProposal(unpreparedProposal) 33 } 34 broadcast ⟨PROPOSAL, h_p, round_p, proposal, validRound_p⟩ 35 } else { 36 schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p) 37 } 38 } 39 ``` 40 41 ### ReceiveProposal 42 43 In the case where the local node is not locked on any round, the following is ran: 44 45 ```go 46 upon ⟨PROPOSAL, h_p, round_p, v, −1) from proposer(h_p, round_p) while step_p = propose do { 47 if valid(v) ∧ ABCI.ProcessProposal(h_p, v).accept ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) { 48 broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ 49 } else { 50 broadcast ⟨PREVOTE, h_p, round_p, nil⟩ 51 // Include any slashing evidence that may be sent in the process proposal response 52 for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { 53 broadcast ⟨EVIDENCE, evidence⟩ 54 } 55 } 56 step_p ← prevote 57 } 58 ``` 59 60 In the case where the node is locked on a round, the following is ran: 61 62 ```go 63 upon ⟨PROPOSAL, h_p, round_p, v, vr⟩ 64 from proposer(h_p, round_p) 65 AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩ 66 while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do { 67 if valid(v) ∧ ABCI.ProcessProposal(h_p, v).accept ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { 68 broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ 69 } else { 70 broadcast ⟨PREVOTE, h_p, round_p, nil⟩ 71 // Include any slashing evidence that may be sent in the process proposal response 72 for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { 73 broadcast ⟨EVIDENCE, evidence⟩ 74 } 75 } 76 step_p ← prevote 77 } 78 ``` 79 80 ### Prevote timeout 81 82 Upon receiving 2f + 1 prevotes, setup a timeout. 83 84 ```go 85 upon 2f + 1 ⟨PREVOTE, h_p, vr, -1⟩ 86 with step_p = prevote for the first time, do { 87 schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p) 88 } 89 ``` 90 91 with OnTimeoutPrevote defined as: 92 93 ```go 94 function OnTimeoutPrevote(height, round) { 95 if (height = h_p && round = round_p && step_p = prevote) { 96 precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) 97 broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ 98 step_p ← precommit 99 } 100 } 101 ``` 102 103 ### Receiving enough prevotes to precommit 104 105 The following code is ran upon receiving 2f + 1 prevotes for the same block 106 107 ```go 108 upon ⟨PROPOSAL, h_p, round_p, v, *⟩ 109 from proposer(h_p, round_p) 110 AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩ 111 while valid(v) ∧ step_p >= prevote for the first time do { 112 if (step_p = prevote) { 113 lockedValue_p ← v 114 lockedRound_p ← round_p 115 precommit_extension ← ABCI.ExtendVote(h_p, round_p, id(v)) 116 broadcast ⟨PRECOMMIT, h_p, round_p, id(v), precommit_extension⟩ 117 step_p ← precommit 118 } 119 validValue_p ← v 120 validRound_p ← round_p 121 } 122 ``` 123 124 And upon receiving 2f + 1 prevotes for nil: 125 126 ```go 127 upon 2f + 1 ⟨PREVOTE, h_p, round_p, nil⟩ 128 while step_p = prevote do { 129 precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) 130 broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ 131 step_p ← precommit 132 } 133 ``` 134 135 ### Upon receiving a precommit 136 137 Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true` 138 before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped 139 in the syntax of methods from the paper. 140 141 ### Precommit timeout 142 143 Upon receiving 2f + 1 precommits, setup a timeout. 144 145 ```go 146 upon 2f + 1 ⟨PRECOMMIT, h_p, vr, *⟩ for the first time, do { 147 schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p) 148 } 149 ``` 150 151 with OnTimeoutPrecommit defined as: 152 153 ```go 154 function OnTimeoutPrecommit(height, round) { 155 if (height = h_p && round = round_p) { 156 StartRound(round_p + 1) 157 } 158 } 159 ``` 160 161 ### Upon Receiving 2f + 1 precommits 162 163 The following code is ran upon receiving 2f + 1 precommits for the same block 164 165 ```go 166 upon ⟨PROPOSAL, h_p, r, v, *⟩ 167 from proposer(h_p, r) 168 AND 2f + 1 ⟨ PRECOMMIT, h_p, r, id(v)⟩ 169 while decision_p[h_p] = nil do { 170 if (valid(v)) { 171 decision_p[h_p] ← v 172 h_p ← h_p + 1 173 reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values 174 ABCI.FinalizeBlock(id(v)) 175 StartRound(0) 176 } 177 } 178 ``` 179 180 If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.