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::DBErrorMarker;
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    /// RLP error.
24    #[error("{_0}")]
25    Rlp(alloy_rlp::Error),
26    /// Trie witness error.
27    #[error("trie witness error: {_0}")]
28    TrieWitnessError(String),
29    /// Error when recovering the sender for a transaction
30    #[error("failed to recover sender for transaction")]
31    SenderRecoveryError,
32    /// The header number was not found for the given block hash.
33    #[error("block hash {_0} does not exist in Headers table")]
34    BlockHashNotFound(BlockHash),
35    /// A block body is missing.
36    #[error("block meta not found for block #{_0}")]
37    BlockBodyIndicesNotFound(BlockNumber),
38    /// The transition ID was found for the given address and storage key, but the changeset was
39    /// not found.
40    #[error(
41        "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
42    )]
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    /// When required header related data was not found but was required.
62    #[error("no header found for {_0:?}")]
63    HeaderNotFound(BlockHashOrNumber),
64    /// The specific transaction identified by hash or id is missing.
65    #[error("no transaction found for {_0:?}")]
66    TransactionNotFound(HashOrNumber),
67    /// The specific receipt for a transaction identified by hash or id is missing
68    #[error("no receipt found for {_0:?}")]
69    ReceiptNotFound(HashOrNumber),
70    /// Unable to find the best block.
71    #[error("best block does not exist")]
72    BestBlockNotFound,
73    /// Unable to find the finalized block.
74    #[error("finalized block does not exist")]
75    FinalizedBlockNotFound,
76    /// Unable to find the safe block.
77    #[error("safe block does not exist")]
78    SafeBlockNotFound,
79    /// Thrown when we failed to lookup a block for the pending state.
80    #[error("unknown block {_0}")]
81    UnknownBlockHash(B256),
82    /// Thrown when we were unable to find a state for a block hash.
83    #[error("no state found for block {_0}")]
84    StateForHashNotFound(B256),
85    /// Thrown when we were unable to find a state for a block number.
86    #[error("no state found for block number {_0}")]
87    StateForNumberNotFound(u64),
88    /// Unable to find the block number for a given transaction index.
89    #[error("unable to find the block number for a given transaction index")]
90    BlockNumberForTransactionIndexNotFound,
91    /// Root mismatch.
92    #[error("merkle trie {_0}")]
93    StateRootMismatch(Box<RootMismatch>),
94    /// Root mismatch during unwind
95    #[error("unwind merkle trie {_0}")]
96    UnwindStateRootMismatch(Box<RootMismatch>),
97    /// State is not available for the given block number because it is pruned.
98    #[error("state at block #{_0} is pruned")]
99    StateAtBlockPruned(BlockNumber),
100    /// Provider does not support this particular request.
101    #[error("this provider does not support this request")]
102    UnsupportedProvider,
103    /// Static File is not found at specified path.
104    #[cfg(feature = "std")]
105    #[error("not able to find {_0} static file at {_1:?}")]
106    MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
107    /// Static File is not found at specified path.
108    #[cfg(feature = "std")]
109    #[error("not able to find static file at {_0:?}")]
110    MissingStaticFilePath(std::path::PathBuf),
111    /// Static File is not found for requested block.
112    #[error("not able to find {_0} static file for block number {_1}")]
113    MissingStaticFileBlock(StaticFileSegment, BlockNumber),
114    /// Static File is not found for requested transaction.
115    #[error("unable to find {_0} static file for transaction id {_1}")]
116    MissingStaticFileTx(StaticFileSegment, TxNumber),
117    /// Static File is finalized and cannot be written to.
118    #[error("unable to write block #{_1} to finalized static file {_0}")]
119    FinalizedStaticFile(StaticFileSegment, BlockNumber),
120    /// Trying to insert data from an unexpected block number.
121    #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
122    UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
123    /// Trying to insert data from an unexpected block number.
124    #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
125    UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
126    /// Static File Provider was initialized as read-only.
127    #[error("cannot get a writer on a read-only environment.")]
128    ReadOnlyStaticFileAccess,
129    /// Consistent view error.
130    #[error("failed to initialize consistent view: {_0}")]
131    ConsistentView(Box<ConsistentViewError>),
132    /// Received invalid output from configured storage implementation.
133    #[error("received invalid output from storage")]
134    InvalidStorageOutput,
135    /// Missing trie updates.
136    #[error("missing trie updates for block {0}")]
137    MissingTrieUpdates(B256),
138    /// Insufficient changesets to revert to the requested block.
139    #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
140    InsufficientChangesets {
141        /// The block number requested for reversion
142        requested: BlockNumber,
143        /// The available range of blocks with changesets
144        available: core::ops::RangeInclusive<BlockNumber>,
145    },
146    /// Any other error type wrapped into a cloneable [`AnyError`].
147    #[error(transparent)]
148    Other(#[from] AnyError),
149}
150
151impl ProviderError {
152    /// Creates a new [`ProviderError::Other`] variant by wrapping the given error into an
153    /// [`AnyError`]
154    pub fn other<E>(error: E) -> Self
155    where
156        E: core::error::Error + Send + Sync + 'static,
157    {
158        Self::Other(AnyError::new(error))
159    }
160
161    /// Returns the arbitrary error if it is [`ProviderError::Other`]
162    pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
163        match self {
164            Self::Other(err) => Some(err.as_error()),
165            _ => None,
166        }
167    }
168
169    /// Returns a reference to the [`ProviderError::Other`] value if this type is a
170    /// [`ProviderError::Other`] and the [`AnyError`] wraps an error of that type. Returns None
171    /// otherwise.
172    pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
173        let other = self.as_other()?;
174        other.downcast_ref()
175    }
176
177    /// Returns true if the this type is a [`ProviderError::Other`] of that error
178    /// type. Returns false otherwise.
179    pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
180        self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
181    }
182}
183
184impl DBErrorMarker for ProviderError {}
185
186impl From<alloy_rlp::Error> for ProviderError {
187    fn from(error: alloy_rlp::Error) -> Self {
188        Self::Rlp(error)
189    }
190}
191
192impl From<RecoveryError> for ProviderError {
193    fn from(_: RecoveryError) -> Self {
194        Self::SenderRecoveryError
195    }
196}
197
198/// A root mismatch error at a given block height.
199#[derive(Clone, Debug, PartialEq, Eq, Display)]
200#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
201pub struct RootMismatch {
202    /// The target block root diff.
203    pub root: GotExpected<B256>,
204    /// The target block number.
205    pub block_number: BlockNumber,
206    /// The target block hash.
207    pub block_hash: BlockHash,
208}
209
210/// A Static File Write Error.
211#[derive(Debug, thiserror::Error)]
212#[error("{message}")]
213pub struct StaticFileWriterError {
214    /// The error message.
215    pub message: String,
216}
217
218impl StaticFileWriterError {
219    /// Creates a new [`StaticFileWriterError`] with the given message.
220    pub fn new(message: impl Into<String>) -> Self {
221        Self { message: message.into() }
222    }
223}
224/// Consistent database view error.
225#[derive(Clone, Debug, PartialEq, Eq, Display)]
226pub enum ConsistentViewError {
227    /// Error thrown on attempt to initialize provider while node is still syncing.
228    #[display("node is syncing. best block: {best_block:?}")]
229    Syncing {
230        /// Best block diff.
231        best_block: GotExpected<BlockNumber>,
232    },
233    /// Error thrown on inconsistent database view.
234    #[display("inconsistent database state: {tip:?}")]
235    Inconsistent {
236        /// The tip diff.
237        tip: GotExpected<Option<B256>>,
238    },
239    /// Error thrown when the database does not contain a block from the previous database view.
240    #[display("database view no longer contains block: {block:?}")]
241    Reorged {
242        /// The previous block
243        block: B256,
244    },
245}
246
247impl From<ConsistentViewError> for ProviderError {
248    fn from(error: ConsistentViewError) -> Self {
249        Self::ConsistentView(Box::new(error))
250    }
251}
252
253#[cfg(test)]
254mod tests {
255    use super::*;
256
257    #[derive(thiserror::Error, Debug)]
258    #[error("E")]
259    struct E;
260
261    #[test]
262    fn other_err() {
263        let err = ProviderError::other(E);
264        assert!(err.is_other::<E>());
265        assert!(err.downcast_other_ref::<E>().is_some());
266    }
267}