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::{
11 database_interface::{bal::EvmDatabaseError, DBErrorMarker},
12 state::bal::BalError,
13};
14
15pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
17
18#[derive(Clone, Debug, thiserror::Error)]
20pub enum ProviderError {
21 #[error(transparent)]
23 Database(#[from] DatabaseError),
24 #[error("BAL error:{_0}")]
26 Bal(BalError),
27 #[error(transparent)]
29 Pruning(#[from] PruneSegmentError),
30 #[error(transparent)]
32 StaticFileWriter(#[from] StaticFileWriterError),
33 #[error("{_0}")]
35 Rlp(alloy_rlp::Error),
36 #[error("trie witness error: {_0}")]
38 TrieWitnessError(String),
39 #[error("failed to recover sender for transaction")]
41 SenderRecoveryError,
42 #[error("block hash {_0} does not exist in Headers table")]
44 BlockHashNotFound(BlockHash),
45 #[error("block meta not found for block #{_0}")]
47 BlockBodyIndicesNotFound(BlockNumber),
48 #[error(
51 "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
52 )]
53 StorageChangesetNotFound {
54 block_number: BlockNumber,
56 address: Address,
58 storage_key: Box<B256>,
62 },
63 #[error("account change set for address {address} at block #{block_number} does not exist")]
65 AccountChangesetNotFound {
66 block_number: BlockNumber,
68 address: Address,
70 },
71 #[error("no header found for {_0:?}")]
73 HeaderNotFound(BlockHashOrNumber),
74 #[error("no transaction found for {_0:?}")]
76 TransactionNotFound(HashOrNumber),
77 #[error("no receipt found for {_0:?}")]
79 ReceiptNotFound(HashOrNumber),
80 #[error("best block does not exist")]
82 BestBlockNotFound,
83 #[error("finalized block does not exist")]
85 FinalizedBlockNotFound,
86 #[error("safe block does not exist")]
88 SafeBlockNotFound,
89 #[error("unknown block {_0}")]
91 UnknownBlockHash(B256),
92 #[error("no state found for block {_0}")]
94 StateForHashNotFound(B256),
95 #[error("no state found for block number {_0}")]
97 StateForNumberNotFound(u64),
98 #[error("unable to find the block number for a given transaction index")]
100 BlockNumberForTransactionIndexNotFound,
101 #[error("merkle trie {_0}")]
103 StateRootMismatch(Box<RootMismatch>),
104 #[error("unwind merkle trie {_0}")]
106 UnwindStateRootMismatch(Box<RootMismatch>),
107 #[error("state at block #{_0} is pruned")]
109 StateAtBlockPruned(BlockNumber),
110 #[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
112 BlockNotExecuted {
113 requested: BlockNumber,
115 executed: BlockNumber,
117 },
118 #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
122 BlockExpired {
123 requested: BlockNumber,
125 earliest_available: BlockNumber,
127 },
128 #[error("this provider does not support this request")]
130 UnsupportedProvider,
131 #[cfg(feature = "std")]
133 #[error("not able to find {_0} static file at {_1:?}")]
134 MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
135 #[cfg(feature = "std")]
137 #[error("not able to find static file at {_0:?}")]
138 MissingStaticFilePath(std::path::PathBuf),
139 #[error("highest block is not found for {_0} static file")]
141 MissingHighestStaticFileBlock(StaticFileSegment),
142 #[error("not able to find {_0} static file for block number {_1}")]
144 MissingStaticFileBlock(StaticFileSegment, BlockNumber),
145 #[error("unable to find {_0} static file for transaction id {_1}")]
147 MissingStaticFileTx(StaticFileSegment, TxNumber),
148 #[error("unable to write block #{_1} to finalized static file {_0}")]
150 FinalizedStaticFile(StaticFileSegment, BlockNumber),
151 #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
153 UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
154 #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
156 UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
157 #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
159 CorruptedChangeSetStaticFile,
160 #[error("Unbounded start is unsupported in from_reverts")]
162 UnboundedStartUnsupported,
163 #[error("cannot get a writer on a read-only environment.")]
165 ReadOnlyStaticFileAccess,
166 #[error("failed to initialize consistent view: {_0}")]
168 ConsistentView(Box<ConsistentViewError>),
169 #[error("received invalid output from storage")]
171 InvalidStorageOutput,
172 #[error("missing trie updates for block {0}")]
174 MissingTrieUpdates(B256),
175 #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
177 InsufficientChangesets {
178 requested: BlockNumber,
180 available: core::ops::RangeInclusive<BlockNumber>,
182 },
183 #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
187 MustUnwind {
188 data_source: &'static str,
190 unwind_to: BlockNumber,
192 },
193 #[error(
195 "snap attempt record version {found:?} is not supported, this build writes {supported}"
196 )]
197 UnsupportedSnapAttemptVersion {
198 found: Option<u64>,
200 supported: u32,
202 },
203 #[error("snap attempt {attempt} has not verified the downloaded state")]
205 UnverifiedSnapState {
206 attempt: u64,
208 },
209 #[error(transparent)]
211 Other(#[from] AnyError),
212}
213
214impl ProviderError {
215 pub fn other<E>(error: E) -> Self
218 where
219 E: core::error::Error + Send + Sync + 'static,
220 {
221 Self::Other(AnyError::new(error))
222 }
223
224 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
226 match self {
227 Self::Other(err) => Some(err.as_error()),
228 _ => None,
229 }
230 }
231
232 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
236 let other = self.as_other()?;
237 other.downcast_ref()
238 }
239
240 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
243 self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
244 }
245}
246
247impl DBErrorMarker for ProviderError {}
248
249impl From<alloy_rlp::Error> for ProviderError {
250 fn from(error: alloy_rlp::Error) -> Self {
251 Self::Rlp(error)
252 }
253}
254
255impl From<RecoveryError> for ProviderError {
256 fn from(_: RecoveryError) -> Self {
257 Self::SenderRecoveryError
258 }
259}
260
261impl From<ProviderError> for EvmDatabaseError<ProviderError> {
262 fn from(error: ProviderError) -> Self {
263 Self::Database(error)
264 }
265}
266
267impl From<DecompressError> for ProviderError {
268 fn from(error: DecompressError) -> Self {
269 Self::Database(error.into())
270 }
271}
272
273#[derive(Clone, Debug, PartialEq, Eq, Display)]
275#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
276pub struct RootMismatch {
277 pub root: GotExpected<B256>,
279 pub block_number: BlockNumber,
281 pub block_hash: BlockHash,
283}
284
285#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
287pub enum StaticFileWriterError {
288 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
290 FinalizeWithPruneQueued,
291 #[error("thread panicked: {_0}")]
293 ThreadPanic(&'static str),
294 #[error("{0}")]
296 Other(String),
297}
298
299impl StaticFileWriterError {
300 pub fn new(message: impl Into<String>) -> Self {
302 Self::Other(message.into())
303 }
304}
305#[derive(Clone, Debug, PartialEq, Eq, Display)]
307pub enum ConsistentViewError {
308 #[display("node is syncing. best block: {best_block:?}")]
310 Syncing {
311 best_block: GotExpected<BlockNumber>,
313 },
314 #[display("inconsistent database state: {tip:?}")]
316 Inconsistent {
317 tip: GotExpected<Option<B256>>,
319 },
320 #[display("database view no longer contains block: {block:?}")]
322 Reorged {
323 block: B256,
325 },
326}
327
328impl From<ConsistentViewError> for ProviderError {
329 fn from(error: ConsistentViewError) -> Self {
330 Self::ConsistentView(Box::new(error))
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::*;
337
338 #[derive(thiserror::Error, Debug)]
339 #[error("E")]
340 struct E;
341
342 #[test]
343 fn other_err() {
344 let err = ProviderError::other(E);
345 assert!(err.is_other::<E>());
346 assert!(err.downcast_other_ref::<E>().is_some());
347 }
348}