# Tendermint v0 Markdown pseudocode This translates the latex code for Tendermint consensus from the Tendermint paper into markdown. ### Initialization ```go h_p ← 0 round_p ← 0 step_p is one of {propose, prevote, precommit} decision_p ← Vector() lockedRound_p ← -1 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 { proposal ← getValue() } 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) ∧ (lockedRound_p = −1 ∨ lockedValue_p = v) { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { 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, 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) ∧ (lockedRound_p ≤ vr ∨ lockedValue_p = v) { broadcast ⟨PREVOTE, h_p, round_p, id(v)⟩ } else { 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, *⟩ 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) { broadcast ⟨PRECOMMIT, h_p, round_p, nil⟩ 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, round_p, id(v)⟩ while valid(v) ∧ step_p >= prevote for the first time do { if (step_p = prevote) { lockedValue_p ← v lockedRound_p ← round_p broadcast ⟨PRECOMMIT, h_p, round_p, id(v)⟩ 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 { broadcast ⟨PRECOMMIT, h_p, round_p, nil⟩ step_p ← precommit } ``` ### 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 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.