github.com/ari-anchor/sei-tendermint@v0.0.0-20230519144642-dc826b7b56bb/spec/abci++/v3.md (about) 1 # Tendermint v3 Markdown pseudocode 2 3 This is a single-threaded implementation of ABCI++, 4 with an optimization for the ProcessProposal phase. 5 Namely, processing of the header and the block data is separated into two different functions. 6 7 ### Initialization 8 9 ```go 10 h_p ← 0 11 round_p ← 0 12 step_p is one of {propose, prevote, precommit} 13 decision_p ← Vector() 14 lockedValue_p ← nil 15 validValue_p ← nil 16 validRound_p ← -1 17 ``` 18 19 ### StartRound(round) 20 21 ```go 22 function startRound(round) { 23 round_p ← round 24 step_p ← propose 25 if proposer(h_p, round_p) = p { 26 if validValue_p != nil { 27 proposal ← validValue_p 28 } else { 29 txdata ← mempool.GetBlock() 30 // getUnpreparedBlockProposal fills in header 31 unpreparedProposal ← getUnpreparedBlockProposal(txdata) 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_header, −1) from proposer(h_p, round_p) while step_p = propose do { 47 prevote_nil ← false 48 // valid is Tendermints validation, ABCI.VerifyHeader is the applications 49 if valid(v_header) ∧ ABCI.VerifyHeader(h_p, v_header) ∧ (lockedRound_p = −1 ∨ lockedValue_p = id(v_header)) { 50 wait to receive proposal v corresponding to v_header 51 // We split up the app's header verification from the remainder of its processing of the proposal 52 if ABCI.ProcessProposal(h_p, v).accept { 53 broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ 54 } else { 55 prevote_nil ← true 56 // Include any slashing evidence that may be sent in the process proposal response 57 for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { 58 broadcast ⟨EVIDENCE, evidence⟩ 59 } 60 } 61 } else { 62 prevote_nil ← true 63 } 64 if prevote_nil { 65 broadcast ⟨PREVOTE, h_p, round_p, nil⟩ 66 } 67 step_p ← prevote 68 } 69 ``` 70 71 In the case where the node is locked on a round, the following is ran: 72 73 ```go 74 upon ⟨PROPOSAL, h_p, round_p, v_header, vr⟩ 75 from proposer(h_p, round_p) 76 AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v_header)⟩ 77 while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do { 78 prevote_nil ← false 79 if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { 80 wait to receive proposal v corresponding to v_header 81 // We split up the app's header verification from the remainder of its processing of the proposal 82 if ABCI.ProcessProposal(h_p, v).accept { 83 broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ 84 } else { 85 prevote_nil ← true 86 // Include any slashing evidence that may be sent in the process proposal response 87 for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { 88 broadcast ⟨EVIDENCE, evidence⟩ 89 } 90 } 91 } else { 92 prevote_nil ← true 93 } 94 if prevote_nil { 95 broadcast ⟨PREVOTE, h_p, round_p, nil⟩ 96 } 97 step_p ← prevote 98 } 99 ``` 100 101 ### Prevote timeout 102 103 Upon receiving 2f + 1 prevotes, setup a timeout. 104 105 ```go 106 upon 2f + 1 ⟨PREVOTE, h_p, vr, -1⟩ 107 with step_p = prevote for the first time, do { 108 schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p) 109 } 110 ``` 111 112 with OnTimeoutPrevote defined as: 113 114 ```go 115 function OnTimeoutPrevote(height, round) { 116 if (height = h_p && round = round_p && step_p = prevote) { 117 precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) 118 broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ 119 step_p ← precommit 120 } 121 } 122 ``` 123 124 ### Receiving enough prevotes to precommit 125 126 The following code is ran upon receiving 2f + 1 prevotes for the same block 127 128 ```go 129 upon ⟨PROPOSAL, h_p, round_p, v, *⟩ 130 from proposer(h_p, round_p) 131 AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩ 132 while valid(v) ∧ step_p >= prevote for the first time do { 133 if (step_p = prevote) { 134 lockedValue_p ← v 135 lockedRound_p ← round_p 136 precommit_extension ← ABCI.ExtendVote(h_p, round_p, id(v)) 137 broadcast ⟨PRECOMMIT, h_p, round_p, id(v), precommit_extension⟩ 138 step_p ← precommit 139 } 140 validValue_p ← v 141 validRound_p ← round_p 142 } 143 ``` 144 145 And upon receiving 2f + 1 prevotes for nil: 146 147 ```go 148 upon 2f + 1 ⟨PREVOTE, h_p, round_p, nil⟩ 149 while step_p = prevote do { 150 precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) 151 broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ 152 step_p ← precommit 153 } 154 ``` 155 156 ### Upon receiving a precommit 157 158 Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true` 159 before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped 160 in the syntax of methods from the paper. 161 162 ### Precommit timeout 163 164 Upon receiving 2f + 1 precommits, setup a timeout. 165 166 ```go 167 upon 2f + 1 ⟨PRECOMMIT, h_p, vr, *⟩ for the first time, do { 168 schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p) 169 } 170 ``` 171 172 with OnTimeoutPrecommit defined as: 173 174 ```go 175 function OnTimeoutPrecommit(height, round) { 176 if (height = h_p && round = round_p) { 177 StartRound(round_p + 1) 178 } 179 } 180 ``` 181 182 ### Upon Receiving 2f + 1 precommits 183 184 The following code is ran upon receiving 2f + 1 precommits for the same block 185 186 ```go 187 upon ⟨PROPOSAL, h_p, r, v, *⟩ 188 from proposer(h_p, r) 189 AND 2f + 1 ⟨ PRECOMMIT, h_p, r, id(v)⟩ 190 while decision_p[h_p] = nil do { 191 if (valid(v)) { 192 decision_p[h_p] ← v 193 h_p ← h_p + 1 194 reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values 195 ABCI.FinalizeBlock(id(v)) 196 StartRound(0) 197 } 198 } 199 ``` 200 201 If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.