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