refactor: dedicated blockchain insert errors (#2712)

This commit is contained in:
Matthias Seitz
2023-05-18 15:00:47 +02:00
committed by GitHub
parent 149cac060a
commit 8fee5d3e28
10 changed files with 401 additions and 126 deletions

View 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)),
}
}
}

View File

@ -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>;

View File

@ -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:?}")]