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("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
109 BlockNotExecuted {
110 requested: BlockNumber,
112 executed: BlockNumber,
114 },
115 #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
119 BlockExpired {
120 requested: BlockNumber,
122 earliest_available: BlockNumber,
124 },
125 #[error("this provider does not support this request")]
127 UnsupportedProvider,
128 #[cfg(feature = "std")]
130 #[error("not able to find {_0} static file at {_1:?}")]
131 MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
132 #[cfg(feature = "std")]
134 #[error("not able to find static file at {_0:?}")]
135 MissingStaticFilePath(std::path::PathBuf),
136 #[error("highest block is not found for {_0} static file")]
138 MissingHighestStaticFileBlock(StaticFileSegment),
139 #[error("not able to find {_0} static file for block number {_1}")]
141 MissingStaticFileBlock(StaticFileSegment, BlockNumber),
142 #[error("unable to find {_0} static file for transaction id {_1}")]
144 MissingStaticFileTx(StaticFileSegment, TxNumber),
145 #[error("unable to write block #{_1} to finalized static file {_0}")]
147 FinalizedStaticFile(StaticFileSegment, BlockNumber),
148 #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
150 UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
151 #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
153 UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
154 #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
156 CorruptedChangeSetStaticFile,
157 #[error("Unbounded start is unsupported in from_reverts")]
159 UnboundedStartUnsupported,
160 #[error("cannot get a writer on a read-only environment.")]
162 ReadOnlyStaticFileAccess,
163 #[error("failed to initialize consistent view: {_0}")]
165 ConsistentView(Box<ConsistentViewError>),
166 #[error("received invalid output from storage")]
168 InvalidStorageOutput,
169 #[error("missing trie updates for block {0}")]
171 MissingTrieUpdates(B256),
172 #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
174 InsufficientChangesets {
175 requested: BlockNumber,
177 available: core::ops::RangeInclusive<BlockNumber>,
179 },
180 #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
184 MustUnwind {
185 data_source: &'static str,
187 unwind_to: BlockNumber,
189 },
190 #[error(transparent)]
192 Other(#[from] AnyError),
193}
194
195impl ProviderError {
196 pub fn other<E>(error: E) -> Self
199 where
200 E: core::error::Error + Send + Sync + 'static,
201 {
202 Self::Other(AnyError::new(error))
203 }
204
205 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
207 match self {
208 Self::Other(err) => Some(err.as_error()),
209 _ => None,
210 }
211 }
212
213 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
217 let other = self.as_other()?;
218 other.downcast_ref()
219 }
220
221 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
224 self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
225 }
226}
227
228impl DBErrorMarker for ProviderError {}
229
230impl From<alloy_rlp::Error> for ProviderError {
231 fn from(error: alloy_rlp::Error) -> Self {
232 Self::Rlp(error)
233 }
234}
235
236impl From<RecoveryError> for ProviderError {
237 fn from(_: RecoveryError) -> Self {
238 Self::SenderRecoveryError
239 }
240}
241
242impl From<ProviderError> for EvmDatabaseError<ProviderError> {
243 fn from(error: ProviderError) -> Self {
244 Self::Database(error)
245 }
246}
247
248#[derive(Clone, Debug, PartialEq, Eq, Display)]
250#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
251pub struct RootMismatch {
252 pub root: GotExpected<B256>,
254 pub block_number: BlockNumber,
256 pub block_hash: BlockHash,
258}
259
260#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
262pub enum StaticFileWriterError {
263 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
265 FinalizeWithPruneQueued,
266 #[error("thread panicked: {_0}")]
268 ThreadPanic(&'static str),
269 #[error("{0}")]
271 Other(String),
272}
273
274impl StaticFileWriterError {
275 pub fn new(message: impl Into<String>) -> Self {
277 Self::Other(message.into())
278 }
279}
280#[derive(Clone, Debug, PartialEq, Eq, Display)]
282pub enum ConsistentViewError {
283 #[display("node is syncing. best block: {best_block:?}")]
285 Syncing {
286 best_block: GotExpected<BlockNumber>,
288 },
289 #[display("inconsistent database state: {tip:?}")]
291 Inconsistent {
292 tip: GotExpected<Option<B256>>,
294 },
295 #[display("database view no longer contains block: {block:?}")]
297 Reorged {
298 block: B256,
300 },
301}
302
303impl From<ConsistentViewError> for ProviderError {
304 fn from(error: ConsistentViewError) -> Self {
305 Self::ConsistentView(Box::new(error))
306 }
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[derive(thiserror::Error, Debug)]
314 #[error("E")]
315 struct E;
316
317 #[test]
318 fn other_err() {
319 let err = ProviderError::other(E);
320 assert!(err.is_other::<E>());
321 assert!(err.downcast_other_ref::<E>().is_some());
322 }
323}