Files
tendermint/spec/abci++/v4.md

199 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Tendermint v4 Markdown pseudocode
This is a multi-threaded implementation of ABCI++,
where ProcessProposal starts when the proposal is received, but ends before precommitting.
### 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, 1) from proposer(h_p, round_p) while step_p = propose do {
if valid(v) ABCI.VerifyHeader(h_p, v.header) (lockedRound_p = 1 lockedValue_p = v) {
// We fork process proposal into a parallel process
Fork ABCI.ProcessProposal(h_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) ABCI.VerifyHeader(h_p, v.header) (lockedRound_p vr lockedValue_p = v) {
// We fork process proposal into a parallel process
Fork ABCI.ProcessProposal(h_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, -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
def OnTimeoutPrevote(height, round) {
if (height = h_p && round = round_p && step_p = prevote) {
// Join the ProcessProposal, and output any evidence in case it has some.
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
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
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
// If the proposal is valid precommit as before.
// If it was invalid, precommit nil.
// Note that ABCI.ProcessProposal(h_p, v).accept is deterministic for all honest nodes.
precommit_value nil
if processProposalOutput.accept {
precommit_value id(v)
}
precommit_extension ABCI.ExtendVote(h_p, round_p, precommit_value)
broadcast PRECOMMIT, h_p, round_p, precommit_value, precommit_extension
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
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 {
// Join ABCI.ProcessProposal, and broadcast any evidence if it exists.
processProposalOutput Join ABCI.ProcessProposal(h_p, v)
for evidence in processProposalOutput.evidence_list {
broadcast EVIDENCE, evidence
}
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
def 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.