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_codecs::DecompressError;
7use reth_primitives_traits::{transaction::signed::RecoveryError, GotExpected};
8use reth_prune_types::PruneSegmentError;
9use reth_static_file_types::StaticFileSegment;
10use revm::{
11 database_interface::{bal::EvmDatabaseError, DBErrorMarker},
12 state::bal::BalError,
13};
14
15pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
17
18#[derive(Clone, Debug, thiserror::Error)]
20pub enum ProviderError {
21 #[error(transparent)]
23 Database(#[from] DatabaseError),
24 #[error("BAL error:{_0}")]
26 Bal(BalError),
27 #[error(transparent)]
29 Pruning(#[from] PruneSegmentError),
30 #[error(transparent)]
32 StaticFileWriter(#[from] StaticFileWriterError),
33 #[error("{_0}")]
35 Rlp(alloy_rlp::Error),
36 #[error("trie witness error: {_0}")]
38 TrieWitnessError(String),
39 #[error("failed to recover sender for transaction")]
41 SenderRecoveryError,
42 #[error("block hash {_0} does not exist in Headers table")]
44 BlockHashNotFound(BlockHash),
45 #[error("block meta not found for block #{_0}")]
47 BlockBodyIndicesNotFound(BlockNumber),
48 #[error(
51 "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
52 )]
53 StorageChangesetNotFound {
54 block_number: BlockNumber,
56 address: Address,
58 storage_key: Box<B256>,
62 },
63 #[error("account change set for address {address} at block #{block_number} does not exist")]
65 AccountChangesetNotFound {
66 block_number: BlockNumber,
68 address: Address,
70 },
71 #[error("no header found for {_0:?}")]
73 HeaderNotFound(BlockHashOrNumber),
74 #[error("no transaction found for {_0:?}")]
76 TransactionNotFound(HashOrNumber),
77 #[error("no receipt found for {_0:?}")]
79 ReceiptNotFound(HashOrNumber),
80 #[error("best block does not exist")]
82 BestBlockNotFound,
83 #[error("finalized block does not exist")]
85 FinalizedBlockNotFound,
86 #[error("safe block does not exist")]
88 SafeBlockNotFound,
89 #[error("unknown block {_0}")]
91 UnknownBlockHash(B256),
92 #[error("no state found for block {_0}")]
94 StateForHashNotFound(B256),
95 #[error("no state found for block number {_0}")]
97 StateForNumberNotFound(u64),
98 #[error("unable to find the block number for a given transaction index")]
100 BlockNumberForTransactionIndexNotFound,
101 #[error("merkle trie {_0}")]
103 StateRootMismatch(Box<RootMismatch>),
104 #[error("unwind merkle trie {_0}")]
106 UnwindStateRootMismatch(Box<RootMismatch>),
107 #[error("state at block #{_0} is pruned")]
109 StateAtBlockPruned(BlockNumber),
110 #[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
112 BlockNotExecuted {
113 requested: BlockNumber,
115 executed: BlockNumber,
117 },
118 #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
122 BlockExpired {
123 requested: BlockNumber,
125 earliest_available: BlockNumber,
127 },
128 #[error("this provider does not support this request")]
130 UnsupportedProvider,
131 #[cfg(feature = "std")]
133 #[error("not able to find {_0} static file at {_1:?}")]
134 MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
135 #[cfg(feature = "std")]
137 #[error("not able to find static file at {_0:?}")]
138 MissingStaticFilePath(std::path::PathBuf),
139 #[error("highest block is not found for {_0} static file")]
141 MissingHighestStaticFileBlock(StaticFileSegment),
142 #[error("not able to find {_0} static file for block number {_1}")]
144 MissingStaticFileBlock(StaticFileSegment, BlockNumber),
145 #[error("unable to find {_0} static file for transaction id {_1}")]
147 MissingStaticFileTx(StaticFileSegment, TxNumber),
148 #[error("unable to write block #{_1} to finalized static file {_0}")]
150 FinalizedStaticFile(StaticFileSegment, BlockNumber),
151 #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
153 UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
154 #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
156 UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
157 #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
159 CorruptedChangeSetStaticFile,
160 #[error("Unbounded start is unsupported in from_reverts")]
162 UnboundedStartUnsupported,
163 #[error("cannot get a writer on a read-only environment.")]
165 ReadOnlyStaticFileAccess,
166 #[error("failed to initialize consistent view: {_0}")]
168 ConsistentView(Box<ConsistentViewError>),
169 #[error("received invalid output from storage")]
171 InvalidStorageOutput,
172 #[error("missing trie updates for block {0}")]
174 MissingTrieUpdates(B256),
175 #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
177 InsufficientChangesets {
178 requested: BlockNumber,
180 available: core::ops::RangeInclusive<BlockNumber>,
182 },
183 #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
187 MustUnwind {
188 data_source: &'static str,
190 unwind_to: BlockNumber,
192 },
193 #[error(transparent)]
195 Other(#[from] AnyError),
196}
197
198impl ProviderError {
199 pub fn other<E>(error: E) -> Self
202 where
203 E: core::error::Error + Send + Sync + 'static,
204 {
205 Self::Other(AnyError::new(error))
206 }
207
208 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
210 match self {
211 Self::Other(err) => Some(err.as_error()),
212 _ => None,
213 }
214 }
215
216 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
220 let other = self.as_other()?;
221 other.downcast_ref()
222 }
223
224 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
227 self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
228 }
229}
230
231impl DBErrorMarker for ProviderError {}
232
233impl From<alloy_rlp::Error> for ProviderError {
234 fn from(error: alloy_rlp::Error) -> Self {
235 Self::Rlp(error)
236 }
237}
238
239impl From<RecoveryError> for ProviderError {
240 fn from(_: RecoveryError) -> Self {
241 Self::SenderRecoveryError
242 }
243}
244
245impl From<ProviderError> for EvmDatabaseError<ProviderError> {
246 fn from(error: ProviderError) -> Self {
247 Self::Database(error)
248 }
249}
250
251impl From<DecompressError> for ProviderError {
252 fn from(error: DecompressError) -> Self {
253 Self::Database(error.into())
254 }
255}
256
257#[derive(Clone, Debug, PartialEq, Eq, Display)]
259#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
260pub struct RootMismatch {
261 pub root: GotExpected<B256>,
263 pub block_number: BlockNumber,
265 pub block_hash: BlockHash,
267}
268
269#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
271pub enum StaticFileWriterError {
272 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
274 FinalizeWithPruneQueued,
275 #[error("thread panicked: {_0}")]
277 ThreadPanic(&'static str),
278 #[error("{0}")]
280 Other(String),
281}
282
283impl StaticFileWriterError {
284 pub fn new(message: impl Into<String>) -> Self {
286 Self::Other(message.into())
287 }
288}
289#[derive(Clone, Debug, PartialEq, Eq, Display)]
291pub enum ConsistentViewError {
292 #[display("node is syncing. best block: {best_block:?}")]
294 Syncing {
295 best_block: GotExpected<BlockNumber>,
297 },
298 #[display("inconsistent database state: {tip:?}")]
300 Inconsistent {
301 tip: GotExpected<Option<B256>>,
303 },
304 #[display("database view no longer contains block: {block:?}")]
306 Reorged {
307 block: B256,
309 },
310}
311
312impl From<ConsistentViewError> for ProviderError {
313 fn from(error: ConsistentViewError) -> Self {
314 Self::ConsistentView(Box::new(error))
315 }
316}
317
318#[cfg(test)]
319mod tests {
320 use super::*;
321
322 #[derive(thiserror::Error, Debug)]
323 #[error("E")]
324 struct E;
325
326 #[test]
327 fn other_err() {
328 let err = ProviderError::other(E);
329 assert!(err.is_other::<E>());
330 assert!(err.downcast_other_ref::<E>().is_some());
331 }
332}