# Tendermint v3 Markdown pseudocode This is a single-threaded implementation of ABCI++, with an optimization for the ProcessProposal phase. Namely, processing of the header and the block data is separated into two different functions. ### 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 fills in header unpreparedProposal ← getUnpreparedBlockProposal(txdata) 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_header, −1) from proposer(h_p, round_p) while step_p = propose do { prevote_nil ← false // valid is Tendermints validation, ABCI.VerifyHeader is the applications if valid(v_header) ∧ ABCI.VerifyHeader(h_p, v_header) ∧ (lockedRound_p = −1 ∨ lockedValue_p = id(v_header)) { wait to receive proposal v corresponding to v_header // We split up the app's header verification from the remainder of its processing of the proposal if ABCI.ProcessProposal(h_p, v).accept { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { prevote_nil ← true // 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⟩ } } } else { prevote_nil ← true } if prevote_nil { broadcast ⟨PREVOTE, h_p, round_p, nil⟩ } 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_header, vr⟩ from proposer(h_p, round_p) AND 2f + 1 ⟨PREVOTE, h_p, vr, id(v_header)⟩ while step_p = propose ∧ (vr ≥ 0 ∧ vr < round_p) do { prevote_nil ← false if valid(v) ∧ ABCI.VerifyHeader(h_p, v.header) ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { wait to receive proposal v corresponding to v_header // We split up the app's header verification from the remainder of its processing of the proposal if ABCI.ProcessProposal(h_p, v).accept { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { prevote_nil ← true // 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⟩ } } } else { prevote_nil ← true } if prevote_nil { broadcast ⟨PREVOTE, h_p, round_p, nil⟩ } 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.