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;
11
12#[derive(Error, Debug)]
14pub enum BlockErrorKind {
15 #[error("validation error: {0}")]
17 Validation(#[from] ConsensusError),
18 #[error("execution error: {0}")]
20 Execution(#[from] BlockExecutionError),
21}
22
23impl BlockErrorKind {
24 pub const fn is_state_root_error(&self) -> bool {
26 matches!(self, Self::Validation(err) if err.is_state_root_error())
27 }
28}
29
30#[derive(Error, Debug)]
32pub enum StageError {
33 #[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
35 Block {
36 block: Box<BlockWithParent>,
38 #[source]
40 error: BlockErrorKind,
41 },
42 #[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 )]
53 DetachedHead {
54 local_head: Box<BlockWithParent>,
56 header: Box<BlockWithParent>,
58 #[source]
60 error: Box<ConsensusError>,
61 },
62 #[error("missing sync gap")]
64 MissingSyncGap,
65 #[error("internal database error occurred: {0}")]
67 Database(#[from] DatabaseError),
68 #[error(transparent)]
70 PruningConfiguration(#[from] PruneSegmentError),
71 #[error(transparent)]
73 Pruner(#[from] PrunerError),
74 #[error("invalid stage checkpoint: {0}")]
76 StageCheckpoint(u64),
77 #[error("missing download buffer")]
80 MissingDownloadBuffer,
81 #[error("download channel closed")]
83 ChannelClosed,
84 #[error("database integrity error occurred: {0}")]
86 DatabaseIntegrity(#[from] ProviderError),
87 #[error("invalid download response: {0}")]
90 Download(#[from] DownloadError),
91 #[error("missing static file data for block number: {number}", number = block.block.number)]
93 MissingStaticFileData {
94 block: Box<BlockWithParent>,
96 segment: StaticFileSegment,
98 },
99 #[error("missing prune checkpoint for {0}")]
101 MissingPruneCheckpoint(PruneSegment),
102 #[error("post execute commit error occurred: {_0}")]
104 PostExecuteCommit(&'static str),
105 #[error(transparent)]
107 Internal(#[from] RethError),
108 #[error(transparent)]
113 Recoverable(Box<dyn core::error::Error + Send + Sync>),
114 #[error(transparent)]
118 Fatal(Box<dyn core::error::Error + Send + Sync>),
119}
120
121impl StageError {
122 pub const fn is_fatal(&self) -> bool {
124 matches!(
125 self,
126 Self::Database(_) |
127 Self::Download(_) |
128 Self::DatabaseIntegrity(_) |
129 Self::StageCheckpoint(_) |
130 Self::MissingDownloadBuffer |
131 Self::MissingSyncGap |
132 Self::ChannelClosed |
133 Self::Internal(_) |
134 Self::Fatal(_)
135 )
136 }
137}
138
139impl From<std::io::Error> for StageError {
140 fn from(source: std::io::Error) -> Self {
141 Self::Fatal(Box::new(source))
142 }
143}
144
145#[derive(Error, Debug)]
147pub enum PipelineError {
148 #[error(transparent)]
150 Stage(#[from] StageError),
151 #[error(transparent)]
153 Database(#[from] DatabaseError),
154 #[error(transparent)]
156 Provider(#[from] ProviderError),
157 #[error("pipeline encountered an error while trying to send an event")]
159 Channel(#[from] Box<SendError<PipelineEvent>>),
160 #[error(transparent)]
162 Internal(#[from] RethError),
163 #[error("unexpected unwind")]
165 UnexpectedUnwind,
166}