You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5.3 KiB

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

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)

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:

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:

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.

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:

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

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:

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.

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:

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

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.