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    /// Block data is not available because history has expired.
108    ///
109    /// The requested block number is below the earliest available block.
110    #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
111    BlockExpired {
112        /// The block number that was requested.
113        requested: BlockNumber,
114        /// The earliest available block number.
115        earliest_available: BlockNumber,
116    },
117    /// Provider does not support this particular request.
118    #[error("this provider does not support this request")]
119    UnsupportedProvider,
120    /// Static File is not found at specified path.
121    #[cfg(feature = "std")]
122    #[error("not able to find {_0} static file at {_1:?}")]
123    MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
124    /// Static File is not found at specified path.
125    #[cfg(feature = "std")]
126    #[error("not able to find static file at {_0:?}")]
127    MissingStaticFilePath(std::path::PathBuf),
128    /// Highest block is not found for static file block.
129    #[error("highest block is not found for {_0} static file")]
130    MissingHighestStaticFileBlock(StaticFileSegment),
131    /// Static File is not found for requested block.
132    #[error("not able to find {_0} static file for block number {_1}")]
133    MissingStaticFileBlock(StaticFileSegment, BlockNumber),
134    /// Static File is not found for requested transaction.
135    #[error("unable to find {_0} static file for transaction id {_1}")]
136    MissingStaticFileTx(StaticFileSegment, TxNumber),
137    /// Static File is finalized and cannot be written to.
138    #[error("unable to write block #{_1} to finalized static file {_0}")]
139    FinalizedStaticFile(StaticFileSegment, BlockNumber),
140    /// Trying to insert data from an unexpected block number.
141    #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
142    UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
143    /// Trying to insert data from an unexpected block number.
144    #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
145    UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
146    /// Changeset static file is corrupted, and does not have offsets for changesets in each block
147    #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
148    CorruptedChangeSetStaticFile,
149    /// Error when constructing hashed post state reverts
150    #[error("Unbounded start is unsupported in from_reverts")]
151    UnboundedStartUnsupported,
152    /// Static File Provider was initialized as read-only.
153    #[error("cannot get a writer on a read-only environment.")]
154    ReadOnlyStaticFileAccess,
155    /// Consistent view error.
156    #[error("failed to initialize consistent view: {_0}")]
157    ConsistentView(Box<ConsistentViewError>),
158    /// Received invalid output from configured storage implementation.
159    #[error("received invalid output from storage")]
160    InvalidStorageOutput,
161    /// Missing trie updates.
162    #[error("missing trie updates for block {0}")]
163    MissingTrieUpdates(B256),
164    /// Insufficient changesets to revert to the requested block.
165    #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
166    InsufficientChangesets {
167        /// The block number requested for reversion
168        requested: BlockNumber,
169        /// The available range of blocks with changesets
170        available: core::ops::RangeInclusive<BlockNumber>,
171    },
172    /// Any other error type wrapped into a cloneable [`AnyError`].
173    #[error(transparent)]
174    Other(#[from] AnyError),
175}
176
177impl ProviderError {
178    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
179    /// [`AnyError`]
180    pub fn other<E>(error: E) -> Self
181    where
182        E: core::error::Error + Send + Sync + 'static,
183    {
184        Self::Other(AnyError::new(error))
185    }
186
187    /// Returns the arbitrary error if it is [`ProviderError::Other`]
188    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
189        match self {
190            Self::Other(err) => Some(err.as_error()),
191            _ => None,
192        }
193    }
194
195    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
196    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
197    /// otherwise.
198    pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
199        let other = self.as_other()?;
200        other.downcast_ref()
201    }
202
203    /// Returns true if this type is a [`ProviderError::Other`] of that error
204    /// type. Returns false otherwise.
205    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
206        self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
207    }
208}
209
210impl DBErrorMarker for ProviderError {}
211
212impl From<alloy_rlp::Error> for ProviderError {
213    fn from(error: alloy_rlp::Error) -> Self {
214        Self::Rlp(error)
215    }
216}
217
218impl From<RecoveryError> for ProviderError {
219    fn from(_: RecoveryError) -> Self {
220        Self::SenderRecoveryError
221    }
222}
223
224impl From<ProviderError> for EvmDatabaseError<ProviderError> {
225    fn from(error: ProviderError) -> Self {
226        Self::Database(error)
227    }
228}
229
230/// A root mismatch error at a given block height.
231#[derive(Clone, Debug, PartialEq, Eq, Display)]
232#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
233pub struct RootMismatch {
234    /// The target block root diff.
235    pub root: GotExpected<B256>,
236    /// The target block number.
237    pub block_number: BlockNumber,
238    /// The target block hash.
239    pub block_hash: BlockHash,
240}
241
242/// A Static File Writer Error.
243#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
244pub enum StaticFileWriterError {
245    /// Cannot call `sync_all` or `finalize` when prune is queued.
246    #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
247    FinalizeWithPruneQueued,
248    /// Thread panicked during execution.
249    #[error("thread panicked: {_0}")]
250    ThreadPanic(&'static str),
251    /// Other error with message.
252    #[error("{0}")]
253    Other(String),
254}
255
256impl StaticFileWriterError {
257    /// Creates a new [`StaticFileWriterError::Other`] with the given message.
258    pub fn new(message: impl Into<String>) -> Self {
259        Self::Other(message.into())
260    }
261}
262/// Consistent database view error.
263#[derive(Clone, Debug, PartialEq, Eq, Display)]
264pub enum ConsistentViewError {
265    /// Error thrown on attempt to initialize provider while node is still syncing.
266    #[display("node is syncing. best block: {best_block:?}")]
267    Syncing {
268        /// Best block diff.
269        best_block: GotExpected<BlockNumber>,
270    },
271    /// Error thrown on inconsistent database view.
272    #[display("inconsistent database state: {tip:?}")]
273    Inconsistent {
274        /// The tip diff.
275        tip: GotExpected<Option<B256>>,
276    },
277    /// Error thrown when the database does not contain a block from the previous database view.
278    #[display("database view no longer contains block: {block:?}")]
279    Reorged {
280        /// The previous block
281        block: B256,
282    },
283}
284
285impl From<ConsistentViewError> for ProviderError {
286    fn from(error: ConsistentViewError) -> Self {
287        Self::ConsistentView(Box::new(error))
288    }
289}
290
291#[cfg(test)]
292mod tests {
293    use super::*;
294
295    #[derive(thiserror::Error, Debug)]
296    #[error("E")]
297    struct E;
298
299    #[test]
300    fn other_err() {
301        let err = ProviderError::other(E);
302        assert!(err.is_other::<E>());
303        assert!(err.downcast_other_ref::<E>().is_some());
304    }
305}