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