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.