Skip to content

range checks for storage API #7998

@AlexHentschel

Description

@AlexHentschel

At the moment, the storage API naively returns any data it finds in the database (when queried with the appropriate key). For example:

  • store.Headers will return any block header by its ID if that header is found in the database
  • Lets take a look at how we currently bootstrap nodes:
    • the sealing segment contains a section of a finalized fork (blocks B to head in the documentation below):
      // SealingSegment is a continuous segment of recently finalized blocks that contains enough history
      // for a new node to execute its business logic normally. It is part of the data need to initialize
      // a new node to join the network.
      // DETAILED SPECIFICATION: ./sealing_segment.md
      //
      // ├═══════════┤ ├───────────────────────┤
      // ExtraBlocks ^ Blocks ^
      // B head
      //
      // Lets denote the highest block in the sealing segment as `head`. Per convention, `head` must be a
      // finalized block. Consider the chain of blocks leading up to `head` (included). The highest block
      // in chain leading up to `head` that is sealed, we denote as B.
      // In other words, head is the last finalized block, and B is the last sealed block,
      // block at height (B.Height + 1) is not sealed.
      type SealingSegment struct {
    • Block B (which is SealingSegment.Blocks[0]) will be set as the node's root block (👉 code). For the node's root block and above, the protocol state guarantees that all indices are populated.
    • The challenge is that consensus might require information below the node's root block. This information is provided by ExtraBlocks, as explained here (see sealing_segment.md for further details):
      // ExtraBlocks [optional] holds ancestors of `Blocks` in ascending height order.
      // Formally, ExtraBlocks contains at least the additional history to satisfy conditions
      // (see sealing_segment.md for details):
      // (ii) All blocks that are sealed by `head`. This is relevant if `head` contains _multiple_ seals.
      // (iii) The sealing segment holds the history of all non-expired collection guarantees, i.e.
      // limitHeight := max(blockSealedAtHead.Height - flow.DefaultTransactionExpiry, SporkRootBlockHeight)
      // where blockSealedAtHead is the block sealed by `head` block.
      // (Potentially longer history is permitted)
      ExtraBlocks []*Proposal
    • It is important to emphasize that we store these extra blocks in the database (👉 code):

      These blocks are ancestors of segment.Blocks, i.e. below the history cut-off. Therefore, we only persist the extra blocks and index them by height, while all the other indices are omitted, as they would potentially reference non-existent data.

In summary,

  • It is currently possible to query blocks and some other information for blocks below the node's root block. However, this information is not complete.
  • This induces a consistency problem for the Access Node. For example, a client can query by height blocks some blocks that are below the node's root block. But when asked about the children of such block, the Access Node will respond with "this block has no known children" (👉 code). But how can that be, the Access Node knows the finalized block at that height (?) From the client's perspective, this doesn't make sense.

We have discussed this in slack here.

Suggestion

by @peterargue

  • For some storage abstractions (like store.Headers), we could add a wrapper that checks whether the data object is below the node's root block. For headers, this is just a simple height or view comparison.
    • we could error in case a header is below the node's root block
    • we probably need to add additional methods for querying data below the node's root block for the very few places in the code where this is actually intended
  • However, for other data types such as Payloads, this check is not trivial and requires pulling additional data.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions