1use crate::PipelineEvent;
2use alloy_eips::eip1898::BlockWithParent;
3use reth_consensus::ConsensusError;
4use reth_errors::{BlockExecutionError, DatabaseError, RethError};
5use reth_network_p2p::error::DownloadError;
6use reth_provider::ProviderError;
7use reth_prune::{PruneSegment, PruneSegmentError, PrunerError};
8use reth_static_file_types::StaticFileSegment;
9use thiserror::Error;
10use tokio::sync::broadcast::error::SendError;
1112/// Represents the specific error type within a block error.
13#[derive(Error, Debug)]
14pub enum BlockErrorKind {
15/// The block encountered a validation error.
16#[error("validation error: {0}")]
17Validation(#[from] ConsensusError),
18/// The block encountered an execution error.
19#[error("execution error: {0}")]
20Execution(#[from] BlockExecutionError),
21}
2223impl BlockErrorKind {
24/// Returns `true` if the error is a state root error.
25pub const fn is_state_root_error(&self) -> bool {
26matches!(self, Self::Validation(err) if err.is_state_root_error())
27 }
28}
2930/// A stage execution error.
31#[derive(Error, Debug)]
32pub enum StageError {
33/// The stage encountered an error related to a block.
34#[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
35Block {
36/// The block that caused the error.
37block: Box<BlockWithParent>,
38/// The specific error type, either consensus or execution error.
39#[source]
40error: BlockErrorKind,
41 },
42/// The stage encountered a downloader error where the responses cannot be attached to the
43 /// current head.
44#[error(
45"stage encountered inconsistent chain: \
46 downloaded header #{header_number} ({header_hash}) is detached from \
47 local head #{head_number} ({head_hash}): {error}",
48 header_number = header.block.number,
49 header_hash = header.block.hash,
50 head_number = local_head.block.number,
51 head_hash = local_head.block.hash,
52 )]
53DetachedHead {
54/// The local head we attempted to attach to.
55local_head: Box<BlockWithParent>,
56/// The header we attempted to attach.
57header: Box<BlockWithParent>,
58/// The error that occurred when attempting to attach the header.
59#[source]
60error: Box<ConsensusError>,
61 },
62/// The headers stage is missing sync gap.
63#[error("missing sync gap")]
64MissingSyncGap,
65/// The stage encountered a database error.
66#[error("internal database error occurred: {0}")]
67Database(#[from] DatabaseError),
68/// Invalid pruning configuration
69#[error(transparent)]
70PruningConfiguration(#[from] PruneSegmentError),
71/// Pruner error
72#[error(transparent)]
73Pruner(#[from] PrunerError),
74/// Invalid checkpoint passed to the stage
75#[error("invalid stage checkpoint: {0}")]
76StageCheckpoint(u64),
77/// Missing download buffer on stage execution.
78 /// Returned if stage execution was called without polling for readiness.
79#[error("missing download buffer")]
80MissingDownloadBuffer,
81/// Download channel closed
82#[error("download channel closed")]
83ChannelClosed,
84/// The stage encountered a database integrity error.
85#[error("database integrity error occurred: {0}")]
86DatabaseIntegrity(#[from] ProviderError),
87/// Invalid download response. Applicable for stages which
88 /// rely on external downloaders
89#[error("invalid download response: {0}")]
90Download(#[from] DownloadError),
91/// Database is ahead of static file data.
92#[error("missing static file data for block number: {number}", number = block.block.number)]
93MissingStaticFileData {
94/// Starting block with missing data.
95block: Box<BlockWithParent>,
96/// Static File segment
97segment: StaticFileSegment,
98 },
99/// The prune checkpoint for the given segment is missing.
100#[error("missing prune checkpoint for {0}")]
101MissingPruneCheckpoint(PruneSegment),
102/// Post Execute Commit error
103#[error("post execute commit error occurred: {_0}")]
104PostExecuteCommit(&'static str),
105/// Internal error
106#[error(transparent)]
107Internal(#[from] RethError),
108/// The stage encountered a recoverable error.
109 ///
110 /// These types of errors are caught by the [Pipeline][crate::Pipeline] and trigger a restart
111 /// of the stage.
112#[error(transparent)]
113Recoverable(Box<dyn core::error::Error + Send + Sync>),
114/// The stage encountered a fatal error.
115 ///
116 /// These types of errors stop the pipeline.
117#[error(transparent)]
118Fatal(Box<dyn core::error::Error + Send + Sync>),
119}
120121impl StageError {
122/// If the error is fatal the pipeline will stop.
123pub const fn is_fatal(&self) -> bool {
124matches!(
125self,
126Self::Database(_) |
127Self::Download(_) |
128Self::DatabaseIntegrity(_) |
129Self::StageCheckpoint(_) |
130Self::MissingDownloadBuffer |
131Self::MissingSyncGap |
132Self::ChannelClosed |
133Self::Internal(_) |
134Self::Fatal(_)
135 )
136 }
137}
138139impl From<std::io::Error> for StageError {
140fn from(source: std::io::Error) -> Self {
141Self::Fatal(Box::new(source))
142 }
143}
144145/// A pipeline execution error.
146#[derive(Error, Debug)]
147pub enum PipelineError {
148/// The pipeline encountered an irrecoverable error in one of the stages.
149#[error(transparent)]
150Stage(#[from] StageError),
151/// The pipeline encountered a database error.
152#[error(transparent)]
153Database(#[from] DatabaseError),
154/// Provider error.
155#[error(transparent)]
156Provider(#[from] ProviderError),
157/// The pipeline encountered an error while trying to send an event.
158#[error("pipeline encountered an error while trying to send an event")]
159Channel(#[from] Box<SendError<PipelineEvent>>),
160/// Internal error
161#[error(transparent)]
162Internal(#[from] RethError),
163/// The pipeline encountered an unwind when `fail_on_unwind` was set to `true`.
164#[error("unexpected unwind")]
165UnexpectedUnwind,
166}