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(transparent)]
215 Other(#[from] AnyError),
216}
217
218impl ProviderError {
219 pub fn other<E>(error: E) -> Self
222 where
223 E: core::error::Error + Send + Sync + 'static,
224 {
225 Self::Other(AnyError::new(error))
226 }
227
228 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
230 match self {
231 Self::Other(err) => Some(err.as_error()),
232 _ => None,
233 }
234 }
235
236 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
240 let other = self.as_other()?;
241 other.downcast_ref()
242 }
243
244 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
247 self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
248 }
249}
250
251impl DBErrorMarker for ProviderError {}
252
253impl From<alloy_rlp::Error> for ProviderError {
254 fn from(error: alloy_rlp::Error) -> Self {
255 Self::Rlp(error)
256 }
257}
258
259impl From<RecoveryError> for ProviderError {
260 fn from(_: RecoveryError) -> Self {
261 Self::SenderRecoveryError
262 }
263}
264
265impl From<ProviderError> for EvmDatabaseError<ProviderError> {
266 fn from(error: ProviderError) -> Self {
267 Self::Database(error)
268 }
269}
270
271impl From<DecompressError> for ProviderError {
272 fn from(error: DecompressError) -> Self {
273 Self::Database(error.into())
274 }
275}
276
277#[derive(Clone, Debug, PartialEq, Eq, Display)]
279#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
280pub struct RootMismatch {
281 pub root: GotExpected<B256>,
283 pub block_number: BlockNumber,
285 pub block_hash: BlockHash,
287}
288
289#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
291pub enum StaticFileWriterError {
292 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
294 FinalizeWithPruneQueued,
295 #[error("thread panicked: {_0}")]
297 ThreadPanic(&'static str),
298 #[error("{0}")]
300 Other(String),
301}
302
303impl StaticFileWriterError {
304 pub fn new(message: impl Into<String>) -> Self {
306 Self::Other(message.into())
307 }
308}
309#[derive(Clone, Debug, PartialEq, Eq, Display)]
311pub enum ConsistentViewError {
312 #[display("node is syncing. best block: {best_block:?}")]
314 Syncing {
315 best_block: GotExpected<BlockNumber>,
317 },
318 #[display("inconsistent database state: {tip:?}")]
320 Inconsistent {
321 tip: GotExpected<Option<B256>>,
323 },
324 #[display("database view no longer contains block: {block:?}")]
326 Reorged {
327 block: B256,
329 },
330}
331
332impl From<ConsistentViewError> for ProviderError {
333 fn from(error: ConsistentViewError) -> Self {
334 Self::ConsistentView(Box::new(error))
335 }
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341
342 #[derive(thiserror::Error, Debug)]
343 #[error("E")]
344 struct E;
345
346 #[test]
347 fn other_err() {
348 let err = ProviderError::other(E);
349 assert!(err.is_other::<E>());
350 assert!(err.downcast_other_ref::<E>().is_some());
351 }
352}