reth_storage_errors/
provider.rs

1use crate::{
2    any::AnyError, db::DatabaseError, lockfile::StorageLockError, writer::UnifiedStorageWriterError,
3};
4use alloc::{boxed::Box, string::String};
5use alloy_eips::{BlockHashOrNumber, HashOrNumber};
6use alloy_primitives::{Address, BlockHash, BlockNumber, TxNumber, B256};
7use derive_more::Display;
8use reth_primitives_traits::{transaction::signed::RecoveryError, GotExpected};
9use reth_prune_types::PruneSegmentError;
10use reth_static_file_types::StaticFileSegment;
11use revm_database_interface::DBErrorMarker;
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    /// Pruning error.
23    #[error(transparent)]
24    Pruning(#[from] PruneSegmentError),
25    /// RLP error.
26    #[error("{_0}")]
27    Rlp(alloy_rlp::Error),
28    /// Trie witness error.
29    #[error("trie witness error: {_0}")]
30    TrieWitnessError(String),
31    /// Error when recovering the sender for a transaction
32    #[error("failed to recover sender for transaction")]
33    SenderRecoveryError,
34    /// The header number was not found for the given block hash.
35    #[error("block hash {_0} does not exist in Headers table")]
36    BlockHashNotFound(BlockHash),
37    /// A block body is missing.
38    #[error("block meta not found for block #{_0}")]
39    BlockBodyIndicesNotFound(BlockNumber),
40    /// The transition ID was found for the given address and storage key, but the changeset was
41    /// not found.
42    #[error("storage change set for address {address} and key {storage_key} at block #{block_number} does not exist")]
43    StorageChangesetNotFound {
44        /// The block number found for the address and storage key.
45        block_number: BlockNumber,
46        /// The account address.
47        address: Address,
48        /// The storage key.
49        // NOTE: This is a Box only because otherwise this variant is 16 bytes larger than the
50        // second largest (which uses `BlockHashOrNumber`).
51        storage_key: Box<B256>,
52    },
53    /// The block number was found for the given address, but the changeset was not found.
54    #[error("account change set for address {address} at block #{block_number} does not exist")]
55    AccountChangesetNotFound {
56        /// Block number found for the address.
57        block_number: BlockNumber,
58        /// The account address.
59        address: Address,
60    },
61    /// The total difficulty for a block is missing.
62    #[error("total difficulty not found for block #{_0}")]
63    TotalDifficultyNotFound(BlockNumber),
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 the cache service task dropped.
83    #[error("cache service task stopped")]
84    CacheServiceUnavailable,
85    /// Thrown when we failed to lookup a block for the pending state.
86    #[error("unknown block {_0}")]
87    UnknownBlockHash(B256),
88    /// Thrown when we were unable to find a state for a block hash.
89    #[error("no state found for block {_0}")]
90    StateForHashNotFound(B256),
91    /// Thrown when we were unable to find a state for a block number.
92    #[error("no state found for block number {_0}")]
93    StateForNumberNotFound(u64),
94    /// Unable to find the block number for a given transaction index.
95    #[error("unable to find the block number for a given transaction index")]
96    BlockNumberForTransactionIndexNotFound,
97    /// Root mismatch.
98    #[error("merkle trie {_0}")]
99    StateRootMismatch(Box<RootMismatch>),
100    /// Root mismatch during unwind
101    #[error("unwind merkle trie {_0}")]
102    UnwindStateRootMismatch(Box<RootMismatch>),
103    /// State is not available for the given block number because it is pruned.
104    #[error("state at block #{_0} is pruned")]
105    StateAtBlockPruned(BlockNumber),
106    /// Provider does not support this particular request.
107    #[error("this provider does not support this request")]
108    UnsupportedProvider,
109    /// Static File is not found at specified path.
110    #[cfg(feature = "std")]
111    #[error("not able to find {_0} static file at {_1:?}")]
112    MissingStaticFilePath(StaticFileSegment, std::path::PathBuf),
113    /// Static File is not found for requested block.
114    #[error("not able to find {_0} static file for block number {_1}")]
115    MissingStaticFileBlock(StaticFileSegment, BlockNumber),
116    /// Static File is not found for requested transaction.
117    #[error("unable to find {_0} static file for transaction id {_1}")]
118    MissingStaticFileTx(StaticFileSegment, TxNumber),
119    /// Static File is finalized and cannot be written to.
120    #[error("unable to write block #{_1} to finalized static file {_0}")]
121    FinalizedStaticFile(StaticFileSegment, BlockNumber),
122    /// Trying to insert data from an unexpected block number.
123    #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
124    UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
125    /// Trying to insert data from an unexpected block number.
126    #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
127    UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
128    /// Static File Provider was initialized as read-only.
129    #[error("cannot get a writer on a read-only environment.")]
130    ReadOnlyStaticFileAccess,
131    /// Consistent view error.
132    #[error("failed to initialize consistent view: {_0}")]
133    ConsistentView(Box<ConsistentViewError>),
134    /// Storage lock error.
135    #[error(transparent)]
136    StorageLockError(#[from] StorageLockError),
137    /// Storage writer error.
138    #[error(transparent)]
139    UnifiedStorageWriterError(#[from] UnifiedStorageWriterError),
140    /// Received invalid output from configured storage implementation.
141    #[error("received invalid output from storage")]
142    InvalidStorageOutput,
143    /// Any other error type wrapped into a cloneable [`AnyError`].
144    #[error(transparent)]
145    Other(#[from] AnyError),
146}
147
148impl ProviderError {
149    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
150    /// [`AnyError`]
151    pub fn other<E>(error: E) -> Self
152    where
153        E: core::error::Error + Send + Sync + 'static,
154    {
155        Self::Other(AnyError::new(error))
156    }
157
158    /// Returns the arbitrary error if it is [`ProviderError::Other`]
159    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
160        match self {
161            Self::Other(err) => Some(err.as_error()),
162            _ => None,
163        }
164    }
165
166    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
167    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
168    /// otherwise.
169    pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
170        let other = self.as_other()?;
171        other.downcast_ref()
172    }
173
174    /// Returns true if the this type is a [`ProviderError::Other`] of that error
175    /// type. Returns false otherwise.
176    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
177        self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
178    }
179}
180
181impl DBErrorMarker for ProviderError {}
182
183impl From<alloy_rlp::Error> for ProviderError {
184    fn from(error: alloy_rlp::Error) -> Self {
185        Self::Rlp(error)
186    }
187}
188
189impl From<RecoveryError> for ProviderError {
190    fn from(_: RecoveryError) -> Self {
191        Self::SenderRecoveryError
192    }
193}
194
195/// A root mismatch error at a given block height.
196#[derive(Clone, Debug, PartialEq, Eq, Display)]
197#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
198pub struct RootMismatch {
199    /// The target block root diff.
200    pub root: GotExpected<B256>,
201    /// The target block number.
202    pub block_number: BlockNumber,
203    /// The target block hash.
204    pub block_hash: BlockHash,
205}
206
207/// A Static File Write Error.
208#[derive(Debug, thiserror::Error)]
209#[error("{message}")]
210pub struct StaticFileWriterError {
211    /// The error message.
212    pub message: String,
213}
214
215impl StaticFileWriterError {
216    /// Creates a new [`StaticFileWriterError`] with the given message.
217    #[allow(dead_code)]
218    pub fn new(message: impl Into<String>) -> Self {
219        Self { message: message.into() }
220    }
221}
222
223/// Consistent database view error.
224#[derive(Clone, Debug, PartialEq, Eq, Display)]
225pub enum ConsistentViewError {
226    /// Error thrown on attempt to initialize provider while node is still syncing.
227    #[display("node is syncing. best block: {best_block:?}")]
228    Syncing {
229        /// Best block diff.
230        best_block: GotExpected<BlockNumber>,
231    },
232    /// Error thrown on inconsistent database view.
233    #[display("inconsistent database state: {tip:?}")]
234    Inconsistent {
235        /// The tip diff.
236        tip: GotExpected<Option<B256>>,
237    },
238    /// Error thrown when the database does not contain a block from the previous database view.
239    #[display("database view no longer contains block: {block:?}")]
240    Reorged {
241        /// The previous block
242        block: B256,
243    },
244}
245
246impl From<ConsistentViewError> for ProviderError {
247    fn from(error: ConsistentViewError) -> Self {
248        Self::ConsistentView(Box::new(error))
249    }
250}
251
252#[cfg(test)]
253mod tests {
254    use super::*;
255
256    #[derive(thiserror::Error, Debug)]
257    #[error("E")]
258    struct E;
259
260    #[test]
261    fn other_err() {
262        let err = ProviderError::other(E);
263        assert!(err.is_other::<E>());
264        assert!(err.downcast_other_ref::<E>().is_some());
265    }
266}