# Tendermint v2 Markdown pseudocode This adds a single-threaded implementation of ABCI++, with no optimization for splitting out verifying the header and verifying the proposal. ### Initialization ```go h_p ← 0 round_p ← 0 step_p is one of {propose, prevote, precommit} decision_p ← Vector() lockedValue_p ← nil validValue_p ← nil validRound_p ← -1 ``` ### StartRound(round) ```go function startRound(round) { round_p ← round step_p ← propose if proposer(h_p, round_p) = p { if validValue_p != nil { proposal ← validValue_p } else { txdata ← mempool.GetBlock() // getUnpreparedBlockProposal takes tx data, and fills in the unprepared header data unpreparedProposal ← getUnpreparedBlockProposal(txdata) // ABCI++: the proposer may reorder/update transactions in `unpreparedProposal` proposal ← ABCI.PrepareProposal(unpreparedProposal) } broadcast ⟨PROPOSAL, h_p, round_p, proposal, validRound_p⟩ } else { schedule OnTimeoutPropose(h_p,round_p) to be executed after timeoutPropose(round_p) } } ``` ### ReceiveProposal In the case where the local node is not locked on any round, the following is ran: ```go upon ⟨PROPOSAL, h_p, round_p, v, −1) from proposer(h_p, round_p) while step_p = propose do { if valid(v) ∧ ABCI.ProcessProposal(h_p, v).accept ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { broadcast ⟨PREVOTE, h_p, round_p, nil⟩ // Include any slashing evidence that may be sent in the process proposal response for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { broadcast ⟨EVIDENCE, evidence⟩ } } step_p ← prevote } ``` In the case where the node is locked on a round, the following is ran: ```go upon ⟨PROPOSAL, h_p, round_p, v, vr⟩ from proposer(h_p, round_p) AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩ while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do { if valid(v) ∧ ABCI.ProcessProposal(h_p, v).accept ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { broadcast ⟨PREVOTE, h_p, round_p, nil⟩ // Include any slashing evidence that may be sent in the process proposal response for evidence in ABCI.ProcessProposal(h_p, v).evidence_list { broadcast ⟨EVIDENCE, evidence⟩ } } step_p ← prevote } ``` ### Prevote timeout Upon receiving 2f + 1 prevotes, setup a timeout. ```go upon 2f + 1 ⟨PREVOTE, h_p, vr, -1⟩ with step_p = prevote for the first time, do { schedule OnTimeoutPrevote(h_p, round_p) to be executed after timeoutPrevote(round_p) } ``` with OnTimeoutPrevote defined as: ```go function OnTimeoutPrevote(height, round) { if (height = h_p && round = round_p && step_p = prevote) { precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ step_p ← precommit } } ``` ### Receiving enough prevotes to precommit The following code is ran upon receiving 2f + 1 prevotes for the same block ```go upon ⟨PROPOSAL, h_p, round_p, v, *⟩ from proposer(h_p, round_p) AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v)⟩ while valid(v) ∧ step_p >= prevote for the first time do { if (step_p = prevote) { lockedValue_p ← v lockedRound_p ← round_p precommit_extension ← ABCI.ExtendVote(h_p, round_p, id(v)) broadcast ⟨PRECOMMIT, h_p, round_p, id(v), precommit_extension⟩ step_p ← precommit } validValue_p ← v validRound_p ← round_p } ``` And upon receiving 2f + 1 prevotes for nil: ```go upon 2f + 1 ⟨PREVOTE, h_p, round_p, nil⟩ while step_p = prevote do { precommit_extension ← ABCI.ExtendVote(h_p, round_p, nil) broadcast ⟨PRECOMMIT, h_p, round_p, nil, precommit_extension⟩ step_p ← precommit } ``` ### Upon receiving a precommit Upon receiving a precommit `precommit`, we ensure that `ABCI.VerifyVoteExtension(precommit.precommit_extension) = true` before accepting the precommit. This is akin to how we check the signature on precommits normally, hence its not wrapped in the syntax of methods from the paper. ### Precommit timeout Upon receiving 2f + 1 precommits, setup a timeout. ```go upon 2f + 1 ⟨PRECOMMIT, h_p, vr, *⟩ for the first time, do { schedule OnTimeoutPrecommit(h_p, round_p) to be executed after timeoutPrecommit(round_p) } ``` with OnTimeoutPrecommit defined as: ```go function OnTimeoutPrecommit(height, round) { if (height = h_p && round = round_p) { StartRound(round_p + 1) } } ``` ### Upon Receiving 2f + 1 precommits The following code is ran upon receiving 2f + 1 precommits for the same block ```go upon ⟨PROPOSAL, h_p, r, v, *⟩ from proposer(h_p, r) AND 2f + 1 ⟨ PRECOMMIT, h_p, r, id(v)⟩ while decision_p[h_p] = nil do { if (valid(v)) { decision_p[h_p] ← v h_p ← h_p + 1 reset lockedRound_p, lockedValue_p,validRound_p and validValue_p to initial values ABCI.FinalizeBlock(id(v)) StartRound(0) } } ``` If we don't see 2f + 1 precommits for the same block, we wait until we get 2f + 1 precommits, and the timeout occurs.