github.com/aakash4dev/cometbft@v0.38.2/spec/abci/abci++_example_scenarios.md (about) 1 --- 2 order: 6 3 title: ABCI++ extra 4 --- 5 # Introduction 6 7 In the section [CometBFT's expected behaviour](./abci++_comet_expected_behavior.md#valid-method-call-sequences), 8 we presented the most common behaviour, usually referred to as the good case. 9 However, the grammar specified in the same section is more general and covers more scenarios 10 that an Application designer needs to account for. 11 12 In this section, we give more information about these possible scenarios. We focus on methods 13 introduced by ABCI++: `PrepareProposal` and `ProcessProposal`. Specifically, we concentrate 14 on the part of the grammar presented below. 15 16 ```abnf 17 consensus-height = *consensus-round decide commit 18 consensus-round = proposer / non-proposer 19 20 proposer = [prepare-proposal process-proposal] 21 non-proposer = [process-proposal] 22 ``` 23 24 We can see from the grammar that we can have several rounds before deciding a block. The reasons 25 why one round may not be enough are: 26 27 * network asynchrony, and 28 * a Byzantine process being the proposer. 29 30 If we assume that the consensus algorithm decides on block $X$ in round $r$, in the rounds 31 $r' <= r$, CometBFT can exhibit any of the following behaviours: 32 33 1. Call `PrepareProposal` and/or `ProcessProposal` for block $X$. 34 1. Call `PrepareProposal` and/or `ProcessProposal` for block $Y \neq X$. 35 1. Does not call `PrepareProposal` and/or `ProcessProposal`. 36 37 In the rounds in which the process is the proposer, CometBFT's `PrepareProposal` call is always followed by the 38 `ProcessProposal` call. The reason is that the process also broadcasts the proposal to itself, which is locally delivered and triggers the `ProcessProposal` call. 39 The proposal processed by `ProcessProposal` is the same as what was returned by any of the preceding `PrepareProposal` invoked for the same height and round. 40 While in the absence of restarts there is only one such preceding invocations, if the proposer restarts there could have been one extra invocation to `PrepareProposal` for each restart. 41 42 As the number of rounds the consensus algorithm needs to decide in a given run is a priori unknown, the 43 application needs to account for any number of rounds, where each round can exhibit any of these three 44 behaviours. Recall that the application is unaware of the internals of consensus and thus of the rounds. 45 46 # Possible scenarios 47 The unknown number of rounds we can have when following the consensus algorithm yields a vast number of 48 scenarios we can expect. Listing them all is unfeasible. However, here we give several of them and draw the 49 main conclusions. Specifically, we will show that before block $X$ is decided: 50 51 1. On a correct node, `PrepareProposal` may be called multiple times and for different blocks ([**Scenario 1**](#scenario-1)). 52 1. On a correct node, `ProcessProposal` may be called multiple times and for different blocks ([**Scenario 2**](#scenario-2)). 53 1. On a correct node, `PrepareProposal` and `ProcessProposal` for block $X$ may not be called ([**Scenario 3**](#scenario-3)). 54 1. On a correct node, `PrepareProposal` and `ProcessProposal` may not be called at all ([**Scenario 4**](#scenario-4)). 55 56 57 ## Basic information 58 59 Each scenario is presented from the perspective of a process $p$. More precisely, we show what happens in 60 each round's $step$ of the [Tendermint consensus algorithm](https://arxiv.org/pdf/1807.04938.pdf). While in 61 practice the consensus algorithm works with respect to voting power of the validators, in this document 62 we refer to number of processes (e.g., $n$, $f+1$, $2f+1$) for simplicity. The legend is below: 63 64 ### Round X: 65 66 1. **Propose:** Describes what happens while $step_p = propose$. 67 1. **Prevote:** Describes what happens while $step_p = prevote$. 68 1. **Precommit:** Describes what happens while $step_p = precommit$. 69 70 ## Scenario 1 71 72 $p$ calls `ProcessProposal` many times with different values. 73 74 ### Round 0: 75 76 1. **Propose:** The proposer of this round is a Byzantine process, and it chooses not to send the proposal 77 message. Therefore, $p$'s $timeoutPropose$ expires, it sends $Prevote$ for $nil$, and it does not call 78 `ProcessProposal`. All correct processes do the same. 79 1. **Prevote:** $p$ eventually receives $2f+1$ $Prevote$ messages for $nil$ and starts $timeoutPrevote$. 80 When $timeoutPrevote$ expires it sends $Precommit$ for $nil$. 81 1. **Precommit:** $p$ eventually receives $2f+1$ $Precommit$ messages for $nil$ and starts $timeoutPrecommit$. 82 When it expires, it moves to the next round. 83 84 ### Round 1: 85 86 1. **Propose:** A correct process is the proposer in this round. Its $validValue$ is $nil$, and it is free 87 to generate and propose a new block $Y$. Process $p$ receives this proposal in time, calls `ProcessProposal` 88 for block $Y$, and broadcasts a $Prevote$ message for it. 89 1. **Prevote:** Due to network asynchrony less than $2f+1$ processes send $Prevote$ for this block. 90 Therefore, $p$ does not update $validValue$ in this round. 91 1. **Precommit:** Since less than $2f+1$ processes send $Prevote$, no correct process will lock on this 92 block and send $Precommit$ message. As a consequence, $p$ does not decide on $Y$. 93 94 ### Round 2: 95 96 1. **Propose:** Same as in [**Round 1**](#round-1), just another correct process is the proposer, and it 97 proposes another value $Z$. Process $p$ receives the proposal on time, calls `ProcessProposal` for new block 98 $Z$, and broadcasts a $Prevote$ message for it. 99 1. **Prevote:** Same as in [**Round 1**](#round-1). 100 1. **Precommit:** Same as in [**Round 1**](#round-1). 101 102 103 Rounds like these can continue until we have a round in which process $p$ updates its $validValue$ or until 104 we reach round $r$ where process $p$ decides on a block. After that, it will not call `ProcessProposal` 105 anymore for this height. 106 107 ## Scenario 2 108 109 $p$ calls `PrepareProposal` many times with different values. 110 111 ### Round 0: 112 113 1. **Propose:** Process $p$ is the proposer in this round. Its $validValue$ is $nil$, and it is free to 114 generate and propose new block $Y$. Before proposing, it calls `PrepareProposal` for $Y$. After that, it 115 broadcasts the proposal, delivers it to itself, calls `ProcessProposal` and broadcasts $Prevote$ for it. 116 1. **Prevote:** Due to network asynchrony less than $2f+1$ processes receive the proposal on time and send 117 $Prevote$ for it. Therefore, $p$ does not update $validValue$ in this round. 118 1. **Precommit:** Since less than $2f+1$ processes send $Prevote$, no correct process will lock on this 119 block and send non-$nil$ $Precommit$ message. As a consequence, $p$ does not decide on $Y$. 120 121 After this round, we can have multiple rounds like those in [Scenario 1](#scenario-1). The important thing 122 is that process $p$ should not update its $validValue$. Consequently, when process $p$ reaches the round 123 when it is again the proposer, it will ask the mempool for the new block again, and the mempool may return a 124 different block $Z$, and we can have the same round as [Round 0](#round-0-1) just for a different block. As 125 a result, process $p$ calls `PrepareProposal` again but for a different value. When it reaches round $r$ 126 some process will propose block $X$ and if $p$ receives $2f+1$ $Precommit$ messages, it will decide on this 127 value. 128 129 130 ## Scenario 3 131 132 $p$ calls `PrepareProposal` and `ProcessProposal` for many values, but decides on a value for which it did 133 not call `PrepareProposal` or `ProcessProposal`. 134 135 In this scenario, in all rounds before $r$ we can have any round presented in [Scenario 1](#scenario-1) or 136 [Scenario 2](#scenario-2). What is important is that: 137 138 * no proposer proposed block $X$ or if it did, process $p$, due to asynchrony, did not receive it in time, 139 so it did not call `ProcessProposal`, and 140 141 * if $p$ was the proposer it proposed some other value $\neq X$. 142 143 ### Round $r$: 144 145 1. **Propose:** A correct process is the proposer in this round, and it proposes block $X$. 146 Due to asynchrony, the proposal message arrives to process $p$ after its $timeoutPropose$ 147 expires and it sends $Prevote$ for $nil$. Consequently, process $p$ does not call 148 `ProcessProposal` for block $X$. However, the same proposal arrives at other processes 149 before their $timeoutPropose$ expires, and they send $Prevote$ for this proposal. 150 1. **Prevote:** Process $p$ receives $2f+1$ $Prevote$ messages for proposal $X$, updates correspondingly its 151 $validValue$ and $lockedValue$ and sends $Precommit$ message. All correct processes do the same. 152 1. **Precommit:** Finally, process $p$ receives $2f+1$ $Precommit$ messages, and decides on block $X$. 153 154 155 156 ## Scenario 4 157 158 [Scenario 3](#scenario-3) can be translated into a scenario where $p$ does not call `PrepareProposal` and 159 `ProcessProposal` at all. For this, it is necessary that process $p$ is not the proposer in any of the 160 rounds $0 <= r' <= r$ and that due to network asynchrony or Byzantine proposer, it does not receive the 161 proposal before $timeoutPropose$ expires. As a result, it will enter round $r$ without calling 162 `PrepareProposal` and `ProcessProposal` before it, and as shown in Round $r$ of [Scenario 3](#scenario-3) it 163 will decide in this round. Again without calling any of these two calls.