mirror of
https://github.com/hl-archive-node/nanoreth.git
synced 2025-12-06 10:59:55 +00:00
refactor: dedicated blockchain insert errors (#2712)
This commit is contained in:
186
crates/interfaces/src/blockchain_tree/error.rs
Normal file
186
crates/interfaces/src/blockchain_tree/error.rs
Normal file
@ -0,0 +1,186 @@
|
||||
//! Error handling for the blockchain tree
|
||||
|
||||
use crate::{consensus::ConsensusError, executor::BlockExecutionError};
|
||||
use reth_primitives::{BlockHash, BlockNumber, SealedBlock};
|
||||
use std::fmt::Formatter;
|
||||
|
||||
/// Various error cases that can occur when a block violates tree assumptions.
|
||||
#[derive(Debug, Clone, Copy, thiserror::Error, Eq, PartialEq)]
|
||||
#[allow(missing_docs)]
|
||||
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("BlockChainId can't be found in BlockchainTree with internal index {chain_id}")]
|
||||
BlockSideChainIdConsistency {
|
||||
/// The internal identifier for the side chain.
|
||||
chain_id: u64,
|
||||
},
|
||||
#[error("Canonical chain header #{block_hash} can't be found ")]
|
||||
CanonicalChain { block_hash: BlockHash },
|
||||
#[error("Block number #{block_number} not found in blockchain tree chain")]
|
||||
BlockNumberNotFoundInChain { block_number: BlockNumber },
|
||||
#[error("Block hash {block_hash} not found in blockchain tree chain")]
|
||||
BlockHashNotFoundInChain { block_hash: BlockHash },
|
||||
}
|
||||
|
||||
/// Error thrown when inserting a block failed because the block is considered invalid.
|
||||
#[derive(Debug, 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))
|
||||
}
|
||||
|
||||
/// Returns the error kind
|
||||
#[inline]
|
||||
pub fn kind(&self) -> &InsertBlockErrorKind {
|
||||
&self.inner.kind
|
||||
}
|
||||
|
||||
/// Returns the invalid block.
|
||||
#[inline]
|
||||
pub fn block(&self) -> &SealedBlock {
|
||||
&self.inner.block
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct InsertBlockErrorData {
|
||||
block: SealedBlock,
|
||||
kind: InsertBlockErrorKind,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for InsertBlockErrorData {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Failed to insert block {:?}: {}", self.block.hash, self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for InsertBlockErrorData {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
Some(&self.kind)
|
||||
}
|
||||
}
|
||||
|
||||
impl InsertBlockErrorData {
|
||||
fn new(block: SealedBlock, kind: InsertBlockErrorKind) -> Self {
|
||||
Self { block, kind }
|
||||
}
|
||||
|
||||
fn boxed(block: SealedBlock, kind: InsertBlockErrorKind) -> Box<Self> {
|
||||
Box::new(Self::new(block, kind))
|
||||
}
|
||||
}
|
||||
|
||||
/// 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(ConsensusError),
|
||||
/// Block execution failed.
|
||||
#[error(transparent)]
|
||||
Execution(BlockExecutionError),
|
||||
/// Block violated tree invariants.
|
||||
#[error(transparent)]
|
||||
Tree(#[from] BlockchainTreeError),
|
||||
/// An internal error occurred, like interacting with the database.
|
||||
#[error("Internal error")]
|
||||
Internal(Box<dyn std::error::Error + Send + Sync>),
|
||||
}
|
||||
|
||||
impl InsertBlockErrorKind {
|
||||
/// Returns true if the error is a tree error
|
||||
pub fn is_tree_error(&self) -> bool {
|
||||
matches!(self, InsertBlockErrorKind::Tree(_))
|
||||
}
|
||||
|
||||
/// Returns true if the error is a consensus error
|
||||
pub fn is_consensus_error(&self) -> bool {
|
||||
matches!(self, InsertBlockErrorKind::Consensus(_))
|
||||
}
|
||||
|
||||
/// Returns true if this is a block pre merge error.
|
||||
pub fn is_block_pre_merge(&self) -> bool {
|
||||
matches!(self, InsertBlockErrorKind::Execution(BlockExecutionError::BlockPreMerge { .. }))
|
||||
}
|
||||
|
||||
/// Returns true if the error is an execution error
|
||||
pub fn is_execution_error(&self) -> bool {
|
||||
matches!(self, InsertBlockErrorKind::Execution(_))
|
||||
}
|
||||
|
||||
/// Returns the error if it is a tree error
|
||||
pub fn as_tree_error(&self) -> Option<BlockchainTreeError> {
|
||||
match self {
|
||||
InsertBlockErrorKind::Tree(err) => Some(*err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the error if it is a consensus error
|
||||
pub fn as_consensus_error(&self) -> Option<&ConsensusError> {
|
||||
match self {
|
||||
InsertBlockErrorKind::Consensus(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the error if it is an execution error
|
||||
pub fn as_execution_error(&self) -> Option<&BlockExecutionError> {
|
||||
match self {
|
||||
InsertBlockErrorKind::Execution(err) => Some(err),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is a convenience impl to convert from crate::Error to InsertBlockErrorKind, most
|
||||
impl From<crate::Error> for InsertBlockErrorKind {
|
||||
fn from(err: crate::Error) -> Self {
|
||||
use crate::Error;
|
||||
|
||||
match err {
|
||||
Error::Execution(err) => InsertBlockErrorKind::Execution(err),
|
||||
Error::Consensus(err) => InsertBlockErrorKind::Consensus(err),
|
||||
Error::Database(err) => InsertBlockErrorKind::Internal(Box::new(err)),
|
||||
Error::Provider(err) => InsertBlockErrorKind::Internal(Box::new(err)),
|
||||
Error::Network(err) => InsertBlockErrorKind::Internal(Box::new(err)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,11 @@
|
||||
use crate::{executor::BlockExecutionError, Error};
|
||||
use crate::{blockchain_tree::error::InsertBlockError, Error};
|
||||
use reth_primitives::{
|
||||
BlockHash, BlockNumHash, BlockNumber, SealedBlock, SealedBlockWithSenders, SealedHeader,
|
||||
};
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
|
||||
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
|
||||
@ -13,22 +15,29 @@ use std::collections::{BTreeMap, HashSet};
|
||||
/// 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`].
|
||||
fn insert_block_without_senders(&self, block: SealedBlock) -> Result<BlockStatus, Error> {
|
||||
let block = block.seal_with_senders().ok_or(BlockExecutionError::SenderRecoveryError)?;
|
||||
self.insert_block(block)
|
||||
fn insert_block_without_senders(
|
||||
&self,
|
||||
block: SealedBlock,
|
||||
) -> Result<BlockStatus, InsertBlockError> {
|
||||
match block.try_seal_with_senders() {
|
||||
Ok(block) => self.insert_block(block),
|
||||
Err(block) => Err(InsertBlockError::sender_recovery_error(block)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Recover senders and call [`BlockchainTreeEngine::buffer_block`].
|
||||
fn buffer_block_without_sender(&self, block: SealedBlock) -> Result<(), Error> {
|
||||
let block = block.seal_with_senders().ok_or(BlockExecutionError::SenderRecoveryError)?;
|
||||
self.buffer_block(block)
|
||||
fn buffer_block_without_sender(&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<(), Error>;
|
||||
/// Buffer block with senders
|
||||
fn buffer_block(&self, block: SealedBlockWithSenders) -> Result<(), InsertBlockError>;
|
||||
|
||||
/// Insert block with senders
|
||||
fn insert_block(&self, block: SealedBlockWithSenders) -> Result<BlockStatus, Error>;
|
||||
fn insert_block(&self, block: SealedBlockWithSenders) -> Result<BlockStatus, InsertBlockError>;
|
||||
|
||||
/// Finalize blocks up until and including `finalized_block`, and remove them from the tree.
|
||||
fn finalize_block(&self, finalized_block: BlockNumber);
|
||||
@ -102,6 +111,11 @@ pub trait BlockchainTreeViewer: Send + Sync {
|
||||
/// Caution: This will not return blocks from the canonical chain.
|
||||
fn block_by_hash(&self, hash: BlockHash) -> Option<SealedBlock>;
|
||||
|
||||
/// Returns true if the tree contains the block with matching hash.
|
||||
fn contains(&self, hash: BlockHash) -> bool {
|
||||
self.block_by_hash(hash).is_some()
|
||||
}
|
||||
|
||||
/// Canonical block number and hashes best known by the tree.
|
||||
fn canonical_blocks(&self) -> BTreeMap<BlockNumber, BlockHash>;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use reth_primitives::{BlockHash, BlockNumHash, BlockNumber, Bloom, H256};
|
||||
use reth_primitives::{BlockHash, BlockNumHash, Bloom, H256};
|
||||
use thiserror::Error;
|
||||
|
||||
/// BlockExecutor Errors
|
||||
@ -22,24 +22,13 @@ pub enum BlockExecutionError {
|
||||
BlockGasUsed { got: u64, expected: u64 },
|
||||
#[error("Provider error")]
|
||||
ProviderError,
|
||||
#[error("BlockChainId can't be found in BlockchainTree with internal index {chain_id}")]
|
||||
BlockSideChainIdConsistency { chain_id: u64 },
|
||||
// TODO(mattsse): move this to tree error
|
||||
#[error("Block hash {block_hash} not found in blockchain tree chain")]
|
||||
BlockHashNotFoundInChain { block_hash: BlockHash },
|
||||
#[error(
|
||||
"Appending chain on fork (other_chain_fork:?) is not possible as the tip is {chain_tip:?}"
|
||||
)]
|
||||
AppendChainDoesntConnect { chain_tip: BlockNumHash, other_chain_fork: BlockNumHash },
|
||||
#[error("Canonical chain header #{block_hash} can't be found ")]
|
||||
CanonicalChain { block_hash: BlockHash },
|
||||
#[error("Can't insert #{block_number} {block_hash} as last finalized block number is {last_finalized}")]
|
||||
PendingBlockIsFinalized {
|
||||
block_hash: BlockHash,
|
||||
block_number: BlockNumber,
|
||||
last_finalized: BlockNumber,
|
||||
},
|
||||
#[error("Block number #{block_number} not found in blockchain tree chain")]
|
||||
BlockNumberNotFoundInChain { block_number: BlockNumber },
|
||||
#[error("Block hash {block_hash} not found in blockchain tree chain")]
|
||||
BlockHashNotFoundInChain { block_hash: BlockHash },
|
||||
#[error("Transaction error on revert: {inner:?}")]
|
||||
CanonicalRevert { inner: String },
|
||||
#[error("Transaction error on commit: {inner:?}")]
|
||||
|
||||
Reference in New Issue
Block a user