unfinalized_ancestor_blocks.rs
Overview
This file provides functionality to select and retrieve a chain of unfinalized ancestor blocks from a blockchain-like data structure, starting from a given tail block and moving backward through its ancestry. It defines an error enumeration to represent various failure modes encountered during the retrieval process, a trait specifying the block selection behavior, and an implementation of that trait for a block repository. The main purpose is to traverse the block history while respecting finalization state, sequence number cutoffs, and invalidation conditions.
Error Enumeration: UnfinalizedAncestorBlocksSelectError
This enumeration defines the possible errors that can arise when selecting unfinalized ancestor blocks:
IncompleteHistory: Indicates that some expected block metadata (like sequence number or parent identifier) is missing, preventing further traversal.
BlockSeqNoCutoff(Vec): Triggered when the recursion encounters a block whose sequence number is below a specified cutoff threshold. The vector contains the chain of blocks traversed up to that point.
InvalidatedParent(Vec): Occurs if an ancestor block is found to be invalidated but not finalized, which is considered an error condition in this context. The vector contains the chain leading to this invalidated block.
FailedToLoadBlockState: Indicates failure to load a block state from the repository during traversal.
Debug Implementation
A custom Debug trait implementation provides concise, human-readable string representations for each error variant, including the chain of blocks when relevant.
Trait: UnfinalizedAncestorBlocks
Defines a contract for selecting unfinalized ancestor blocks from a given starting block:
Method: select_unfinalized_ancestor_blocks
fn select_unfinalized_ancestor_blocks(
&self,
tail: &BlockState,
cutoff: BlockSeqNo,
) -> anyhow::Result<Vec<BlockState>, UnfinalizedAncestorBlocksSelectError>;
Parameters:
tail: Reference to the startingBlockStatefrom which to begin ancestor selection.cutoff: ABlockSeqNovalue representing the minimum sequence number boundary; traversal stops if a block's sequence number is below this cutoff.
Returns:
On success, returns a vector of
BlockStateobjects representing the chain of unfinalized ancestor blocks, ordered from oldest to newest (closest to the tail).On failure, returns an error variant from
UnfinalizedAncestorBlocksSelectError.
Usage Example:
let ancestors_result = repository.select_unfinalized_ancestor_blocks(&tail_block, cutoff_seq_no);
match ancestors_result {
Ok(ancestors) => {
// Process the chain of unfinalized ancestors
}
Err(e) => {
// Handle errors such as incomplete history or cutoff reached
}
}
Implementation for BlockStateRepository
The trait UnfinalizedAncestorBlocks is implemented for BlockStateRepository, enabling repository instances to perform ancestor selection.
Algorithm Details
Initialize an empty vector
chainto accumulate ancestor blocks.Set
cursorto the given tail block.Enter a loop:
Use the
guardedmethod oncursorto safely access its inner data and extract:Whether the block is finalized.
The parent block identifier.
The block sequence number.
If the block is invalidated but not finalized, return an
InvalidatedParenterror including the chain so far.If any required metadata is missing, return an
IncompleteHistoryerror.If the current block is finalized, return the accumulated chain in the correct order (oldest first).
If the block sequence number is below the cutoff, return a
BlockSeqNoCutofferror including the chain.Otherwise, push the current block to the chain and advance the cursor to its parent block by loading it from the repository.
If loading the parent block fails, return a
FailedToLoadBlockStateerror.
This traversal ensures correctness by stopping at finalized blocks, respecting the cutoff limit, and validating block states to prevent processing invalid or incomplete histories.
Test Module
The test module contains unit tests verifying the error behavior of the ancestor selection:
Test Function:
a_really_old_fork_must_return_cutoff_errorThis test constructs a scenario with a tail block referencing a very old finalized block, sets a cutoff sequence number, and asserts that the selection returns a
BlockSeqNoCutofferror with the expected chain. It also demonstrates usage ofguarded_mutto mutate block state safely during setup.
Interaction with Other Components
BlockStateRepository: This file extends the repository's capabilities by adding ancestor block selection functionality.
BlockState: Represents individual block metadata and state; accessed and manipulated using guarded methods to ensure thread safety and data consistency.
BlockIdentifier, BlockSeqNo: Types used to uniquely identify blocks and represent their order in the chain.
Guarded: A utility abstraction that provides safe access to encapsulated mutable or immutable data with error handling.
The ancestor selection relies on querying the repository for parent blocks and inspecting their states to build the chain.
Mermaid Diagram: Structure of unfinalized_ancestor_blocks.rs
classDiagram
class UnfinalizedAncestorBlocksSelectError {
<<enum>>
+IncompleteHistory
+BlockSeqNoCutoff(chain: Vec<BlockState>)
+InvalidatedParent(chain: Vec<BlockState>)
+FailedToLoadBlockState
+fmt()
}
class UnfinalizedAncestorBlocks {
<<trait>>
+select_unfinalized_ancestor_blocks(tail: &BlockState, cutoff: BlockSeqNo) Result<Vec<BlockState>, UnfinalizedAncestorBlocksSelectError>
}
class BlockStateRepository {
+select_unfinalized_ancestor_blocks(tail: &BlockState, cutoff: BlockSeqNo) Result<Vec<BlockState>, UnfinalizedAncestorBlocksSelectError>
+get(block_id: &BlockIdentifier) -> Result<BlockState>
}
UnfinalizedAncestorBlocksSelectError <|-- UnfinalizedAncestorBlocksSelectError
UnfinalizedAncestorBlocks <|.. BlockStateRepository