|
|
- # 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.
|