mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
chore: rm blockchaintree api crate (#13731)
This commit is contained in:
15
Cargo.lock
generated
15
Cargo.lock
generated
@ -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"
|
||||
|
||||
@ -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" }
|
||||
|
||||
@ -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
|
||||
@ -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<BlockNumber> {
|
||||
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<InsertBlockErrorData>,
|
||||
}
|
||||
|
||||
// === 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<Self> {
|
||||
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<B: Block> {
|
||||
block: SealedBlockFor<B>,
|
||||
#[source]
|
||||
kind: InsertBlockErrorKindTwo,
|
||||
}
|
||||
|
||||
impl<B: Block> std::fmt::Debug for InsertBlockErrorDataTwo<B> {
|
||||
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<B: Block> InsertBlockErrorDataTwo<B> {
|
||||
const fn new(block: SealedBlockFor<B>, kind: InsertBlockErrorKindTwo) -> Self {
|
||||
Self { block, kind }
|
||||
}
|
||||
|
||||
fn boxed(block: SealedBlockFor<B>, kind: InsertBlockErrorKindTwo) -> Box<Self> {
|
||||
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<B: Block> {
|
||||
inner: Box<InsertBlockErrorDataTwo<B>>,
|
||||
}
|
||||
|
||||
// === impl InsertBlockErrorTwo ===
|
||||
|
||||
impl<B: Block> InsertBlockErrorTwo<B> {
|
||||
/// Create a new `InsertInvalidBlockErrorTwo`
|
||||
pub fn new(block: SealedBlockFor<B>, 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<B>) -> Self {
|
||||
Self::new(block, InsertBlockErrorKindTwo::Consensus(error))
|
||||
}
|
||||
|
||||
/// Create a new `InsertInvalidBlockError` from a consensus error
|
||||
pub fn sender_recovery_error(block: SealedBlockFor<B>) -> Self {
|
||||
Self::new(block, InsertBlockErrorKindTwo::SenderRecovery)
|
||||
}
|
||||
|
||||
/// Create a new `InsertInvalidBlockError` from an execution error
|
||||
pub fn execution_error(error: BlockExecutionError, block: SealedBlockFor<B>) -> 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<B> {
|
||||
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<B> {
|
||||
&self.inner.block
|
||||
}
|
||||
|
||||
/// Consumes the type and returns the block and error kind.
|
||||
#[inline]
|
||||
pub fn split(self) -> (SealedBlockFor<B>, InsertBlockErrorKindTwo) {
|
||||
let inner = *self.inner;
|
||||
(inner.block, inner.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl<B: Block> std::fmt::Debug for InsertBlockErrorTwo<B> {
|
||||
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<dyn core::error::Error + Send + Sync + 'static>),
|
||||
}
|
||||
|
||||
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<InsertBlockValidationError, InsertBlockFatalError> {
|
||||
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<dyn core::error::Error + Send + Sync>),
|
||||
/// 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<BlockchainTreeError> {
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<InsertPayloadOk, InsertBlockError> {
|
||||
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<InsertPayloadOk, InsertBlockError>;
|
||||
|
||||
/// 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<BTreeMap<BlockNumber, BlockHash>, 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<CanonicalOutcome, CanonicalError>;
|
||||
}
|
||||
|
||||
/// 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<SealedHeader>;
|
||||
|
||||
/// 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<SealedBlock>;
|
||||
|
||||
/// 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<SealedBlockWithSenders>;
|
||||
|
||||
/// 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<SealedHeader>;
|
||||
|
||||
/// 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<bool, ProviderError>;
|
||||
|
||||
/// 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<SealedBlockWithSenders>;
|
||||
|
||||
/// 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<BlockNumHash>;
|
||||
|
||||
/// Returns the pending block if there is one.
|
||||
fn pending_block(&self) -> Option<SealedBlock> {
|
||||
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<SealedBlockWithSenders> {
|
||||
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<Receipt>)>;
|
||||
|
||||
/// Returns the pending receipts if there is one.
|
||||
fn pending_receipts(&self) -> Option<Vec<Receipt>> {
|
||||
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<Vec<Receipt>>;
|
||||
|
||||
/// Returns the pending block if there is one.
|
||||
fn pending_header(&self) -> Option<SealedHeader> {
|
||||
self.header_by_hash(self.pending_block_num_hash()?.hash)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user