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;
10
11/// Provider result type.
12pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
13
14/// Bundled errors variants thrown by various providers.
15#[derive(Clone, Debug, thiserror::Error)]
16pub enum ProviderError {
17    /// Database error.
18    #[error(transparent)]
19    Database(#[from] DatabaseError),
20    /// Pruning error.
21    #[error(transparent)]
22    Pruning(#[from] PruneSegmentError),
23    /// Static file writer error.
24    #[error(transparent)]
25    StaticFileWriter(#[from] StaticFileWriterError),
26    /// RLP error.
27    #[error("{_0}")]
28    Rlp(alloy_rlp::Error),
29    /// Trie witness error.
30    #[error("trie witness error: {_0}")]
31    TrieWitnessError(String),
32    /// Error when recovering the sender for a transaction
33    #[error("failed to recover sender for transaction")]
34    SenderRecoveryError,
35    /// The header number was not found for the given block hash.
36    #[error("block hash {_0} does not exist in Headers table")]
37    BlockHashNotFound(BlockHash),
38    /// A block body is missing.
39    #[error("block meta not found for block #{_0}")]
40    BlockBodyIndicesNotFound(BlockNumber),
41    /// The transition ID was found for the given address and storage key, but the changeset was
42    /// not found.
43    #[error(
44        "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
45    )]
46    StorageChangesetNotFound {
47        /// The block number found for the address and storage key.
48        block_number: BlockNumber,
49        /// The account address.
50        address: Address,
51        /// The storage key.
52        // NOTE: This is a Box only because otherwise this variant is 16 bytes larger than the
53        // second largest (which uses `BlockHashOrNumber`).
54        storage_key: Box<B256>,
55    },
56    /// The block number was found for the given address, but the changeset was not found.
57    #[error("account change set for address {address} at block #{block_number} does not exist")]
58    AccountChangesetNotFound {
59        /// Block number found for the address.
60        block_number: BlockNumber,
61        /// The account address.
62        address: Address,
63    },
64    /// When required header related data was not found but was required.
65    #[error("no header found for {_0:?}")]
66    HeaderNotFound(BlockHashOrNumber),
67    /// The specific transaction identified by hash or id is missing.
68    #[error("no transaction found for {_0:?}")]
69    TransactionNotFound(HashOrNumber),
70    /// The specific receipt for a transaction identified by hash or id is missing
71    #[error("no receipt found for {_0:?}")]
72    ReceiptNotFound(HashOrNumber),
73    /// Unable to find the best block.
74    #[error("best block does not exist")]
75    BestBlockNotFound,
76    /// Unable to find the finalized block.
77    #[error("finalized block does not exist")]
78    FinalizedBlockNotFound,
79    /// Unable to find the safe block.
80    #[error("safe block does not exist")]
81    SafeBlockNotFound,
82    /// Thrown when we failed to lookup a block for the pending state.
83    #[error("unknown block {_0}")]
84    UnknownBlockHash(B256),
85    /// Thrown when we were unable to find a state for a block hash.
86    #[error("no state found for block {_0}")]
87    StateForHashNotFound(B256),
88    /// Thrown when we were unable to find a state for a block number.
89    #[error("no state found for block number {_0}")]
90    StateForNumberNotFound(u64),
91    /// Unable to find the block number for a given transaction index.
92    #[error("unable to find the block number for a given transaction index")]
93    BlockNumberForTransactionIndexNotFound,
94    /// Root mismatch.
95    #[error("merkle trie {_0}")]
96    StateRootMismatch(Box<RootMismatch>),
97    /// Root mismatch during unwind
98    #[error("unwind merkle trie {_0}")]
99    UnwindStateRootMismatch(Box<RootMismatch>),
100    /// State is not available for the given block number because it is pruned.
101    #[error("state at block #{_0} is pruned")]
102    StateAtBlockPruned(BlockNumber),
103    /// State is not available because the block has not been executed yet.
104    #[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
105    BlockNotExecuted {
106        /// The block number that was requested.
107        requested: BlockNumber,
108        /// The latest executed block number.
109        executed: BlockNumber,
110    },
111    /// Block data is not available because history has expired.
112    ///
113    /// The requested block number is below the earliest available block.
114    #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
115    BlockExpired {
116        /// The block number that was requested.
117        requested: BlockNumber,
118        /// The earliest available block number.
119        earliest_available: BlockNumber,
120    },
121    /// Provider does not support this particular request.
122    #[error("this provider does not support this request")]
123    UnsupportedProvider,
124    /// Static File is not found at specified path.
125    #[cfg(feature = "std")]
126    #[error("not able to find {_0} static file at {_1:?}")]
127    MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
128    /// Static File is not found at specified path.
129    #[cfg(feature = "std")]
130    #[error("not able to find static file at {_0:?}")]
131    MissingStaticFilePath(std::path::PathBuf),
132    /// Highest block is not found for static file block.
133    #[error("highest block is not found for {_0} static file")]
134    MissingHighestStaticFileBlock(StaticFileSegment),
135    /// Static File is not found for requested block.
136    #[error("not able to find {_0} static file for block number {_1}")]
137    MissingStaticFileBlock(StaticFileSegment, BlockNumber),
138    /// Static File is not found for requested transaction.
139    #[error("unable to find {_0} static file for transaction id {_1}")]
140    MissingStaticFileTx(StaticFileSegment, TxNumber),
141    /// Static File is finalized and cannot be written to.
142    #[error("unable to write block #{_1} to finalized static file {_0}")]
143    FinalizedStaticFile(StaticFileSegment, BlockNumber),
144    /// Trying to insert data from an unexpected block number.
145    #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
146    UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
147    /// Trying to insert data from an unexpected block number.
148    #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
149    UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
150    /// Changeset static file is corrupted, and does not have offsets for changesets in each block
151    #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
152    CorruptedChangeSetStaticFile,
153    /// Error when constructing hashed post state reverts
154    #[error("Unbounded start is unsupported in from_reverts")]
155    UnboundedStartUnsupported,
156    /// Static File Provider was initialized as read-only.
157    #[error("cannot get a writer on a read-only environment.")]
158    ReadOnlyStaticFileAccess,
159    /// Consistent view error.
160    #[error("failed to initialize consistent view: {_0}")]
161    ConsistentView(Box<ConsistentViewError>),
162    /// Received invalid output from configured storage implementation.
163    #[error("received invalid output from storage")]
164    InvalidStorageOutput,
165    /// Missing trie updates.
166    #[error("missing trie updates for block {0}")]
167    MissingTrieUpdates(B256),
168    /// Insufficient changesets to revert to the requested block.
169    #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
170    InsufficientChangesets {
171        /// The block number requested for reversion
172        requested: BlockNumber,
173        /// The available range of blocks with changesets
174        available: core::ops::RangeInclusive<BlockNumber>,
175    },
176    /// Inconsistency detected between static files/rocksdb and the DB during
177    /// `ProviderFactory::check_consistency`. The database must be unwound to
178    /// the specified block number to restore consistency.
179    #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
180    MustUnwind {
181        /// The inconsistent data source(s).
182        data_source: &'static str,
183        /// The block number to which the database must be unwound.
184        unwind_to: BlockNumber,
185    },
186    /// A persisted snap attempt record this build cannot read.
187    #[error(
188        "snap attempt record version {found:?} is not supported, this build writes {supported}"
189    )]
190    UnsupportedSnapAttemptVersion {
191        /// Version found on disk, absent when the record carries no numeric version.
192        found: Option<u64>,
193        /// Version this build writes.
194        supported: u32,
195    },
196    /// Snap sync was selected on a database that predates the hashed state layout it downloads
197    /// into.
198    #[error("snap sync requires the hashed state layout, which this database predates")]
199    SnapStorageLayoutUnsupported,
200    /// State a snap attempt is still downloading was about to be marked complete.
201    #[error("snap attempt {attempt} has not verified the downloaded state")]
202    UnverifiedSnapState {
203        /// Attempt that owns the unverified state.
204        attempt: u64,
205    },
206    /// The staged pipeline was selected on a database holding state a snap attempt left
207    /// unverified, which only snap can finish or replace.
208    #[error(
209        "snap attempt {attempt} left unverified state, restart with snap sync or wipe the database"
210    )]
211    SnapStateRequiresSnapSync {
212        /// Attempt that owns the unverified state.
213        attempt: u64,
214    },
215    /// Any other error type wrapped into a cloneable [`AnyError`].
216    #[error(transparent)]
217    Other(#[from] AnyError),
218}
219
220impl ProviderError {
221    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
222    /// [`AnyError`]
223    pub fn other<E>(error: E) -> Self
224    where
225        E: core::error::Error + Send + Sync + 'static,
226    {
227        Self::Other(AnyError::new(error))
228    }
229
230    /// Returns the arbitrary error if it is [`ProviderError::Other`]
231    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
232        match self {
233            Self::Other(err) => Some(err.as_error()),
234            _ => None,
235        }
236    }
237
238    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
239    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
240    /// otherwise.
241    pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
242        let other = self.as_other()?;
243        other.downcast_ref()
244    }
245
246    /// Returns true if this type is a [`ProviderError::Other`] of that error
247    /// type. Returns false otherwise.
248    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
249        self.as_other().is_some_and(|err| err.is::<T>())
250    }
251}
252
253impl From<alloy_rlp::Error> for ProviderError {
254    fn from(error: alloy_rlp::Error) -> Self {
255        Self::Rlp(error)
256    }
257}
258
259impl From<RecoveryError> for ProviderError {
260    fn from(_: RecoveryError) -> Self {
261        Self::SenderRecoveryError
262    }
263}
264
265impl From<DecompressError> for ProviderError {
266    fn from(error: DecompressError) -> Self {
267        Self::Database(error.into())
268    }
269}
270
271/// A root mismatch error at a given block height.
272#[derive(Clone, Debug, PartialEq, Eq, Display)]
273#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
274pub struct RootMismatch {
275    /// The target block root diff.
276    pub root: GotExpected<B256>,
277    /// The target block number.
278    pub block_number: BlockNumber,
279    /// The target block hash.
280    pub block_hash: BlockHash,
281}
282
283/// A Static File Writer Error.
284#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
285pub enum StaticFileWriterError {
286    /// Cannot call `sync_all` or `finalize` when prune is queued.
287    #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
288    FinalizeWithPruneQueued,
289    /// Thread panicked during execution.
290    #[error("thread panicked: {_0}")]
291    ThreadPanic(&'static str),
292    /// Other error with message.
293    #[error("{0}")]
294    Other(String),
295    /// An unwind would keep blocks below the block a pruned segment is anchored at, which the
296    /// segment no longer stores.
297    #[error("cannot unwind {segment} to block {target} below the pruned anchor at block {anchor}")]
298    UnwindBelowPrunedAnchor {
299        /// Segment being unwound.
300        segment: StaticFileSegment,
301        /// Highest block the unwind would keep.
302        target: BlockNumber,
303        /// Lowest block the segment stores.
304        anchor: BlockNumber,
305    },
306}
307
308impl StaticFileWriterError {
309    /// Creates a new [`StaticFileWriterError::Other`] with the given message.
310    pub fn new(message: impl Into<String>) -> Self {
311        Self::Other(message.into())
312    }
313}
314/// Consistent database view error.
315#[derive(Clone, Debug, PartialEq, Eq, Display)]
316pub enum ConsistentViewError {
317    /// Error thrown on attempt to initialize provider while node is still syncing.
318    #[display("node is syncing. best block: {best_block:?}")]
319    Syncing {
320        /// Best block diff.
321        best_block: GotExpected<BlockNumber>,
322    },
323    /// Error thrown on inconsistent database view.
324    #[display("inconsistent database state: {tip:?}")]
325    Inconsistent {
326        /// The tip diff.
327        tip: GotExpected<Option<B256>>,
328    },
329    /// Error thrown when the database does not contain a block from the previous database view.
330    #[display("database view no longer contains block: {block:?}")]
331    Reorged {
332        /// The previous block
333        block: B256,
334    },
335}
336
337impl From<ConsistentViewError> for ProviderError {
338    fn from(error: ConsistentViewError) -> Self {
339        Self::ConsistentView(Box::new(error))
340    }
341}
342
343#[cfg(test)]
344mod tests {
345    use super::*;
346
347    #[derive(thiserror::Error, Debug)]
348    #[error("E")]
349    struct E;
350
351    #[test]
352    fn other_err() {
353        let err = ProviderError::other(E);
354        assert!(err.is_other::<E>());
355        assert!(err.downcast_other_ref::<E>().is_some());
356    }
357}