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 sync requires the hashed state layout, which this database predates")]
206 SnapStorageLayoutUnsupported,
207 #[error("snap attempt {attempt} has not verified the downloaded state")]
209 UnverifiedSnapState {
210 attempt: u64,
212 },
213 #[error(
216 "snap attempt {attempt} left unverified state, restart with snap sync or wipe the database"
217 )]
218 SnapStateRequiresSnapSync {
219 attempt: u64,
221 },
222 #[error(transparent)]
224 Other(#[from] AnyError),
225}
226
227impl ProviderError {
228 pub fn other<E>(error: E) -> Self
231 where
232 E: core::error::Error + Send + Sync + 'static,
233 {
234 Self::Other(AnyError::new(error))
235 }
236
237 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
239 match self {
240 Self::Other(err) => Some(err.as_error()),
241 _ => None,
242 }
243 }
244
245 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
249 let other = self.as_other()?;
250 other.downcast_ref()
251 }
252
253 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
256 self.as_other().is_some_and(|err| err.is::<T>())
257 }
258}
259
260impl DBErrorMarker for ProviderError {}
261
262impl From<alloy_rlp::Error> for ProviderError {
263 fn from(error: alloy_rlp::Error) -> Self {
264 Self::Rlp(error)
265 }
266}
267
268impl From<RecoveryError> for ProviderError {
269 fn from(_: RecoveryError) -> Self {
270 Self::SenderRecoveryError
271 }
272}
273
274impl From<ProviderError> for EvmDatabaseError<ProviderError> {
275 fn from(error: ProviderError) -> Self {
276 Self::Database(error)
277 }
278}
279
280impl From<DecompressError> for ProviderError {
281 fn from(error: DecompressError) -> Self {
282 Self::Database(error.into())
283 }
284}
285
286#[derive(Clone, Debug, PartialEq, Eq, Display)]
288#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
289pub struct RootMismatch {
290 pub root: GotExpected<B256>,
292 pub block_number: BlockNumber,
294 pub block_hash: BlockHash,
296}
297
298#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
300pub enum StaticFileWriterError {
301 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
303 FinalizeWithPruneQueued,
304 #[error("thread panicked: {_0}")]
306 ThreadPanic(&'static str),
307 #[error("{0}")]
309 Other(String),
310}
311
312impl StaticFileWriterError {
313 pub fn new(message: impl Into<String>) -> Self {
315 Self::Other(message.into())
316 }
317}
318#[derive(Clone, Debug, PartialEq, Eq, Display)]
320pub enum ConsistentViewError {
321 #[display("node is syncing. best block: {best_block:?}")]
323 Syncing {
324 best_block: GotExpected<BlockNumber>,
326 },
327 #[display("inconsistent database state: {tip:?}")]
329 Inconsistent {
330 tip: GotExpected<Option<B256>>,
332 },
333 #[display("database view no longer contains block: {block:?}")]
335 Reorged {
336 block: B256,
338 },
339}
340
341impl From<ConsistentViewError> for ProviderError {
342 fn from(error: ConsistentViewError) -> Self {
343 Self::ConsistentView(Box::new(error))
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350
351 #[derive(thiserror::Error, Debug)]
352 #[error("E")]
353 struct E;
354
355 #[test]
356 fn other_err() {
357 let err = ProviderError::other(E);
358 assert!(err.is_other::<E>());
359 assert!(err.downcast_other_ref::<E>().is_some());
360 }
361}