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