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.
 
 
 
 
 
 

3.9 KiB

Tendermint v0 Markdown pseudocode

This translates the latex code for Tendermint consensus from the Tendermint paper into markdown.

Initialization

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)

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:

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:

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.

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:

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

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:

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.

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