CometBFT’s expected behavior

Valid method call sequences

This section describes what the Application can expect from CometBFT.

The Tendermint consensus algorithm, currently adopted in CometBFT, is designed to protect safety under any network conditions, as long as less than 1/3 of validators’ voting power is byzantine. Most of the time, though, the network will behave synchronously, no process will fall behind, and there will be no byzantine process. The following describes what will happen during a block height h in these frequent, benign conditions:

However, the Application logic must be ready to cope with any possible run of the consensus algorithm for a given height, including bad periods (byzantine proposers, network being asynchronous). In these cases, the sequence of calls to ABCI++ methods may not be so straightforward, but the Application should still be able to handle them, e.g., without crashing. The purpose of this section is to define what these sequences look like in a precise way.

As mentioned in the Basic Concepts section, CometBFT acts as a client of ABCI++ and the Application acts as a server. Thus, it is up to CometBFT to determine when and in which order the different ABCI++ methods will be called. A well-written Application design should consider any of these possible sequences.

The following grammar, written in case-sensitive Augmented Backus–Naur form (ABNF, specified in IETF rfc7405), specifies all possible sequences of calls to ABCI++, taken by a correct process, across all heights from the genesis block, including recovery runs, from the point of view of the Application.

start               = clean-start / recovery

clean-start         = init-chain [state-sync] consensus-exec
state-sync          = *state-sync-attempt success-sync info
state-sync-attempt  = offer-snapshot *apply-chunk
success-sync        = offer-snapshot 1*apply-chunk

recovery            = info consensus-exec

consensus-exec      = (inf)consensus-height
consensus-height    = *consensus-round decide commit
consensus-round     = proposer / non-proposer

proposer            = *got-vote [prepare-proposal [process-proposal]] [extend]
extend              = *got-vote extend-vote *got-vote
non-proposer        = *got-vote [process-proposal] [extend]

init-chain          = %s"<InitChain>"
offer-snapshot      = %s"<OfferSnapshot>"
apply-chunk         = %s"<ApplySnapshotChunk>"
info                = %s"<Info>"
prepare-proposal    = %s"<PrepareProposal>"
process-proposal    = %s"<ProcessProposal>"
extend-vote         = %s"<ExtendVote>"
got-vote            = %s"<VerifyVoteExtension>"
decide              = %s"<FinalizeBlock>"
commit              = %s"<Commit>"

We have kept some ABCI methods out of the grammar, in order to keep it as clear and concise as possible. A common reason for keeping all these methods out is that they all can be called at any point in a sequence defined by the grammar above. Other reasons depend on the method in question:

Finally, method Info is a special case. The method’s purpose is three-fold, it can be used

  1. as part of handling an RPC call from an external client,
  2. as a handshake between CometBFT and the Application upon recovery to check whether any blocks need to be replayed, and
  3. at the end of state-sync to verify that the correct state has been reached.

We have left Info’s first purpose out of the grammar for the same reasons as all the others: it can happen at any time, and has nothing to do with the block execution sequence. The second and third purposes, on the other hand, are present in the grammar.

Let us now examine the grammar line by line, providing further details.

start               = clean-start / recovery
clean-start         = init-chain [state-sync] consensus-exec
state-sync          = *state-sync-attempt success-sync info
state-sync-attempt  = offer-snapshot *apply-chunk
success-sync        = offer-snapshot 1*apply-chunk
recovery            = info consensus-exec
consensus-exec      = (inf)consensus-height
consensus-height    = *consensus-round decide commit
consensus-round     = proposer / non-proposer
proposer            = *got-vote [prepare-proposal [process-proposal]] [extend]
extend              = *got-vote extend-vote *got-vote
non-proposer        = *got-vote [process-proposal] [extend]
init-chain          = %s"<InitChain>"
offer-snapshot      = %s"<OfferSnapshot>"
apply-chunk         = %s"<ApplySnapshotChunk>"
info                = %s"<Info>"
prepare-proposal    = %s"<PrepareProposal>"
process-proposal    = %s"<ProcessProposal>"
extend-vote         = %s"<ExtendVote>"
got-vote            = %s"<VerifyVoteExtension>"
decide              = %s"<FinalizeBlock>"
commit              = %s"<Commit>"

Adapting existing Applications that use ABCI

In some cases, an existing Application using the legacy ABCI may need to be adapted to work with ABCI++ with as minimal changes as possible. In this case, of course, ABCI++ will not provide any advantage with respect to the existing implementation, but will keep the same guarantees already provided by ABCI. Here is how ABCI++ methods should be implemented.

First of all, all the methods that did not change from ABCI 0.17.0 to ABCI 2.0, namely Echo, Flush, Info, InitChain, Query, CheckTx, ListSnapshots, LoadSnapshotChunk, OfferSnapshot, and ApplySnapshotChunk, do not need to undergo any changes in their implementation.

As for the new methods:

Finally, Commit, which is kept in ABCI++, no longer returns the AppHash. It is now up to FinalizeBlock to do so. Thus, a slight refactoring of the old Commit implementation will be needed to move the return of AppHash to FinalizeBlock.

Accommodating for vote extensions

In a manner transparent to the application, CometBFT ensures the node is provided with all the data it needs to participate in consensus.

In the case of recovering from a crash, or joining the network via state sync, CometBFT will make sure the node acquires the necessary vote extensions before switching to consensus.

If a node is already in consensus but falls behind, during catch-up, CometBFT will provide the node with vote extensions from past heights by retrieving the extensions within ExtendedCommit for old heights that it had previously stored.

We realize this is sub-optimal due to the increase in storage needed to store the extensions, we are working on an optimization of this implementation which should alleviate this concern. However, the application can use the existing retain_height parameter to decide how much history it wants to keep, just as is done with the block history. The network-wide implications of the usage of retain_height stay the same. The decision to store historical commits and potential optimizations, are discussed in detail in RFC-100

Handling upgrades to ABCI 2.0

If applications upgrade to ABCI 2.0, CometBFT internally ensures that the application setup is reflected in its operation. CometBFT retrieves from the application configuration the value of VoteExtensionsEnableHeight( he,), the height at which vote extensions are required for consensus to proceed, and uses it to determine the data it stores and data it sends to a peer that is catching up.

Namely, upon saving the block for a given height h in the block store at decision time

In the catch-up mechanism, when a node f realizes that another peer is at height hp, which is more than 2 heights behind height hf,

Decorative Orb