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.