github.com/KYVENetwork/cometbft/v38@v38.0.3/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 48 The unknown number of rounds we can have when following the consensus algorithm yields a vast number of 49 scenarios we can expect. Listing them all is unfeasible. However, here we give several of them and draw the 50 main conclusions. Specifically, we will show that before block $X$ is decided: 51 52 1. On a correct node, `PrepareProposal` may be called multiple times and for different blocks ([**Scenario 1**](#scenario-1)). 53 1. On a correct node, `ProcessProposal` may be called multiple times and for different blocks ([**Scenario 2**](#scenario-2)). 54 1. On a correct node, `PrepareProposal` and `ProcessProposal` for block $X$ may not be called ([**Scenario 3**](#scenario-3)). 55 1. On a correct node, `PrepareProposal` and `ProcessProposal` may not be called at all ([**Scenario 4**](#scenario-4)). 56 57 58 ## Basic information 59 60 Each scenario is presented from the perspective of a process $p$. More precisely, we show what happens in 61 each round's $step$ of the [Tendermint consensus algorithm](https://arxiv.org/pdf/1807.04938.pdf). While in 62 practice the consensus algorithm works with respect to voting power of the validators, in this document 63 we refer to number of processes (e.g., $n$, $f+1$, $2f+1$) for simplicity. The legend is below: 64 65 ### Round X 66 67 1. **Propose:** Describes what happens while $step_p = propose$. 68 1. **Prevote:** Describes what happens while $step_p = prevote$. 69 1. **Precommit:** Describes what happens while $step_p = precommit$. 70 71 ## Scenario 1 72 73 $p$ calls `ProcessProposal` many times with different values. 74 75 ### Round 0 76 77 1. **Propose:** The proposer of this round is a Byzantine process, and it chooses not to send the proposal 78 message. Therefore, $p$'s $timeoutPropose$ expires, it sends $Prevote$ for $nil$, and it does not call 79 `ProcessProposal`. All correct processes do the same. 80 1. **Prevote:** $p$ eventually receives $2f+1$ $Prevote$ messages for $nil$ and starts $timeoutPrevote$. 81 When $timeoutPrevote$ expires it sends $Precommit$ for $nil$. 82 1. **Precommit:** $p$ eventually receives $2f+1$ $Precommit$ messages for $nil$ and starts $timeoutPrecommit$. 83 When it expires, it moves to the next round. 84 85 ### Round 1 86 87 1. **Propose:** A correct process is the proposer in this round. Its $validValue$ is $nil$, and it is free 88 to generate and propose a new block $Y$. Process $p$ receives this proposal in time, calls `ProcessProposal` 89 for block $Y$, and broadcasts a $Prevote$ message for it. 90 1. **Prevote:** Due to network asynchrony less than $2f+1$ processes send $Prevote$ for this block. 91 Therefore, $p$ does not update $validValue$ in this round. 92 1. **Precommit:** Since less than $2f+1$ processes send $Prevote$, no correct process will lock on this 93 block and send $Precommit$ message. As a consequence, $p$ does not decide on $Y$. 94 95 ### Round 2 96 97 1. **Propose:** Same as in [**Round 1**](#round-1), just another correct process is the proposer, and it 98 proposes another value $Z$. Process $p$ receives the proposal on time, calls `ProcessProposal` for new block 99 $Z$, and broadcasts a $Prevote$ message for it. 100 1. **Prevote:** Same as in [**Round 1**](#round-1). 101 1. **Precommit:** Same as in [**Round 1**](#round-1). 102 103 104 Rounds like these can continue until we have a round in which process $p$ updates its $validValue$ or until 105 we reach round $r$ where process $p$ decides on a block. After that, it will not call `ProcessProposal` 106 anymore for this height. 107 108 ## Scenario 2 109 110 $p$ calls `PrepareProposal` many times with different values. 111 112 ### Round 0 113 114 1. **Propose:** Process $p$ is the proposer in this round. Its $validValue$ is $nil$, and it is free to 115 generate and propose new block $Y$. Before proposing, it calls `PrepareProposal` for $Y$. After that, it 116 broadcasts the proposal, delivers it to itself, calls `ProcessProposal` and broadcasts $Prevote$ for it. 117 1. **Prevote:** Due to network asynchrony less than $2f+1$ processes receive the proposal on time and send 118 $Prevote$ for it. Therefore, $p$ does not update $validValue$ in this round. 119 1. **Precommit:** Since less than $2f+1$ processes send $Prevote$, no correct process will lock on this 120 block and send non-$nil$ $Precommit$ message. As a consequence, $p$ does not decide on $Y$. 121 122 After this round, we can have multiple rounds like those in [Scenario 1](#scenario-1). The important thing 123 is that process $p$ should not update its $validValue$. Consequently, when process $p$ reaches the round 124 when it is again the proposer, it will ask the mempool for the new block again, and the mempool may return a 125 different block $Z$, and we can have the same round as [Round 0](#round-0-1) just for a different block. As 126 a result, process $p$ calls `PrepareProposal` again but for a different value. When it reaches round $r$ 127 some process will propose block $X$ and if $p$ receives $2f+1$ $Precommit$ messages, it will decide on this 128 value. 129 130 131 ## Scenario 3 132 133 $p$ calls `PrepareProposal` and `ProcessProposal` for many values, but decides on a value for which it did 134 not call `PrepareProposal` or `ProcessProposal`. 135 136 In this scenario, in all rounds before $r$ we can have any round presented in [Scenario 1](#scenario-1) or 137 [Scenario 2](#scenario-2). What is important is that: 138 139 * no proposer proposed block $X$ or if it did, process $p$, due to asynchrony, did not receive it in time, 140 so it did not call `ProcessProposal`, and 141 142 * if $p$ was the proposer it proposed some other value $\neq X$. 143 144 ### Round $r$ 145 146 1. **Propose:** A correct process is the proposer in this round, and it proposes block $X$. 147 Due to asynchrony, the proposal message arrives to process $p$ after its $timeoutPropose$ 148 expires and it sends $Prevote$ for $nil$. Consequently, process $p$ does not call 149 `ProcessProposal` for block $X$. However, the same proposal arrives at other processes 150 before their $timeoutPropose$ expires, and they send $Prevote$ for this proposal. 151 1. **Prevote:** Process $p$ receives $2f+1$ $Prevote$ messages for proposal $X$, updates correspondingly its 152 $validValue$ and $lockedValue$ and sends $Precommit$ message. All correct processes do the same. 153 1. **Precommit:** Finally, process $p$ receives $2f+1$ $Precommit$ messages, and decides on block $X$. 154 155 156 157 ## Scenario 4 158 159 [Scenario 3](#scenario-3) can be translated into a scenario where $p$ does not call `PrepareProposal` and 160 `ProcessProposal` at all. For this, it is necessary that process $p$ is not the proposer in any of the 161 rounds $0 <= r' <= r$ and that due to network asynchrony or Byzantine proposer, it does not receive the 162 proposal before $timeoutPropose$ expires. As a result, it will enter round $r$ without calling 163 `PrepareProposal` and `ProcessProposal` before it, and as shown in Round $r$ of [Scenario 3](#scenario-3) it 164 will decide in this round. Again without calling any of these two calls.