1use crate::PipelineEvent;
2use alloy_eips::eip1898::BlockWithParent;
3use reth_codecs::DecompressError;
4use reth_consensus::ConsensusError;
5use reth_errors::{BlockExecutionError, DatabaseError, RethError};
6use reth_network_p2p::error::DownloadError;
7use reth_provider::ProviderError;
8use reth_prune::{PruneSegment, PruneSegmentError, PrunerError, UnwindTargetPrunedError};
9use reth_static_file_types::StaticFileSegment;
10use thiserror::Error;
11use tokio::sync::broadcast::error::SendError;
12
13#[derive(Error, Debug)]
15pub enum BlockErrorKind {
16 #[error("validation error: {0}")]
18 Validation(#[from] ConsensusError),
19 #[error("execution error: {0}")]
21 Execution(#[from] BlockExecutionError),
22}
23
24impl BlockErrorKind {
25 pub const fn is_state_root_error(&self) -> bool {
27 matches!(self, Self::Validation(err) if err.is_state_root_error())
28 }
29}
30
31#[derive(Error, Debug)]
33pub enum StageError {
34 #[error("stage encountered an error in block #{number}: {error}", number = block.block.number)]
36 Block {
37 block: Box<BlockWithParent>,
39 #[source]
41 error: BlockErrorKind,
42 },
43 #[error(
46 "stage encountered inconsistent chain: \
47 downloaded header #{header_number} ({header_hash}) is detached from \
48 local head #{head_number} ({head_hash}): {error}",
49 header_number = header.block.number,
50 header_hash = header.block.hash,
51 head_number = local_head.block.number,
52 head_hash = local_head.block.hash,
53 )]
54 DetachedHead {
55 local_head: Box<BlockWithParent>,
57 header: Box<BlockWithParent>,
59 #[source]
61 error: Box<ConsensusError>,
62 },
63 #[error("missing sync gap")]
65 MissingSyncGap,
66 #[error("internal database error occurred: {0}")]
68 Database(#[from] DatabaseError),
69 #[error(transparent)]
71 PruningConfiguration(#[from] PruneSegmentError),
72 #[error(transparent)]
74 Pruner(#[from] PrunerError),
75 #[error("invalid stage checkpoint: {0}")]
77 StageCheckpoint(u64),
78 #[error("missing download buffer")]
81 MissingDownloadBuffer,
82 #[error("download channel closed")]
84 ChannelClosed,
85 #[error("database integrity error occurred: {0}")]
87 DatabaseIntegrity(#[from] ProviderError),
88 #[error("invalid download response: {0}")]
91 Download(#[from] DownloadError),
92 #[error("missing static file data for block number: {number}", number = block.block.number)]
94 MissingStaticFileData {
95 block: Box<BlockWithParent>,
97 segment: StaticFileSegment,
99 },
100 #[error("missing prune checkpoint for {0}")]
102 MissingPruneCheckpoint(PruneSegment),
103 #[error("post execute commit error occurred: {_0}")]
105 PostExecuteCommit(&'static str),
106 #[error(transparent)]
108 Internal(#[from] RethError),
109 #[error(transparent)]
114 Recoverable(Box<dyn core::error::Error + Send + Sync>),
115 #[error(transparent)]
119 Fatal(Box<dyn core::error::Error + Send + Sync>),
120}
121
122impl StageError {
123 pub const fn is_fatal(&self) -> bool {
125 matches!(
126 self,
127 Self::Database(_) |
128 Self::Download(_) |
129 Self::DatabaseIntegrity(_) |
130 Self::StageCheckpoint(_) |
131 Self::MissingDownloadBuffer |
132 Self::MissingSyncGap |
133 Self::ChannelClosed |
134 Self::Internal(_) |
135 Self::Fatal(_)
136 )
137 }
138}
139
140impl From<std::io::Error> for StageError {
141 fn from(source: std::io::Error) -> Self {
142 Self::Fatal(Box::new(source))
143 }
144}
145
146impl From<DecompressError> for StageError {
147 fn from(error: DecompressError) -> Self {
148 Self::Database(DatabaseError::from(error))
149 }
150}
151
152#[derive(Error, Debug)]
154pub enum PipelineError {
155 #[error(transparent)]
157 Stage(#[from] StageError),
158 #[error(transparent)]
160 Database(#[from] DatabaseError),
161 #[error(transparent)]
163 Provider(#[from] ProviderError),
164 #[error("pipeline encountered an error while trying to send an event")]
166 Channel(#[from] Box<SendError<PipelineEvent>>),
167 #[error(transparent)]
169 Internal(#[from] RethError),
170 #[error("unexpected unwind")]
172 UnexpectedUnwind,
173 #[error(transparent)]
175 UnwindTargetPruned(#[from] UnwindTargetPrunedError),
176}