From 53ccb5d46577426a41a4d451a77e6f01c6949939 Mon Sep 17 00:00:00 2001 From: Matthias Seitz Date: Wed, 8 Jan 2025 14:56:15 +0100 Subject: [PATCH] chore: rm blockchaintree api crate (#13731) --- Cargo.lock | 15 - Cargo.toml | 2 - crates/blockchain-tree-api/Cargo.toml | 26 -- crates/blockchain-tree-api/src/error.rs | 530 ------------------------ crates/blockchain-tree-api/src/lib.rs | 372 ----------------- 5 files changed, 945 deletions(-) delete mode 100644 crates/blockchain-tree-api/Cargo.toml delete mode 100644 crates/blockchain-tree-api/src/error.rs delete mode 100644 crates/blockchain-tree-api/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4eb6c84ec..f83bda895 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6517,21 +6517,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "reth-blockchain-tree-api" -version = "1.1.5" -dependencies = [ - "alloy-consensus", - "alloy-eips", - "alloy-primitives", - "reth-consensus", - "reth-execution-errors", - "reth-primitives", - "reth-primitives-traits", - "reth-storage-errors", - "thiserror 2.0.9", -] - [[package]] name = "reth-chain-state" version = "1.1.5" diff --git a/Cargo.toml b/Cargo.toml index 890b79a14..16933fc7d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,6 @@ exclude = [".github/"] members = [ "bin/reth-bench/", "bin/reth/", - "crates/blockchain-tree-api/", "crates/chain-state/", "crates/chainspec/", "crates/cli/cli/", @@ -303,7 +302,6 @@ op-reth = { path = "crates/optimism/bin" } reth = { path = "bin/reth" } reth-basic-payload-builder = { path = "crates/payload/basic" } reth-bench = { path = "bin/reth-bench" } -reth-blockchain-tree-api = { path = "crates/blockchain-tree-api" } reth-chain-state = { path = "crates/chain-state" } reth-chainspec = { path = "crates/chainspec", default-features = false } reth-cli = { path = "crates/cli/cli" } diff --git a/crates/blockchain-tree-api/Cargo.toml b/crates/blockchain-tree-api/Cargo.toml deleted file mode 100644 index 83ae37809..000000000 --- a/crates/blockchain-tree-api/Cargo.toml +++ /dev/null @@ -1,26 +0,0 @@ -[package] -name = "reth-blockchain-tree-api" -version.workspace = true -edition.workspace = true -rust-version.workspace = true -license.workspace = true -homepage.workspace = true -repository.workspace = true - -[lints] -workspace = true - -[dependencies] -reth-consensus.workspace = true -reth-execution-errors.workspace = true -reth-primitives.workspace = true -reth-primitives-traits.workspace = true -reth-storage-errors.workspace = true - -# alloy -alloy-consensus.workspace = true -alloy-primitives.workspace = true -alloy-eips.workspace = true - -# misc -thiserror.workspace = true diff --git a/crates/blockchain-tree-api/src/error.rs b/crates/blockchain-tree-api/src/error.rs deleted file mode 100644 index ddd7cea79..000000000 --- a/crates/blockchain-tree-api/src/error.rs +++ /dev/null @@ -1,530 +0,0 @@ -//! Error handling for the blockchain tree - -use alloy_consensus::BlockHeader; -use alloy_primitives::{BlockHash, BlockNumber}; -use reth_consensus::ConsensusError; -use reth_execution_errors::{ - BlockExecutionError, BlockValidationError, InternalBlockExecutionError, -}; -use reth_primitives::{SealedBlock, SealedBlockFor}; -use reth_primitives_traits::{Block, BlockBody}; -pub use reth_storage_errors::provider::ProviderError; - -/// Various error cases that can occur when a block violates tree assumptions. -#[derive(Debug, Clone, Copy, thiserror::Error, Eq, PartialEq)] -pub enum BlockchainTreeError { - /// Thrown if the block number is lower than the last finalized block number. - #[error("block number is lower than the last finalized block number #{last_finalized}")] - PendingBlockIsFinalized { - /// The block number of the last finalized block. - last_finalized: BlockNumber, - }, - /// Thrown if no side chain could be found for the block. - #[error("chainId can't be found in BlockchainTree with internal index {chain_id}")] - BlockSideChainIdConsistency { - /// The internal identifier for the side chain. - chain_id: u64, - }, - /// Thrown if a canonical chain header cannot be found. - #[error("canonical chain header {block_hash} can't be found")] - CanonicalChain { - /// The block hash of the missing canonical chain header. - block_hash: BlockHash, - }, - /// Thrown if a block number cannot be found in the blockchain tree chain. - #[error("block number #{block_number} not found in blockchain tree chain")] - BlockNumberNotFoundInChain { - /// The block number that could not be found. - block_number: BlockNumber, - }, - /// Thrown if a block hash cannot be found in the blockchain tree chain. - #[error("block hash {block_hash} not found in blockchain tree chain")] - BlockHashNotFoundInChain { - /// The block hash that could not be found. - block_hash: BlockHash, - }, - /// Thrown if the block failed to buffer - #[error("block with hash {block_hash} failed to buffer")] - BlockBufferingFailed { - /// The block hash of the block that failed to buffer. - block_hash: BlockHash, - }, - /// Thrown when trying to access genesis parent. - #[error("genesis block has no parent")] - GenesisBlockHasNoParent, -} - -/// Canonical Errors -#[derive(thiserror::Error, Debug, Clone)] -pub enum CanonicalError { - /// Error originating from validation operations. - #[error(transparent)] - Validation(#[from] BlockValidationError), - /// Error originating from blockchain tree operations. - #[error(transparent)] - BlockchainTree(#[from] BlockchainTreeError), - /// Error originating from a provider operation. - #[error(transparent)] - Provider(#[from] ProviderError), - /// Error indicating a transaction reverted during execution. - #[error("transaction error on revert: {0}")] - CanonicalRevert(String), - /// Error indicating a transaction failed to commit during execution. - #[error("transaction error on commit: {0}")] - CanonicalCommit(String), - /// Error indicating that a previous optimistic sync target was re-orged - #[error("optimistic sync target was re-orged at block: {0}")] - OptimisticTargetRevert(BlockNumber), -} - -impl CanonicalError { - /// Returns `true` if the error is fatal. - pub const fn is_fatal(&self) -> bool { - matches!(self, Self::CanonicalCommit(_) | Self::CanonicalRevert(_)) - } - - /// Returns `true` if the underlying error matches - /// [`BlockchainTreeError::BlockHashNotFoundInChain`]. - pub const fn is_block_hash_not_found(&self) -> bool { - matches!(self, Self::BlockchainTree(BlockchainTreeError::BlockHashNotFoundInChain { .. })) - } - - /// Returns `Some(BlockNumber)` if the underlying error matches - /// [`CanonicalError::OptimisticTargetRevert`]. - pub const fn optimistic_revert_block_number(&self) -> Option { - match self { - Self::OptimisticTargetRevert(block_number) => Some(*block_number), - _ => None, - } - } -} - -/// Error thrown when inserting a block failed because the block is considered invalid. -#[derive(thiserror::Error)] -#[error(transparent)] -pub struct InsertBlockError { - inner: Box, -} - -// === impl InsertBlockError === - -impl InsertBlockError { - /// Create a new `InsertInvalidBlockError` - pub fn new(block: SealedBlock, kind: InsertBlockErrorKind) -> Self { - Self { inner: InsertBlockErrorData::boxed(block, kind) } - } - - /// Create a new `InsertInvalidBlockError` from a tree error - pub fn tree_error(error: BlockchainTreeError, block: SealedBlock) -> Self { - Self::new(block, InsertBlockErrorKind::Tree(error)) - } - - /// Create a new `InsertInvalidBlockError` from a consensus error - pub fn consensus_error(error: ConsensusError, block: SealedBlock) -> Self { - Self::new(block, InsertBlockErrorKind::Consensus(error)) - } - - /// Create a new `InsertInvalidBlockError` from a consensus error - pub fn sender_recovery_error(block: SealedBlock) -> Self { - Self::new(block, InsertBlockErrorKind::SenderRecovery) - } - - /// Create a new `InsertInvalidBlockError` from an execution error - pub fn execution_error(error: BlockExecutionError, block: SealedBlock) -> Self { - Self::new(block, InsertBlockErrorKind::Execution(error)) - } - - /// Consumes the error and returns the block that resulted in the error - #[inline] - pub fn into_block(self) -> SealedBlock { - self.inner.block - } - - /// Returns the error kind - #[inline] - pub const fn kind(&self) -> &InsertBlockErrorKind { - &self.inner.kind - } - - /// Returns the block that resulted in the error - #[inline] - pub const fn block(&self) -> &SealedBlock { - &self.inner.block - } - - /// Consumes the type and returns the block and error kind. - #[inline] - pub fn split(self) -> (SealedBlock, InsertBlockErrorKind) { - let inner = *self.inner; - (inner.block, inner.kind) - } -} - -impl std::fmt::Debug for InsertBlockError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&self.inner, f) - } -} - -#[derive(thiserror::Error, Debug)] -#[error("Failed to insert block (hash={}, number={}, parent_hash={}): {kind}", - .block.hash(), - .block.number, - .block.parent_hash)] -struct InsertBlockErrorData { - block: SealedBlock, - #[source] - kind: InsertBlockErrorKind, -} - -impl InsertBlockErrorData { - const fn new(block: SealedBlock, kind: InsertBlockErrorKind) -> Self { - Self { block, kind } - } - - fn boxed(block: SealedBlock, kind: InsertBlockErrorKind) -> Box { - Box::new(Self::new(block, kind)) - } -} - -#[derive(thiserror::Error)] -#[error("Failed to insert block (hash={}, number={}, parent_hash={}): {}", - .block.hash(), - .block.number(), - .block.parent_hash(), - .kind)] -struct InsertBlockErrorDataTwo { - block: SealedBlockFor, - #[source] - kind: InsertBlockErrorKindTwo, -} - -impl std::fmt::Debug for InsertBlockErrorDataTwo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("InsertBlockError") - .field("error", &self.kind) - .field("hash", &self.block.hash()) - .field("number", &self.block.number()) - .field("parent_hash", &self.block.parent_hash()) - .field("num_txs", &self.block.body().transactions().len()) - .finish_non_exhaustive() - } -} - -impl InsertBlockErrorDataTwo { - const fn new(block: SealedBlockFor, kind: InsertBlockErrorKindTwo) -> Self { - Self { block, kind } - } - - fn boxed(block: SealedBlockFor, kind: InsertBlockErrorKindTwo) -> Box { - Box::new(Self::new(block, kind)) - } -} - -/// Error thrown when inserting a block failed because the block is considered invalid. -#[derive(thiserror::Error)] -#[error(transparent)] -pub struct InsertBlockErrorTwo { - inner: Box>, -} - -// === impl InsertBlockErrorTwo === - -impl InsertBlockErrorTwo { - /// Create a new `InsertInvalidBlockErrorTwo` - pub fn new(block: SealedBlockFor, kind: InsertBlockErrorKindTwo) -> Self { - Self { inner: InsertBlockErrorDataTwo::boxed(block, kind) } - } - - /// Create a new `InsertInvalidBlockError` from a consensus error - pub fn consensus_error(error: ConsensusError, block: SealedBlockFor) -> Self { - Self::new(block, InsertBlockErrorKindTwo::Consensus(error)) - } - - /// Create a new `InsertInvalidBlockError` from a consensus error - pub fn sender_recovery_error(block: SealedBlockFor) -> Self { - Self::new(block, InsertBlockErrorKindTwo::SenderRecovery) - } - - /// Create a new `InsertInvalidBlockError` from an execution error - pub fn execution_error(error: BlockExecutionError, block: SealedBlockFor) -> Self { - Self::new(block, InsertBlockErrorKindTwo::Execution(error)) - } - - /// Consumes the error and returns the block that resulted in the error - #[inline] - pub fn into_block(self) -> SealedBlockFor { - self.inner.block - } - - /// Returns the error kind - #[inline] - pub const fn kind(&self) -> &InsertBlockErrorKindTwo { - &self.inner.kind - } - - /// Returns the block that resulted in the error - #[inline] - pub const fn block(&self) -> &SealedBlockFor { - &self.inner.block - } - - /// Consumes the type and returns the block and error kind. - #[inline] - pub fn split(self) -> (SealedBlockFor, InsertBlockErrorKindTwo) { - let inner = *self.inner; - (inner.block, inner.kind) - } -} - -impl std::fmt::Debug for InsertBlockErrorTwo { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - std::fmt::Debug::fmt(&self.inner, f) - } -} - -/// All error variants possible when inserting a block -#[derive(Debug, thiserror::Error)] -pub enum InsertBlockErrorKindTwo { - /// Failed to recover senders for the block - #[error("failed to recover senders for block")] - SenderRecovery, - /// Block violated consensus rules. - #[error(transparent)] - Consensus(#[from] ConsensusError), - /// Block execution failed. - #[error(transparent)] - Execution(#[from] BlockExecutionError), - /// Provider error. - #[error(transparent)] - Provider(#[from] ProviderError), - /// Other errors. - #[error(transparent)] - Other(#[from] Box), -} - -impl InsertBlockErrorKindTwo { - /// Returns an [`InsertBlockValidationError`] if the error is caused by an invalid block. - /// - /// Returns an [`InsertBlockFatalError`] if the error is caused by an error that is not - /// validation related or is otherwise fatal. - /// - /// This is intended to be used to determine if we should respond `INVALID` as a response when - /// processing a new block. - pub fn ensure_validation_error( - self, - ) -> Result { - match self { - Self::SenderRecovery => Ok(InsertBlockValidationError::SenderRecovery), - Self::Consensus(err) => Ok(InsertBlockValidationError::Consensus(err)), - // other execution errors that are considered internal errors - Self::Execution(err) => { - match err { - BlockExecutionError::Validation(err) => { - Ok(InsertBlockValidationError::Validation(err)) - } - BlockExecutionError::Consensus(err) => { - Ok(InsertBlockValidationError::Consensus(err)) - } - // these are internal errors, not caused by an invalid block - BlockExecutionError::Internal(error) => { - Err(InsertBlockFatalError::BlockExecutionError(error)) - } - } - } - Self::Provider(err) => Err(InsertBlockFatalError::Provider(err)), - Self::Other(err) => Err(InternalBlockExecutionError::Other(err).into()), - } - } -} - -/// Error variants that are not caused by invalid blocks -#[derive(Debug, thiserror::Error)] -pub enum InsertBlockFatalError { - /// A provider error - #[error(transparent)] - Provider(#[from] ProviderError), - /// An internal / fatal block execution error - #[error(transparent)] - BlockExecutionError(#[from] InternalBlockExecutionError), -} - -/// Error variants that are caused by invalid blocks -#[derive(Debug, thiserror::Error)] -pub enum InsertBlockValidationError { - /// Failed to recover senders for the block - #[error("failed to recover senders for block")] - SenderRecovery, - /// Block violated consensus rules. - #[error(transparent)] - Consensus(#[from] ConsensusError), - /// Validation error, transparently wrapping [`BlockValidationError`] - #[error(transparent)] - Validation(#[from] BlockValidationError), -} - -impl InsertBlockValidationError { - /// Returns true if this is a block pre merge error. - pub const fn is_block_pre_merge(&self) -> bool { - matches!(self, Self::Validation(BlockValidationError::BlockPreMerge { .. })) - } -} - -/// All error variants possible when inserting a block -#[derive(Debug, thiserror::Error)] -pub enum InsertBlockErrorKind { - /// Failed to recover senders for the block - #[error("failed to recover senders for block")] - SenderRecovery, - /// Block violated consensus rules. - #[error(transparent)] - Consensus(#[from] ConsensusError), - /// Block execution failed. - #[error(transparent)] - Execution(#[from] BlockExecutionError), - /// Block violated tree invariants. - #[error(transparent)] - Tree(#[from] BlockchainTreeError), - /// Provider error. - #[error(transparent)] - Provider(#[from] ProviderError), - /// An internal error occurred, like interacting with the database. - #[error(transparent)] - Internal(#[from] Box), - /// Canonical error. - #[error(transparent)] - Canonical(#[from] CanonicalError), -} - -impl InsertBlockErrorKind { - /// Returns true if the error is a tree error - pub const fn is_tree_error(&self) -> bool { - matches!(self, Self::Tree(_)) - } - - /// Returns true if the error is a consensus error - pub const fn is_consensus_error(&self) -> bool { - matches!(self, Self::Consensus(_)) - } - - /// Returns true if this error is a state root error - pub const fn is_state_root_error(&self) -> bool { - // we need to get the state root errors inside of the different variant branches - match self { - Self::Execution(err) => { - matches!( - err, - BlockExecutionError::Validation(BlockValidationError::StateRoot { .. }) - ) - } - Self::Canonical(err) => { - matches!( - err, - CanonicalError::Validation(BlockValidationError::StateRoot { .. }) | - CanonicalError::Provider( - ProviderError::StateRootMismatch(_) | - ProviderError::UnwindStateRootMismatch(_) - ) - ) - } - Self::Provider(err) => { - matches!( - err, - ProviderError::StateRootMismatch(_) | ProviderError::UnwindStateRootMismatch(_) - ) - } - _ => false, - } - } - - /// Returns true if the error is caused by an invalid block - /// - /// This is intended to be used to determine if the block should be marked as invalid. - #[allow(clippy::match_same_arms)] - pub const fn is_invalid_block(&self) -> bool { - match self { - Self::SenderRecovery | Self::Consensus(_) => true, - // other execution errors that are considered internal errors - Self::Execution(err) => { - match err { - BlockExecutionError::Validation(_) | BlockExecutionError::Consensus(_) => { - // this is caused by an invalid block - true - } - // these are internal errors, not caused by an invalid block - BlockExecutionError::Internal(_) => false, - } - } - Self::Tree(err) => { - match err { - BlockchainTreeError::PendingBlockIsFinalized { .. } => { - // the block's number is lower than the finalized block's number - true - } - BlockchainTreeError::BlockSideChainIdConsistency { .. } | - BlockchainTreeError::CanonicalChain { .. } | - BlockchainTreeError::BlockNumberNotFoundInChain { .. } | - BlockchainTreeError::BlockHashNotFoundInChain { .. } | - BlockchainTreeError::BlockBufferingFailed { .. } | - BlockchainTreeError::GenesisBlockHasNoParent => false, - } - } - Self::Provider(_) | Self::Internal(_) => { - // any other error, such as database errors, are considered internal errors - false - } - Self::Canonical(err) => match err { - CanonicalError::BlockchainTree(_) | - CanonicalError::CanonicalCommit(_) | - CanonicalError::CanonicalRevert(_) | - CanonicalError::OptimisticTargetRevert(_) | - CanonicalError::Provider(_) => false, - CanonicalError::Validation(_) => true, - }, - } - } - - /// Returns true if this is a block pre merge error. - pub const fn is_block_pre_merge(&self) -> bool { - matches!( - self, - Self::Execution(BlockExecutionError::Validation( - BlockValidationError::BlockPreMerge { .. } - )) - ) - } - - /// Returns true if the error is an execution error - pub const fn is_execution_error(&self) -> bool { - matches!(self, Self::Execution(_)) - } - - /// Returns true if the error is an internal error - pub const fn is_internal(&self) -> bool { - matches!(self, Self::Internal(_)) - } - - /// Returns the error if it is a tree error - pub const fn as_tree_error(&self) -> Option { - match self { - Self::Tree(err) => Some(*err), - _ => None, - } - } - - /// Returns the error if it is a consensus error - pub const fn as_consensus_error(&self) -> Option<&ConsensusError> { - match self { - Self::Consensus(err) => Some(err), - _ => None, - } - } - - /// Returns the error if it is an execution error - pub const fn as_execution_error(&self) -> Option<&BlockExecutionError> { - match self { - Self::Execution(err) => Some(err), - _ => None, - } - } -} diff --git a/crates/blockchain-tree-api/src/lib.rs b/crates/blockchain-tree-api/src/lib.rs deleted file mode 100644 index 7e1d0d714..000000000 --- a/crates/blockchain-tree-api/src/lib.rs +++ /dev/null @@ -1,372 +0,0 @@ -//! Interfaces and types for interacting with the blockchain tree. -#![doc( - html_logo_url = "https://raw.githubusercontent.com/paradigmxyz/reth/main/assets/reth-docs.png", - html_favicon_url = "https://avatars0.githubusercontent.com/u/97369466?s=256", - issue_tracker_base_url = "https://github.com/paradigmxyz/reth/issues/" -)] -#![cfg_attr(not(test), warn(unused_crate_dependencies))] -#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))] - -use self::error::CanonicalError; -use crate::error::InsertBlockError; -use alloy_eips::BlockNumHash; -use alloy_primitives::{BlockHash, BlockNumber}; -use reth_primitives::{Receipt, SealedBlock, SealedBlockWithSenders, SealedHeader}; -use reth_storage_errors::provider::{ProviderError, ProviderResult}; -use std::collections::BTreeMap; - -pub mod error; - -/// * [`BlockchainTreeEngine::insert_block`]: Connect block to chain, execute it and if valid insert -/// block inside tree. -/// * [`BlockchainTreeEngine::finalize_block`]: Remove chains that join to now finalized block, as -/// chain becomes invalid. -/// * [`BlockchainTreeEngine::make_canonical`]: Check if we have the hash of block that we want to -/// finalize and commit it to db. If we don't have the block, syncing should start to fetch the -/// blocks from p2p. Do reorg in tables if canonical chain if needed. -pub trait BlockchainTreeEngine: BlockchainTreeViewer + Send + Sync { - /// Recover senders and call [`BlockchainTreeEngine::insert_block`]. - /// - /// This will recover all senders of the transactions in the block first, and then try to insert - /// the block. - fn insert_block_without_senders( - &self, - block: SealedBlock, - validation_kind: BlockValidationKind, - ) -> Result { - match block.try_seal_with_senders() { - Ok(block) => self.insert_block(block, validation_kind), - Err(block) => Err(InsertBlockError::sender_recovery_error(block)), - } - } - - /// Recover senders and call [`BlockchainTreeEngine::buffer_block`]. - /// - /// This will recover all senders of the transactions in the block first, and then try to buffer - /// the block. - fn buffer_block_without_senders(&self, block: SealedBlock) -> Result<(), InsertBlockError> { - match block.try_seal_with_senders() { - Ok(block) => self.buffer_block(block), - Err(block) => Err(InsertBlockError::sender_recovery_error(block)), - } - } - - /// Buffer block with senders - fn buffer_block(&self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError>; - - /// Inserts block with senders - /// - /// The `validation_kind` parameter controls which validation checks are performed. - /// - /// Caution: If the block was received from the consensus layer, this should always be called - /// with [`BlockValidationKind::Exhaustive`] to validate the state root, if possible to adhere - /// to the engine API spec. - fn insert_block( - &self, - block: SealedBlockWithSenders, - validation_kind: BlockValidationKind, - ) -> Result; - - /// Finalize blocks up until and including `finalized_block`, and remove them from the tree. - fn finalize_block(&self, finalized_block: BlockNumber) -> ProviderResult<()>; - - /// Reads the last `N` canonical hashes from the database and updates the block indices of the - /// tree by attempting to connect the buffered blocks to canonical hashes. - /// - /// - /// `N` is the maximum of `max_reorg_depth` and the number of block hashes needed to satisfy the - /// `BLOCKHASH` opcode in the EVM. - /// - /// # Note - /// - /// This finalizes `last_finalized_block` prior to reading the canonical hashes (using - /// [`BlockchainTreeEngine::finalize_block`]). - fn connect_buffered_blocks_to_canonical_hashes_and_finalize( - &self, - last_finalized_block: BlockNumber, - ) -> Result<(), CanonicalError>; - - /// Update all block hashes. iterate over present and new list of canonical hashes and compare - /// them. Remove all mismatches, disconnect them, removes all chains and clears all buffered - /// blocks before the tip. - fn update_block_hashes_and_clear_buffered( - &self, - ) -> Result, CanonicalError>; - - /// Reads the last `N` canonical hashes from the database and updates the block indices of the - /// tree by attempting to connect the buffered blocks to canonical hashes. - /// - /// `N` is the maximum of `max_reorg_depth` and the number of block hashes needed to satisfy the - /// `BLOCKHASH` opcode in the EVM. - fn connect_buffered_blocks_to_canonical_hashes(&self) -> Result<(), CanonicalError>; - - /// Make a block and its parent chain part of the canonical chain by committing it to the - /// database. - /// - /// # Note - /// - /// This unwinds the database if necessary, i.e. if parts of the canonical chain have been - /// re-orged. - /// - /// # Returns - /// - /// Returns `Ok` if the blocks were canonicalized, or if the blocks were already canonical. - fn make_canonical(&self, block_hash: BlockHash) -> Result; -} - -/// Represents the kind of validation that should be performed when inserting a block. -/// -/// The motivation for this is that the engine API spec requires that block's state root is -/// validated when received from the CL. -/// -/// This step is very expensive due to how changesets are stored in the database, so we want to -/// avoid doing it if not necessary. Blocks can also originate from the network where this step is -/// not required. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] -pub enum BlockValidationKind { - /// All validation checks that can be performed. - /// - /// This includes validating the state root, if possible. - /// - /// Note: This should always be used when inserting blocks that originate from the consensus - /// layer. - #[default] - Exhaustive, - /// Perform all validation checks except for state root validation. - SkipStateRootValidation, -} - -impl BlockValidationKind { - /// Returns true if the state root should be validated if possible. - pub const fn is_exhaustive(&self) -> bool { - matches!(self, Self::Exhaustive) - } -} - -impl std::fmt::Display for BlockValidationKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Exhaustive => { - write!(f, "Exhaustive") - } - Self::SkipStateRootValidation => { - write!(f, "SkipStateRootValidation") - } - } - } -} - -/// All possible outcomes of a canonicalization attempt of [`BlockchainTreeEngine::make_canonical`]. -#[derive(Debug, Clone, PartialEq, Eq)] -pub enum CanonicalOutcome { - /// The block is already canonical. - AlreadyCanonical { - /// Block number and hash of current head. - head: BlockNumHash, - /// The corresponding [`SealedHeader`] that was attempted to be made a current head and - /// is already canonical. - header: SealedHeader, - }, - /// Committed the block to the database. - Committed { - /// The new corresponding canonical head - head: SealedHeader, - }, -} - -impl CanonicalOutcome { - /// Returns the header of the block that was made canonical. - pub const fn header(&self) -> &SealedHeader { - match self { - Self::AlreadyCanonical { header, .. } => header, - Self::Committed { head } => head, - } - } - - /// Consumes the outcome and returns the header of the block that was made canonical. - pub fn into_header(self) -> SealedHeader { - match self { - Self::AlreadyCanonical { header, .. } => header, - Self::Committed { head } => head, - } - } - - /// Returns true if the block was already canonical. - pub const fn is_already_canonical(&self) -> bool { - matches!(self, Self::AlreadyCanonical { .. }) - } -} - -/// Block inclusion can be valid, accepted, or invalid. Invalid blocks are returned as an error -/// variant. -/// -/// If we don't know the block's parent, we return `Disconnected`, as we can't claim that the block -/// is valid or not. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum BlockStatus2 { - /// The block is valid and block extends canonical chain. - Valid, - /// The block may be valid and has an unknown missing ancestor. - Disconnected { - /// Current canonical head. - head: BlockNumHash, - /// The lowest ancestor block that is not connected to the canonical chain. - missing_ancestor: BlockNumHash, - }, -} - -/// How a payload was inserted if it was valid. -/// -/// If the payload was valid, but has already been seen, [`InsertPayloadOk2::AlreadySeen(_)`] is -/// returned, otherwise [`InsertPayloadOk2::Inserted(_)`] is returned. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum InsertPayloadOk2 { - /// The payload was valid, but we have already seen it. - AlreadySeen(BlockStatus2), - /// The payload was valid and inserted into the tree. - Inserted(BlockStatus2), -} - -/// From Engine API spec, block inclusion can be valid, accepted or invalid. -/// Invalid case is already covered by error, but we need to make distinction -/// between valid blocks that extend canonical chain and the ones that fork off -/// into side chains (see [`BlockAttachment`]). If we don't know the block -/// parent we are returning Disconnected status as we can't make a claim if -/// block is valid or not. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum BlockStatus { - /// If block is valid and block extends canonical chain. - /// In `BlockchainTree` terms, it forks off canonical tip. - Valid(BlockAttachment), - /// If block is valid and block forks off canonical chain. - /// If blocks is not connected to canonical chain. - Disconnected { - /// Current canonical head. - head: BlockNumHash, - /// The lowest ancestor block that is not connected to the canonical chain. - missing_ancestor: BlockNumHash, - }, -} - -/// Represents what kind of block is being executed and validated. -/// -/// This is required to: -/// - differentiate whether trie state updates should be cached. -/// - inform other -/// -/// This is required because the state root check can only be performed if the targeted block can be -/// traced back to the canonical __head__. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BlockAttachment { - /// The `block` is canonical or a descendant of the canonical head. - /// ([`head..(block.parent)*,block`]) - Canonical, - /// The block can be traced back to an ancestor of the canonical head: a historical block, but - /// this chain does __not__ include the canonical head. - HistoricalFork, -} - -impl BlockAttachment { - /// Returns `true` if the block is canonical or a descendant of the canonical head. - #[inline] - pub const fn is_canonical(&self) -> bool { - matches!(self, Self::Canonical) - } -} - -/// How a payload was inserted if it was valid. -/// -/// If the payload was valid, but has already been seen, [`InsertPayloadOk::AlreadySeen(_)`] is -/// returned, otherwise [`InsertPayloadOk::Inserted(_)`] is returned. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub enum InsertPayloadOk { - /// The payload was valid, but we have already seen it. - AlreadySeen(BlockStatus), - /// The payload was valid and inserted into the tree. - Inserted(BlockStatus), -} - -/// Allows read only functionality on the blockchain tree. -/// -/// Tree contains all blocks that are not canonical that can potentially be included -/// as canonical chain. For better explanation we can group blocks into four groups: -/// * Canonical chain blocks -/// * Side chain blocks. Side chain are block that forks from canonical chain but not its tip. -/// * Pending blocks that extend the canonical chain but are not yet included. -/// * Future pending blocks that extend the pending blocks. -pub trait BlockchainTreeViewer: Send + Sync { - /// Returns the header with matching hash from the tree, if it exists. - /// - /// Caution: This will not return headers from the canonical chain. - fn header_by_hash(&self, hash: BlockHash) -> Option; - - /// Returns the block with matching hash from the tree, if it exists. - /// - /// Caution: This will not return blocks from the canonical chain or buffered blocks that are - /// disconnected from the canonical chain. - fn block_by_hash(&self, hash: BlockHash) -> Option; - - /// Returns the block with matching hash from the tree, if it exists. - /// - /// Caution: This will not return blocks from the canonical chain or buffered blocks that are - /// disconnected from the canonical chain. - fn block_with_senders_by_hash(&self, hash: BlockHash) -> Option; - - /// Returns the _buffered_ (disconnected) header with matching hash from the internal buffer if - /// it exists. - /// - /// Caution: Unlike [`Self::block_by_hash`] this will only return headers that are currently - /// disconnected from the canonical chain. - fn buffered_header_by_hash(&self, block_hash: BlockHash) -> Option; - - /// Returns true if the tree contains the block with matching hash. - fn contains(&self, hash: BlockHash) -> bool { - self.block_by_hash(hash).is_some() - } - - /// Return whether or not the block is known and in the canonical chain. - fn is_canonical(&self, hash: BlockHash) -> Result; - - /// Given the hash of a block, this checks the buffered blocks for the lowest ancestor in the - /// buffer. - /// - /// If there is a buffered block with the given hash, this returns the block itself. - fn lowest_buffered_ancestor(&self, hash: BlockHash) -> Option; - - /// Return `BlockchainTree` best known canonical chain tip (`BlockHash`, `BlockNumber`) - fn canonical_tip(&self) -> BlockNumHash; - - /// Return block number and hash that extends the canonical chain tip by one. - /// - /// If there is no such block, this returns `None`. - fn pending_block_num_hash(&self) -> Option; - - /// Returns the pending block if there is one. - fn pending_block(&self) -> Option { - self.block_by_hash(self.pending_block_num_hash()?.hash) - } - - /// Returns the pending block if there is one. - fn pending_block_with_senders(&self) -> Option { - self.block_with_senders_by_hash(self.pending_block_num_hash()?.hash) - } - - /// Returns the pending block and its receipts in one call. - /// - /// This exists to prevent a potential data race if the pending block changes in between - /// [`Self::pending_block`] and [`Self::pending_receipts`] calls. - fn pending_block_and_receipts(&self) -> Option<(SealedBlock, Vec)>; - - /// Returns the pending receipts if there is one. - fn pending_receipts(&self) -> Option> { - self.receipts_by_block_hash(self.pending_block_num_hash()?.hash) - } - - /// Returns the pending receipts if there is one. - fn receipts_by_block_hash(&self, block_hash: BlockHash) -> Option>; - - /// Returns the pending block if there is one. - fn pending_header(&self) -> Option { - self.header_by_hash(self.pending_block_num_hash()?.hash) - } -}