1use crate::{any::AnyError, db::DatabaseError};
2use alloc::{boxed::Box, string::String};
3use alloy_eips::{BlockHashOrNumber, HashOrNumber};
4use alloy_primitives::{Address, BlockHash, BlockNumber, TxNumber, B256};
5use derive_more::Display;
6use reth_primitives_traits::{transaction::signed::RecoveryError, GotExpected};
7use reth_prune_types::PruneSegmentError;
8use reth_static_file_types::StaticFileSegment;
9use revm_database_interface::{bal::EvmDatabaseError, DBErrorMarker};
10use revm_state::bal::BalError;
11
12pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
14
15#[derive(Clone, Debug, thiserror::Error)]
17pub enum ProviderError {
18 #[error(transparent)]
20 Database(#[from] DatabaseError),
21 #[error("BAL error:{_0}")]
23 Bal(BalError),
24 #[error(transparent)]
26 Pruning(#[from] PruneSegmentError),
27 #[error(transparent)]
29 StaticFileWriter(#[from] StaticFileWriterError),
30 #[error("{_0}")]
32 Rlp(alloy_rlp::Error),
33 #[error("trie witness error: {_0}")]
35 TrieWitnessError(String),
36 #[error("failed to recover sender for transaction")]
38 SenderRecoveryError,
39 #[error("block hash {_0} does not exist in Headers table")]
41 BlockHashNotFound(BlockHash),
42 #[error("block meta not found for block #{_0}")]
44 BlockBodyIndicesNotFound(BlockNumber),
45 #[error(
48 "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
49 )]
50 StorageChangesetNotFound {
51 block_number: BlockNumber,
53 address: Address,
55 storage_key: Box<B256>,
59 },
60 #[error("account change set for address {address} at block #{block_number} does not exist")]
62 AccountChangesetNotFound {
63 block_number: BlockNumber,
65 address: Address,
67 },
68 #[error("no header found for {_0:?}")]
70 HeaderNotFound(BlockHashOrNumber),
71 #[error("no transaction found for {_0:?}")]
73 TransactionNotFound(HashOrNumber),
74 #[error("no receipt found for {_0:?}")]
76 ReceiptNotFound(HashOrNumber),
77 #[error("best block does not exist")]
79 BestBlockNotFound,
80 #[error("finalized block does not exist")]
82 FinalizedBlockNotFound,
83 #[error("safe block does not exist")]
85 SafeBlockNotFound,
86 #[error("unknown block {_0}")]
88 UnknownBlockHash(B256),
89 #[error("no state found for block {_0}")]
91 StateForHashNotFound(B256),
92 #[error("no state found for block number {_0}")]
94 StateForNumberNotFound(u64),
95 #[error("unable to find the block number for a given transaction index")]
97 BlockNumberForTransactionIndexNotFound,
98 #[error("merkle trie {_0}")]
100 StateRootMismatch(Box<RootMismatch>),
101 #[error("unwind merkle trie {_0}")]
103 UnwindStateRootMismatch(Box<RootMismatch>),
104 #[error("state at block #{_0} is pruned")]
106 StateAtBlockPruned(BlockNumber),
107 #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
111 BlockExpired {
112 requested: BlockNumber,
114 earliest_available: BlockNumber,
116 },
117 #[error("this provider does not support this request")]
119 UnsupportedProvider,
120 #[cfg(feature = "std")]
122 #[error("not able to find {_0} static file at {_1:?}")]
123 MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
124 #[cfg(feature = "std")]
126 #[error("not able to find static file at {_0:?}")]
127 MissingStaticFilePath(std::path::PathBuf),
128 #[error("highest block is not found for {_0} static file")]
130 MissingHighestStaticFileBlock(StaticFileSegment),
131 #[error("not able to find {_0} static file for block number {_1}")]
133 MissingStaticFileBlock(StaticFileSegment, BlockNumber),
134 #[error("unable to find {_0} static file for transaction id {_1}")]
136 MissingStaticFileTx(StaticFileSegment, TxNumber),
137 #[error("unable to write block #{_1} to finalized static file {_0}")]
139 FinalizedStaticFile(StaticFileSegment, BlockNumber),
140 #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
142 UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
143 #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
145 UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
146 #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
148 CorruptedChangeSetStaticFile,
149 #[error("Unbounded start is unsupported in from_reverts")]
151 UnboundedStartUnsupported,
152 #[error("cannot get a writer on a read-only environment.")]
154 ReadOnlyStaticFileAccess,
155 #[error("failed to initialize consistent view: {_0}")]
157 ConsistentView(Box<ConsistentViewError>),
158 #[error("received invalid output from storage")]
160 InvalidStorageOutput,
161 #[error("missing trie updates for block {0}")]
163 MissingTrieUpdates(B256),
164 #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
166 InsufficientChangesets {
167 requested: BlockNumber,
169 available: core::ops::RangeInclusive<BlockNumber>,
171 },
172 #[error(transparent)]
174 Other(#[from] AnyError),
175}
176
177impl ProviderError {
178 pub fn other<E>(error: E) -> Self
181 where
182 E: core::error::Error + Send + Sync + 'static,
183 {
184 Self::Other(AnyError::new(error))
185 }
186
187 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
189 match self {
190 Self::Other(err) => Some(err.as_error()),
191 _ => None,
192 }
193 }
194
195 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
199 let other = self.as_other()?;
200 other.downcast_ref()
201 }
202
203 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
206 self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
207 }
208}
209
210impl DBErrorMarker for ProviderError {}
211
212impl From<alloy_rlp::Error> for ProviderError {
213 fn from(error: alloy_rlp::Error) -> Self {
214 Self::Rlp(error)
215 }
216}
217
218impl From<RecoveryError> for ProviderError {
219 fn from(_: RecoveryError) -> Self {
220 Self::SenderRecoveryError
221 }
222}
223
224impl From<ProviderError> for EvmDatabaseError<ProviderError> {
225 fn from(error: ProviderError) -> Self {
226 Self::Database(error)
227 }
228}
229
230#[derive(Clone, Debug, PartialEq, Eq, Display)]
232#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
233pub struct RootMismatch {
234 pub root: GotExpected<B256>,
236 pub block_number: BlockNumber,
238 pub block_hash: BlockHash,
240}
241
242#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
244pub enum StaticFileWriterError {
245 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
247 FinalizeWithPruneQueued,
248 #[error("thread panicked: {_0}")]
250 ThreadPanic(&'static str),
251 #[error("{0}")]
253 Other(String),
254}
255
256impl StaticFileWriterError {
257 pub fn new(message: impl Into<String>) -> Self {
259 Self::Other(message.into())
260 }
261}
262#[derive(Clone, Debug, PartialEq, Eq, Display)]
264pub enum ConsistentViewError {
265 #[display("node is syncing. best block: {best_block:?}")]
267 Syncing {
268 best_block: GotExpected<BlockNumber>,
270 },
271 #[display("inconsistent database state: {tip:?}")]
273 Inconsistent {
274 tip: GotExpected<Option<B256>>,
276 },
277 #[display("database view no longer contains block: {block:?}")]
279 Reorged {
280 block: B256,
282 },
283}
284
285impl From<ConsistentViewError> for ProviderError {
286 fn from(error: ConsistentViewError) -> Self {
287 Self::ConsistentView(Box::new(error))
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use super::*;
294
295 #[derive(thiserror::Error, Debug)]
296 #[error("E")]
297 struct E;
298
299 #[test]
300 fn other_err() {
301 let err = ProviderError::other(E);
302 assert!(err.is_other::<E>());
303 assert!(err.downcast_other_ref::<E>().is_some());
304 }
305}