Skip to main content

reth_storage_errors/
provider.rs

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
15/// Provider result type.
16pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
17
18/// Bundled errors variants thrown by various providers.
19#[derive(Clone, Debug, thiserror::Error)]
20pub enum ProviderError {
21    /// Database error.
22    #[error(transparent)]
23    Database(#[from] DatabaseError),
24    /// BAL error.
25    #[error("BAL error:{_0}")]
26    Bal(BalError),
27    /// Pruning error.
28    #[error(transparent)]
29    Pruning(#[from] PruneSegmentError),
30    /// Static file writer error.
31    #[error(transparent)]
32    StaticFileWriter(#[from] StaticFileWriterError),
33    /// RLP error.
34    #[error("{_0}")]
35    Rlp(alloy_rlp::Error),
36    /// Trie witness error.
37    #[error("trie witness error: {_0}")]
38    TrieWitnessError(String),
39    /// Error when recovering the sender for a transaction
40    #[error("failed to recover sender for transaction")]
41    SenderRecoveryError,
42    /// The header number was not found for the given block hash.
43    #[error("block hash {_0} does not exist in Headers table")]
44    BlockHashNotFound(BlockHash),
45    /// A block body is missing.
46    #[error("block meta not found for block #{_0}")]
47    BlockBodyIndicesNotFound(BlockNumber),
48    /// The transition ID was found for the given address and storage key, but the changeset was
49    /// not found.
50    #[error(
51        "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
52    )]
53    StorageChangesetNotFound {
54        /// The block number found for the address and storage key.
55        block_number: BlockNumber,
56        /// The account address.
57        address: Address,
58        /// The storage key.
59        // NOTE: This is a Box only because otherwise this variant is 16 bytes larger than the
60        // second largest (which uses `BlockHashOrNumber`).
61        storage_key: Box<B256>,
62    },
63    /// The block number was found for the given address, but the changeset was not found.
64    #[error("account change set for address {address} at block #{block_number} does not exist")]
65    AccountChangesetNotFound {
66        /// Block number found for the address.
67        block_number: BlockNumber,
68        /// The account address.
69        address: Address,
70    },
71    /// When required header related data was not found but was required.
72    #[error("no header found for {_0:?}")]
73    HeaderNotFound(BlockHashOrNumber),
74    /// The specific transaction identified by hash or id is missing.
75    #[error("no transaction found for {_0:?}")]
76    TransactionNotFound(HashOrNumber),
77    /// The specific receipt for a transaction identified by hash or id is missing
78    #[error("no receipt found for {_0:?}")]
79    ReceiptNotFound(HashOrNumber),
80    /// Unable to find the best block.
81    #[error("best block does not exist")]
82    BestBlockNotFound,
83    /// Unable to find the finalized block.
84    #[error("finalized block does not exist")]
85    FinalizedBlockNotFound,
86    /// Unable to find the safe block.
87    #[error("safe block does not exist")]
88    SafeBlockNotFound,
89    /// Thrown when we failed to lookup a block for the pending state.
90    #[error("unknown block {_0}")]
91    UnknownBlockHash(B256),
92    /// Thrown when we were unable to find a state for a block hash.
93    #[error("no state found for block {_0}")]
94    StateForHashNotFound(B256),
95    /// Thrown when we were unable to find a state for a block number.
96    #[error("no state found for block number {_0}")]
97    StateForNumberNotFound(u64),
98    /// Unable to find the block number for a given transaction index.
99    #[error("unable to find the block number for a given transaction index")]
100    BlockNumberForTransactionIndexNotFound,
101    /// Root mismatch.
102    #[error("merkle trie {_0}")]
103    StateRootMismatch(Box<RootMismatch>),
104    /// Root mismatch during unwind
105    #[error("unwind merkle trie {_0}")]
106    UnwindStateRootMismatch(Box<RootMismatch>),
107    /// State is not available for the given block number because it is pruned.
108    #[error("state at block #{_0} is pruned")]
109    StateAtBlockPruned(BlockNumber),
110    /// State is not available because the block has not been executed yet.
111    #[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
112    BlockNotExecuted {
113        /// The block number that was requested.
114        requested: BlockNumber,
115        /// The latest executed block number.
116        executed: BlockNumber,
117    },
118    /// Block data is not available because history has expired.
119    ///
120    /// The requested block number is below the earliest available block.
121    #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
122    BlockExpired {
123        /// The block number that was requested.
124        requested: BlockNumber,
125        /// The earliest available block number.
126        earliest_available: BlockNumber,
127    },
128    /// Provider does not support this particular request.
129    #[error("this provider does not support this request")]
130    UnsupportedProvider,
131    /// Static File is not found at specified path.
132    #[cfg(feature = "std")]
133    #[error("not able to find {_0} static file at {_1:?}")]
134    MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
135    /// Static File is not found at specified path.
136    #[cfg(feature = "std")]
137    #[error("not able to find static file at {_0:?}")]
138    MissingStaticFilePath(std::path::PathBuf),
139    /// Highest block is not found for static file block.
140    #[error("highest block is not found for {_0} static file")]
141    MissingHighestStaticFileBlock(StaticFileSegment),
142    /// Static File is not found for requested block.
143    #[error("not able to find {_0} static file for block number {_1}")]
144    MissingStaticFileBlock(StaticFileSegment, BlockNumber),
145    /// Static File is not found for requested transaction.
146    #[error("unable to find {_0} static file for transaction id {_1}")]
147    MissingStaticFileTx(StaticFileSegment, TxNumber),
148    /// Static File is finalized and cannot be written to.
149    #[error("unable to write block #{_1} to finalized static file {_0}")]
150    FinalizedStaticFile(StaticFileSegment, BlockNumber),
151    /// Trying to insert data from an unexpected block number.
152    #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
153    UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
154    /// Trying to insert data from an unexpected block number.
155    #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
156    UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
157    /// Changeset static file is corrupted, and does not have offsets for changesets in each block
158    #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
159    CorruptedChangeSetStaticFile,
160    /// Error when constructing hashed post state reverts
161    #[error("Unbounded start is unsupported in from_reverts")]
162    UnboundedStartUnsupported,
163    /// Static File Provider was initialized as read-only.
164    #[error("cannot get a writer on a read-only environment.")]
165    ReadOnlyStaticFileAccess,
166    /// Consistent view error.
167    #[error("failed to initialize consistent view: {_0}")]
168    ConsistentView(Box<ConsistentViewError>),
169    /// Received invalid output from configured storage implementation.
170    #[error("received invalid output from storage")]
171    InvalidStorageOutput,
172    /// Missing trie updates.
173    #[error("missing trie updates for block {0}")]
174    MissingTrieUpdates(B256),
175    /// Insufficient changesets to revert to the requested block.
176    #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
177    InsufficientChangesets {
178        /// The block number requested for reversion
179        requested: BlockNumber,
180        /// The available range of blocks with changesets
181        available: core::ops::RangeInclusive<BlockNumber>,
182    },
183    /// Inconsistency detected between static files/rocksdb and the DB during
184    /// `ProviderFactory::check_consistency`. The database must be unwound to
185    /// the specified block number to restore consistency.
186    #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
187    MustUnwind {
188        /// The inconsistent data source(s).
189        data_source: &'static str,
190        /// The block number to which the database must be unwound.
191        unwind_to: BlockNumber,
192    },
193    /// Any other error type wrapped into a cloneable [`AnyError`].
194    #[error(transparent)]
195    Other(#[from] AnyError),
196}
197
198impl ProviderError {
199    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
200    /// [`AnyError`]
201    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    /// Returns the arbitrary error if it is [`ProviderError::Other`]
209    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    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
217    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
218    /// otherwise.
219    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    /// Returns true if this type is a [`ProviderError::Other`] of that error
225    /// type. Returns false otherwise.
226    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/// A root mismatch error at a given block height.
258#[derive(Clone, Debug, PartialEq, Eq, Display)]
259#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
260pub struct RootMismatch {
261    /// The target block root diff.
262    pub root: GotExpected<B256>,
263    /// The target block number.
264    pub block_number: BlockNumber,
265    /// The target block hash.
266    pub block_hash: BlockHash,
267}
268
269/// A Static File Writer Error.
270#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
271pub enum StaticFileWriterError {
272    /// Cannot call `sync_all` or `finalize` when prune is queued.
273    #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
274    FinalizeWithPruneQueued,
275    /// Thread panicked during execution.
276    #[error("thread panicked: {_0}")]
277    ThreadPanic(&'static str),
278    /// Other error with message.
279    #[error("{0}")]
280    Other(String),
281}
282
283impl StaticFileWriterError {
284    /// Creates a new [`StaticFileWriterError::Other`] with the given message.
285    pub fn new(message: impl Into<String>) -> Self {
286        Self::Other(message.into())
287    }
288}
289/// Consistent database view error.
290#[derive(Clone, Debug, PartialEq, Eq, Display)]
291pub enum ConsistentViewError {
292    /// Error thrown on attempt to initialize provider while node is still syncing.
293    #[display("node is syncing. best block: {best_block:?}")]
294    Syncing {
295        /// Best block diff.
296        best_block: GotExpected<BlockNumber>,
297    },
298    /// Error thrown on inconsistent database view.
299    #[display("inconsistent database state: {tip:?}")]
300    Inconsistent {
301        /// The tip diff.
302        tip: GotExpected<Option<B256>>,
303    },
304    /// Error thrown when the database does not contain a block from the previous database view.
305    #[display("database view no longer contains block: {block:?}")]
306    Reorged {
307        /// The previous block
308        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}