use crate::PipelineEvent;
use alloy_eips::eip1898::BlockWithParent;
use reth_consensus::ConsensusError;
use reth_errors::{BlockExecutionError, DatabaseError, RethError};
use reth_network_p2p::error::DownloadError;
use reth_provider::ProviderError;
use reth_prune::{PruneSegment, PruneSegmentError, PrunerError};
use reth_static_file_types::StaticFileSegment;
use thiserror::Error;
use tokio::sync::broadcast::error::SendError;
#[derive(Error, Debug)]
pub enum BlockErrorKind {
#[error("validation error: {0}")]
Validation(#[from] ConsensusError),
#[error("execution error: {0}")]
Execution(#[from] BlockExecutionError),
}
impl BlockErrorKind {
pub const fn is_state_root_error(&self) -> bool {
match self {
Self::Validation(err) => err.is_state_root_error(),
Self::Execution(err) => err.is_state_root_error(),
}
}
}
#[derive(Error, Debug)]
pub enum StageError {
#[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
Block {
block: Box<BlockWithParent>,
#[source]
error: BlockErrorKind,
},
#[error(
"stage encountered inconsistent chain: \
downloaded header #{header_number} ({header_hash}) is detached from \
local head #{head_number} ({head_hash}): {error}",
header_number = header.block.number,
header_hash = header.block.hash,
head_number = local_head.block.number,
head_hash = local_head.block.hash,
)]
DetachedHead {
local_head: Box<BlockWithParent>,
header: Box<BlockWithParent>,
#[source]
error: Box<ConsensusError>,
},
#[error("missing sync gap")]
MissingSyncGap,
#[error("internal database error occurred: {0}")]
Database(#[from] DatabaseError),
#[error(transparent)]
PruningConfiguration(#[from] PruneSegmentError),
#[error(transparent)]
Pruner(#[from] PrunerError),
#[error("invalid stage checkpoint: {0}")]
StageCheckpoint(u64),
#[error("missing download buffer")]
MissingDownloadBuffer,
#[error("download channel closed")]
ChannelClosed,
#[error("database integrity error occurred: {0}")]
DatabaseIntegrity(#[from] ProviderError),
#[error("invalid download response: {0}")]
Download(#[from] DownloadError),
#[error("missing static file data for block number: {number}", number = block.block.number)]
MissingStaticFileData {
block: Box<BlockWithParent>,
segment: StaticFileSegment,
},
#[error("missing prune checkpoint for {0}")]
MissingPruneCheckpoint(PruneSegment),
#[error("post execute commit error occurred: {_0}")]
PostExecuteCommit(&'static str),
#[error(transparent)]
Internal(#[from] RethError),
#[error(transparent)]
Recoverable(Box<dyn core::error::Error + Send + Sync>),
#[error(transparent)]
Fatal(Box<dyn core::error::Error + Send + Sync>),
}
impl StageError {
pub const fn is_fatal(&self) -> bool {
matches!(
self,
Self::Database(_) |
Self::Download(_) |
Self::DatabaseIntegrity(_) |
Self::StageCheckpoint(_) |
Self::MissingDownloadBuffer |
Self::MissingSyncGap |
Self::ChannelClosed |
Self::Internal(_) |
Self::Fatal(_)
)
}
}
impl From<std::io::Error> for StageError {
fn from(source: std::io::Error) -> Self {
Self::Fatal(Box::new(source))
}
}
#[derive(Error, Debug)]
pub enum PipelineError {
#[error(transparent)]
Stage(#[from] StageError),
#[error(transparent)]
Database(#[from] DatabaseError),
#[error(transparent)]
Provider(#[from] ProviderError),
#[error("pipeline encountered an error while trying to send an event")]
Channel(#[from] Box<SendError<PipelineEvent>>),
#[error(transparent)]
Internal(#[from] RethError),
#[error("unexpected unwind")]
UnexpectedUnwind,
}