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;
10
11pub type ProviderResult<Ok> = Result<Ok, ProviderError>;
13
14#[derive(Clone, Debug, thiserror::Error)]
16pub enum ProviderError {
17 #[error(transparent)]
19 Database(#[from] DatabaseError),
20 #[error(transparent)]
22 Pruning(#[from] PruneSegmentError),
23 #[error(transparent)]
25 StaticFileWriter(#[from] StaticFileWriterError),
26 #[error("{_0}")]
28 Rlp(alloy_rlp::Error),
29 #[error("trie witness error: {_0}")]
31 TrieWitnessError(String),
32 #[error("failed to recover sender for transaction")]
34 SenderRecoveryError,
35 #[error("block hash {_0} does not exist in Headers table")]
37 BlockHashNotFound(BlockHash),
38 #[error("block meta not found for block #{_0}")]
40 BlockBodyIndicesNotFound(BlockNumber),
41 #[error(
44 "storage change set for address {address} and key {storage_key} at block #{block_number} does not exist"
45 )]
46 StorageChangesetNotFound {
47 block_number: BlockNumber,
49 address: Address,
51 storage_key: Box<B256>,
55 },
56 #[error("account change set for address {address} at block #{block_number} does not exist")]
58 AccountChangesetNotFound {
59 block_number: BlockNumber,
61 address: Address,
63 },
64 #[error("no header found for {_0:?}")]
66 HeaderNotFound(BlockHashOrNumber),
67 #[error("no transaction found for {_0:?}")]
69 TransactionNotFound(HashOrNumber),
70 #[error("no receipt found for {_0:?}")]
72 ReceiptNotFound(HashOrNumber),
73 #[error("best block does not exist")]
75 BestBlockNotFound,
76 #[error("finalized block does not exist")]
78 FinalizedBlockNotFound,
79 #[error("safe block does not exist")]
81 SafeBlockNotFound,
82 #[error("unknown block {_0}")]
84 UnknownBlockHash(B256),
85 #[error("no state found for block {_0}")]
87 StateForHashNotFound(B256),
88 #[error("no state found for block number {_0}")]
90 StateForNumberNotFound(u64),
91 #[error("unable to find the block number for a given transaction index")]
93 BlockNumberForTransactionIndexNotFound,
94 #[error("merkle trie {_0}")]
96 StateRootMismatch(Box<RootMismatch>),
97 #[error("unwind merkle trie {_0}")]
99 UnwindStateRootMismatch(Box<RootMismatch>),
100 #[error("state at block #{_0} is pruned")]
102 StateAtBlockPruned(BlockNumber),
103 #[error("state at block #{requested} is not available, block has not been executed yet (latest executed: #{executed})")]
105 BlockNotExecuted {
106 requested: BlockNumber,
108 executed: BlockNumber,
110 },
111 #[error("block #{requested} is not available, history has expired (earliest available: #{earliest_available})")]
115 BlockExpired {
116 requested: BlockNumber,
118 earliest_available: BlockNumber,
120 },
121 #[error("this provider does not support this request")]
123 UnsupportedProvider,
124 #[cfg(feature = "std")]
126 #[error("not able to find {_0} static file at {_1:?}")]
127 MissingStaticFileSegmentPath(StaticFileSegment, std::path::PathBuf),
128 #[cfg(feature = "std")]
130 #[error("not able to find static file at {_0:?}")]
131 MissingStaticFilePath(std::path::PathBuf),
132 #[error("highest block is not found for {_0} static file")]
134 MissingHighestStaticFileBlock(StaticFileSegment),
135 #[error("not able to find {_0} static file for block number {_1}")]
137 MissingStaticFileBlock(StaticFileSegment, BlockNumber),
138 #[error("unable to find {_0} static file for transaction id {_1}")]
140 MissingStaticFileTx(StaticFileSegment, TxNumber),
141 #[error("unable to write block #{_1} to finalized static file {_0}")]
143 FinalizedStaticFile(StaticFileSegment, BlockNumber),
144 #[error("trying to append data to {_0} as block #{_1} but expected block #{_2}")]
146 UnexpectedStaticFileBlockNumber(StaticFileSegment, BlockNumber, BlockNumber),
147 #[error("trying to append row to {_0} at index #{_1} but expected index #{_2}")]
149 UnexpectedStaticFileTxNumber(StaticFileSegment, TxNumber, TxNumber),
150 #[error("changeset static file is corrupted, missing offsets for changesets in each block")]
152 CorruptedChangeSetStaticFile,
153 #[error("Unbounded start is unsupported in from_reverts")]
155 UnboundedStartUnsupported,
156 #[error("cannot get a writer on a read-only environment.")]
158 ReadOnlyStaticFileAccess,
159 #[error("failed to initialize consistent view: {_0}")]
161 ConsistentView(Box<ConsistentViewError>),
162 #[error("received invalid output from storage")]
164 InvalidStorageOutput,
165 #[error("missing trie updates for block {0}")]
167 MissingTrieUpdates(B256),
168 #[error("insufficient changesets to revert to block #{requested}. Available changeset range: {available:?}")]
170 InsufficientChangesets {
171 requested: BlockNumber,
173 available: core::ops::RangeInclusive<BlockNumber>,
175 },
176 #[error("consistency check failed for {data_source}. Db must be unwound to {unwind_to}")]
180 MustUnwind {
181 data_source: &'static str,
183 unwind_to: BlockNumber,
185 },
186 #[error(
188 "snap attempt record version {found:?} is not supported, this build writes {supported}"
189 )]
190 UnsupportedSnapAttemptVersion {
191 found: Option<u64>,
193 supported: u32,
195 },
196 #[error("snap sync requires the hashed state layout, which this database predates")]
199 SnapStorageLayoutUnsupported,
200 #[error("snap attempt {attempt} has not verified the downloaded state")]
202 UnverifiedSnapState {
203 attempt: u64,
205 },
206 #[error(
209 "snap attempt {attempt} left unverified state, restart with snap sync or wipe the database"
210 )]
211 SnapStateRequiresSnapSync {
212 attempt: u64,
214 },
215 #[error(transparent)]
217 Other(#[from] AnyError),
218}
219
220impl ProviderError {
221 pub fn other<E>(error: E) -> Self
224 where
225 E: core::error::Error + Send + Sync + 'static,
226 {
227 Self::Other(AnyError::new(error))
228 }
229
230 pub fn as_other(&self) -> Option<&(dyn core::error::Error + Send + Sync + 'static)> {
232 match self {
233 Self::Other(err) => Some(err.as_error()),
234 _ => None,
235 }
236 }
237
238 pub fn downcast_other_ref<T: core::error::Error + 'static>(&self) -> Option<&T> {
242 let other = self.as_other()?;
243 other.downcast_ref()
244 }
245
246 pub fn is_other<T: core::error::Error + 'static>(&self) -> bool {
249 self.as_other().is_some_and(|err| err.is::<T>())
250 }
251}
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<DecompressError> for ProviderError {
266 fn from(error: DecompressError) -> Self {
267 Self::Database(error.into())
268 }
269}
270
271#[derive(Clone, Debug, PartialEq, Eq, Display)]
273#[display("root mismatch at #{block_number} ({block_hash}): {root}")]
274pub struct RootMismatch {
275 pub root: GotExpected<B256>,
277 pub block_number: BlockNumber,
279 pub block_hash: BlockHash,
281}
282
283#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
285pub enum StaticFileWriterError {
286 #[error("cannot call sync_all or finalize when prune is queued, use commit() instead")]
288 FinalizeWithPruneQueued,
289 #[error("thread panicked: {_0}")]
291 ThreadPanic(&'static str),
292 #[error("{0}")]
294 Other(String),
295 #[error("cannot unwind {segment} to block {target} below the pruned anchor at block {anchor}")]
298 UnwindBelowPrunedAnchor {
299 segment: StaticFileSegment,
301 target: BlockNumber,
303 anchor: BlockNumber,
305 },
306}
307
308impl StaticFileWriterError {
309 pub fn new(message: impl Into<String>) -> Self {
311 Self::Other(message.into())
312 }
313}
314#[derive(Clone, Debug, PartialEq, Eq, Display)]
316pub enum ConsistentViewError {
317 #[display("node is syncing. best block: {best_block:?}")]
319 Syncing {
320 best_block: GotExpected<BlockNumber>,
322 },
323 #[display("inconsistent database state: {tip:?}")]
325 Inconsistent {
326 tip: GotExpected<Option<B256>>,
328 },
329 #[display("database view no longer contains block: {block:?}")]
331 Reorged {
332 block: B256,
334 },
335}
336
337impl From<ConsistentViewError> for ProviderError {
338 fn from(error: ConsistentViewError) -> Self {
339 Self::ConsistentView(Box::new(error))
340 }
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346
347 #[derive(thiserror::Error, Debug)]
348 #[error("E")]
349 struct E;
350
351 #[test]
352 fn other_err() {
353 let err = ProviderError::other(E);
354 assert!(err.is_other::<E>());
355 assert!(err.downcast_other_ref::<E>().is_some());
356 }
357}