github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/state/fork/Readme.md (about)

     1  # Traversing a Fork
     2  
     3  Generally, when traversing a fork (in either direction), there are two distinct blocks:
     4   * the `head` of the fork that should be traversed
     5   * the `lowestBlock` in that fork, that should be included in the traversal
     6  
     7  The traversal the walks `head <--> lowestBlock` (in either direction).
     8  
     9  There are a variety of ways to precisely specify `head` and `lowestBlock`:
    10   * At least one block, `head` or `lowestBlock`, must be specified by its ID
    11     to unambiguously identify the fork that should be traversed.
    12   * The other block can either be specified by ID or height.
    13   * If both `head` and `lowestBlock` are specified by their ID,
    14     they must both be on the same fork.
    15  
    16  ### Convention
    17  
    18  **For the core traversal algorithms, we use the following conventions:** 
    19  1. The `head` of the fork that should be traversed is specified by its `ID`
    20  2. The `lowestBlock` is specified by its height: `lowestHeightToVisit`.
    21  3. If `head.Height < lowestHeightToVisit`, no blocks are visited and the 
    22     traversal returns immediately (without error). 
    23  
    24  This design is inspired by a `for` loop. For example, lets consider the case where we start 
    25  at the fork `head` and traverse backwards until we reach a block with height `lowestHeightToVisit`:
    26  ```golang
    27  for block := head; block.Height >= lowestHeightToVisit; block = block.Parent {
    28  	visit(block)
    29  }
    30  ```
    31  
    32  For the core traversal algorithms, the parametrization of `head` [type `flow.ID`],
    33  `lowestHeightToVisit` [type `uint64`], and direction is beneficial, as there are no inconsistent parameter sets.
    34  (There is the edge-case of the root block, which we ignore in this high-level discussion)
    35  
    36  
    37  
    38  
    39  ### Terminals
    40  
    41  Higher-level algorithms that want to collect specific data from a fork have a variety of
    42  different termination conditions, such as:
    43  * walk backwards up to (_including_) the block with ID `xyz` or _excluding_ the block with ID `xyz`
    44  
    45  To provide higher-level primitives for terminating the fork traversal, we include
    46  `Terminals`. Essentially, a `Terminal` converts a high-level condition for the `lowestBlock`
    47  to a suitable parametrization for the core traversal algorithm:
    48  
    49  * The height `lowestHeightToVisit` of the lowest block that should be visited.
    50  * A consistency check `ConfirmTerminalReached` for the lowest visited block.
    51    This check is predominantly useful
    52    in case both `head` and `lowestBlock` are specified by their ID. It allows to enforce that
    53    `head` and `lowestBlock` are both on the same fork and error otherwise.
    54    
    55    However, the precise implementation of `ConfirmTerminalReached` is left to the Terminal.
    56    Other, more elaborate conditions  are possible.