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    /// A persisted snap attempt record this build cannot read.
194    #[error(
195        "snap attempt record version {found:?} is not supported, this build writes {supported}"
196    )]
197    UnsupportedSnapAttemptVersion {
198        /// Version found on disk, absent when the record carries no numeric version.
199        found: Option<u64>,
200        /// Version this build writes.
201        supported: u32,
202    },
203    /// State a snap attempt is still downloading was about to be marked complete.
204    #[error("snap attempt {attempt} has not verified the downloaded state")]
205    UnverifiedSnapState {
206        /// Attempt that owns the unverified state.
207        attempt: u64,
208    },
209    /// Any other error type wrapped into a cloneable [`AnyError`].
210    #[error(transparent)]
211    Other(#[from] AnyError),
212}
213
214impl ProviderError {
215    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
216    /// [`AnyError`]
217    pub fn other<E>(error: E) -> Self
218    where
219        E: core::error::Error + Send + Sync + 'static,
220    {
221        Self::Other(AnyError::new(error))
222    }
223
224    /// Returns the arbitrary error if it is [`ProviderError::Other`]
225    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
226        match self {
227            Self::Other(err) => Some(err.as_error()),
228            _ => None,
229        }
230    }
231
232    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
233    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
234    /// otherwise.
235    pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
236        let other = self.as_other()?;
237        other.downcast_ref()
238    }
239
240    /// Returns true if this type is a [`ProviderError::Other`] of that error
241    /// type. Returns false otherwise.
242    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
243        self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
244    }
245}
246
247impl DBErrorMarker for ProviderError {}
248
249impl From<alloy_rlp::Error> for ProviderError {
250    fn from(error: alloy_rlp::Error) -> Self {
251        Self::Rlp(error)
252    }
253}
254
255impl From<RecoveryError> for ProviderError {
256    fn from(_: RecoveryError) -> Self {
257        Self::SenderRecoveryError
258    }
259}
260
261impl From<ProviderError> for EvmDatabaseError<ProviderError> {
262    fn from(error: ProviderError) -> Self {
263        Self::Database(error)
264    }
265}
266
267impl From<DecompressError> for ProviderError {
268    fn from(error: DecompressError) -> Self {
269        Self::Database(error.into())
270    }
271}
272
273/// A root mismatch error at a given block height.
274#[derive(Clone, Debug, PartialEq, Eq, Display)]
275#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
276pub struct RootMismatch {
277    /// The target block root diff.
278    pub root: GotExpected<B256>,
279    /// The target block number.
280    pub block_number: BlockNumber,
281    /// The target block hash.
282    pub block_hash: BlockHash,
283}
284
285/// A Static File Writer Error.
286#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
287pub enum StaticFileWriterError {
288    /// Cannot call `sync_all` or `finalize` when prune is queued.
289    #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
290    FinalizeWithPruneQueued,
291    /// Thread panicked during execution.
292    #[error("thread panicked: {_0}")]
293    ThreadPanic(&'static str),
294    /// Other error with message.
295    #[error("{0}")]
296    Other(String),
297}
298
299impl StaticFileWriterError {
300    /// Creates a new [`StaticFileWriterError::Other`] with the given message.
301    pub fn new(message: impl Into<String>) -> Self {
302        Self::Other(message.into())
303    }
304}
305/// Consistent database view error.
306#[derive(Clone, Debug, PartialEq, Eq, Display)]
307pub enum ConsistentViewError {
308    /// Error thrown on attempt to initialize provider while node is still syncing.
309    #[display("node is syncing. best block: {best_block:?}")]
310    Syncing {
311        /// Best block diff.
312        best_block: GotExpected<BlockNumber>,
313    },
314    /// Error thrown on inconsistent database view.
315    #[display("inconsistent database state: {tip:?}")]
316    Inconsistent {
317        /// The tip diff.
318        tip: GotExpected<Option<B256>>,
319    },
320    /// Error thrown when the database does not contain a block from the previous database view.
321    #[display("database view no longer contains block: {block:?}")]
322    Reorged {
323        /// The previous block
324        block: B256,
325    },
326}
327
328impl From<ConsistentViewError> for ProviderError {
329    fn from(error: ConsistentViewError) -> Self {
330        Self::ConsistentView(Box::new(error))
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337
338    #[derive(thiserror::Error, Debug)]
339    #[error("E")]
340    struct E;
341
342    #[test]
343    fn other_err() {
344        let err = ProviderError::other(E);
345        assert!(err.is_other::<E>());
346        assert!(err.downcast_other_ref::<E>().is_some());
347    }
348}