kona_derive/errors/
pipeline.rs

1//! This module contains derivation errors thrown within the pipeline.
2
3use crate::BuilderError;
4use alloc::string::String;
5use alloy_primitives::B256;
6use kona_genesis::SystemConfigUpdateError;
7use kona_protocol::{DepositError, SpanBatchError};
8use thiserror::Error;
9
10/// [crate::ensure] is a short-hand for bubbling up errors in the case of a condition not being met.
11#[macro_export]
12macro_rules! ensure {
13    ($cond:expr, $err:expr) => {
14        if !($cond) {
15            return Err($err);
16        }
17    };
18}
19
20/// A top-level severity filter for [`PipelineError`] that categorizes errors by handling strategy.
21///
22/// The [`PipelineErrorKind`] wrapper provides a severity classification system that enables
23/// sophisticated error handling in the derivation pipeline. Different error types require
24/// different response strategies:
25///
26/// - **Temporary**: Retry-able errors that may resolve with more data
27/// - **Critical**: Fatal errors that require external intervention
28/// - **Reset**: Errors that require pipeline state reset but allow continued operation
29///
30/// # Error Handling Strategy
31/// ```text
32/// Temporary -> Retry operation, may succeed with more data
33/// Critical  -> Stop derivation, external intervention required  
34/// Reset     -> Reset pipeline state, continue with clean slate
35/// ```
36///
37/// # Usage in Pipeline
38/// Error kinds are used by pipeline stages to determine appropriate error handling:
39/// - Temporary errors trigger retries in the main derivation loop
40/// - Critical errors halt derivation and bubble up to the caller
41/// - Reset errors trigger pipeline resets with appropriate recovery logic
42#[derive(Error, Debug, PartialEq, Eq)]
43pub enum PipelineErrorKind {
44    /// A temporary error that may resolve with additional data or time.
45    ///
46    /// Temporary errors indicate transient conditions such as insufficient data,
47    /// network timeouts, or resource unavailability. These errors suggest that
48    /// retrying the operation may succeed once the underlying condition resolves.
49    ///
50    /// # Examples
51    /// - Not enough L1 data available yet
52    /// - Network communication timeouts
53    /// - Insufficient channel data for frame assembly
54    ///
55    /// # Handling
56    /// The pipeline typically retries temporary errors in a loop, waiting for
57    /// conditions to improve or for additional data to become available.
58    #[error("Temporary error: {0}")]
59    Temporary(#[source] PipelineError),
60    /// A critical error that requires external intervention to resolve.
61    ///
62    /// Critical errors indicate fundamental issues that cannot be resolved through
63    /// retries or pipeline resets. These errors require external intervention such
64    /// as updated L1 data, configuration changes, or system fixes.
65    ///
66    /// # Examples
67    /// - Data source completely exhausted
68    /// - Fundamental configuration errors
69    /// - Irrecoverable data corruption
70    ///
71    /// # Handling
72    /// Critical errors halt the derivation process and are returned to the caller
73    /// for external resolution. The pipeline cannot continue without intervention.
74    #[error("Critical error: {0}")]
75    Critical(#[source] PipelineError),
76    /// A reset error that requires pipeline state reset but allows continued operation.
77    ///
78    /// Reset errors indicate conditions that invalidate the current pipeline state
79    /// but can be resolved by resetting to a known good state and continuing
80    /// derivation. These typically occur due to chain reorganizations or state
81    /// inconsistencies.
82    ///
83    /// # Examples
84    /// - L1 chain reorganization detected
85    /// - Block hash mismatches indicating reorg
86    /// - Hard fork activation requiring state reset
87    ///
88    /// # Handling
89    /// Reset errors trigger pipeline state cleanup and reset to a safe state,
90    /// after which derivation can continue with fresh state.
91    #[error("Pipeline reset: {0}")]
92    Reset(#[from] ResetError),
93}
94
95/// An error encountered during derivation pipeline processing.
96///
97/// [`PipelineError`] represents specific error conditions that can occur during the
98/// various stages of L2 block derivation from L1 data. Each error variant provides
99/// detailed context about the failure mode and suggests appropriate recovery strategies.
100///
101/// # Error Categories
102///
103/// ## Data Availability Errors
104/// - [`Self::Eof`]: No more data available from source
105/// - [`Self::NotEnoughData`]: Insufficient data for current operation
106/// - [`Self::MissingL1Data`]: Required L1 data not available
107/// - [`Self::EndOfSource`]: Data source completely exhausted
108///
109/// ## Stage-Specific Errors  
110/// - [`Self::ChannelProviderEmpty`]: No channels available for processing
111/// - [`Self::ChannelReaderEmpty`]: Channel reader has no data
112/// - [`Self::BatchQueueEmpty`]: No batches available for processing
113///
114/// ## Validation Errors
115/// - [`Self::InvalidBatchType`]: Unsupported or malformed batch type
116/// - [`Self::InvalidBatchValidity`]: Batch failed validation checks
117/// - [`Self::BadEncoding`]: Data decoding/encoding failures
118///
119/// ## System Errors
120/// - [`Self::SystemConfigUpdate`]: System configuration update failures
121/// - [`Self::AttributesBuilder`]: Block attribute construction failures
122/// - [`Self::Provider`]: External provider communication failures
123#[derive(Error, Debug, PartialEq, Eq)]
124pub enum PipelineError {
125    /// End of file: no more data available from the channel bank.
126    ///
127    /// This error indicates that the channel bank has been completely drained
128    /// and no additional frame data is available for processing. It typically
129    /// occurs at the end of a derivation sequence when all available L1 data
130    /// has been consumed.
131    ///
132    /// # Recovery
133    /// Usually indicates completion of derivation for available data. May
134    /// require waiting for new L1 blocks to provide additional frame data.
135    #[error("EOF")]
136    Eof,
137    /// Insufficient data available to complete the current processing stage.
138    ///
139    /// This error indicates that the current operation requires more data than
140    /// is currently available, but additional data may become available in the
141    /// future. It suggests that retrying the operation later may succeed.
142    ///
143    /// # Common Scenarios
144    /// - Partial frame received, waiting for completion
145    /// - Channel assembly requires more frames
146    /// - Batch construction needs additional channel data
147    ///
148    /// # Recovery
149    /// Retry the operation after more L1 data becomes available or after
150    /// waiting for network propagation delays.
151    #[error("Not enough data")]
152    NotEnoughData,
153    /// No channels are available in the [`ChannelProvider`].
154    ///
155    /// This error occurs when the channel provider stage has no assembled
156    /// channels ready for reading. It typically indicates that frame assembly
157    /// is still in progress or that no valid channels have been constructed
158    /// from available L1 data.
159    ///
160    /// [`ChannelProvider`]: crate::stages::ChannelProvider
161    #[error("The channel provider is empty")]
162    ChannelProviderEmpty,
163    /// The channel has already been fully processed by the [`ChannelAssembler`] stage.
164    ///
165    /// This error indicates an attempt to reprocess a channel that has already
166    /// been assembled and consumed. It suggests a logic error in channel tracking
167    /// or an attempt to double-process the same channel data.
168    ///
169    /// [`ChannelAssembler`]: crate::stages::ChannelAssembler
170    #[error("Channel already built")]
171    ChannelAlreadyBuilt,
172    /// Failed to locate the requested channel in the [`ChannelProvider`].
173    ///
174    /// This error occurs when attempting to access a specific channel that
175    /// is not available in the channel provider's cache or storage. It may
176    /// indicate a channel ID mismatch or premature channel eviction.
177    ///
178    /// [`ChannelProvider`]: crate::stages::ChannelProvider
179    #[error("Channel not found in channel provider")]
180    ChannelNotFound,
181    /// No channel data returned by the [`ChannelReader`] stage.
182    ///
183    /// This error indicates that the channel reader stage has no channels
184    /// available for reading. It typically occurs when all channels have
185    /// been consumed or when no valid channels have been assembled yet.
186    ///
187    /// [`ChannelReader`]: crate::stages::ChannelReader
188    #[error("The channel reader has no channel available")]
189    ChannelReaderEmpty,
190    /// The [`BatchQueue`] contains no batches ready for processing.
191    ///
192    /// This error occurs when the batch queue stage has no assembled batches
193    /// available for attribute generation. It indicates that batch assembly
194    /// is still in progress or that no valid batches have been constructed.
195    ///
196    /// [`BatchQueue`]: crate::stages::BatchQueue
197    #[error("The batch queue has no batches available")]
198    BatchQueueEmpty,
199    /// Required L1 origin information is missing from the previous pipeline stage.
200    ///
201    /// This error indicates a pipeline stage dependency violation where a stage
202    /// expects L1 origin information that wasn't provided by the preceding stage.
203    /// It suggests a configuration or sequencing issue in the pipeline setup.
204    #[error("Missing L1 origin from previous stage")]
205    MissingOrigin,
206    /// Required L1 data is missing from the [`L1Retrieval`] stage.
207    ///
208    /// This error occurs when the L1 retrieval stage cannot provide the
209    /// requested L1 block data, transactions, or receipts. It may indicate
210    /// network issues, data availability problems, or L1 node synchronization lag.
211    ///
212    /// [`L1Retrieval`]: crate::stages::L1Retrieval
213    #[error("L1 Retrieval missing data")]
214    MissingL1Data,
215    /// Invalid or unsupported batch type encountered during processing.
216    ///
217    /// This error occurs when a pipeline stage receives a batch type that
218    /// it cannot process or that violates the expected batch format. It
219    /// indicates either malformed L1 data or unsupported batch versions.
220    #[error("Invalid batch type passed to stage")]
221    InvalidBatchType,
222    /// Batch failed validation checks during processing.
223    ///
224    /// This error indicates that a batch contains invalid data that fails
225    /// validation rules such as timestamp constraints, parent hash checks,
226    /// or format requirements. It suggests potentially malicious or corrupted L1 data.
227    #[error("Invalid batch validity")]
228    InvalidBatchValidity,
229    /// [`SystemConfig`] update operation failed.
230    ///
231    /// This error occurs when attempting to update the system configuration
232    /// fails due to invalid parameters, version mismatches, or other
233    /// configuration-related issues.
234    ///
235    /// [`SystemConfig`]: kona_genesis::SystemConfig
236    #[error("Error updating system config: {0}")]
237    SystemConfigUpdate(SystemConfigUpdateError),
238    /// Block attributes construction failed with detailed error information.
239    ///
240    /// This error wraps [`BuilderError`] variants that occur during the
241    /// construction of block attributes from batch data. It indicates issues
242    /// with attribute validation, formatting, or consistency checks.
243    #[error("Attributes builder error: {0}")]
244    AttributesBuilder(#[from] BuilderError),
245    /// Data encoding or decoding operation failed.
246    ///
247    /// This error wraps [`PipelineEncodingError`] variants that occur during
248    /// serialization or deserialization of pipeline data structures. It
249    /// indicates malformed input data or encoding format violations.
250    #[error("Decode error: {0}")]
251    BadEncoding(#[from] PipelineEncodingError),
252    /// The data source has been completely exhausted and cannot provide more data.
253    ///
254    /// This error indicates that the underlying L1 data source has reached
255    /// its end and no additional data will become available. It typically
256    /// occurs when derivation has caught up to the L1 chain head.
257    #[error("Data source exhausted")]
258    EndOfSource,
259    /// External provider communication or operation failed.
260    ///
261    /// This error wraps failures from external data providers such as L1
262    /// nodes, blob providers, or other data sources. It includes network
263    /// failures, API errors, and provider-specific issues.
264    #[error("Provider error: {0}")]
265    Provider(String),
266    /// The pipeline received an unsupported signal type.
267    ///
268    /// This error occurs when a pipeline stage receives a signal that it
269    /// cannot process or that is not supported in the current configuration.
270    /// It indicates a protocol version mismatch or configuration issue.
271    #[error("Unsupported signal")]
272    UnsupportedSignal,
273}
274
275impl PipelineError {
276    /// Wraps this [`PipelineError`] as a [PipelineErrorKind::Critical].
277    ///
278    /// Critical errors indicate fundamental issues that cannot be resolved through
279    /// retries or pipeline resets. They require external intervention to resolve.
280    ///
281    /// # Usage
282    /// Use this method when an error condition is unrecoverable and requires
283    /// halting the derivation process for external intervention.
284    ///
285    /// # Example
286    /// ```rust,ignore
287    /// if data_source_corrupted {
288    ///     return Err(PipelineError::Provider("corrupted data".to_string()).crit());
289    /// }
290    /// ```
291    pub const fn crit(self) -> PipelineErrorKind {
292        PipelineErrorKind::Critical(self)
293    }
294
295    /// Wraps this [`PipelineError`] as a [PipelineErrorKind::Temporary].
296    ///
297    /// Temporary errors indicate transient conditions that may resolve with
298    /// additional data, time, or retries. The pipeline can attempt to recover
299    /// by retrying the operation.
300    ///
301    /// # Usage
302    /// Use this method when an error condition might resolve if the operation
303    /// is retried, particularly for data availability or network issues.
304    ///
305    /// # Example
306    /// ```rust,ignore
307    /// if insufficient_data {
308    ///     return Err(PipelineError::NotEnoughData.temp());
309    /// }
310    /// ```
311    pub const fn temp(self) -> PipelineErrorKind {
312        PipelineErrorKind::Temporary(self)
313    }
314}
315
316/// A reset error
317#[derive(Error, Clone, Debug, Eq, PartialEq)]
318pub enum ResetError {
319    /// The batch has a bad parent hash.
320    /// The first argument is the expected parent hash, and the second argument is the actual
321    /// parent hash.
322    #[error("Bad parent hash: expected {0}, got {1}")]
323    BadParentHash(B256, B256),
324    /// The batch has a bad timestamp.
325    /// The first argument is the expected timestamp, and the second argument is the actual
326    /// timestamp.
327    #[error("Bad timestamp: expected {0}, got {1}")]
328    BadTimestamp(u64, u64),
329    /// L1 origin mismatch.
330    #[error("L1 origin mismatch. Expected {0:?}, got {1:?}")]
331    L1OriginMismatch(u64, u64),
332    /// The stage detected a block reorg.
333    /// The first argument is the expected block hash.
334    /// The second argument is the parent_hash of the next l1 origin block.
335    #[error("L1 reorg detected: expected {0}, got {1}")]
336    ReorgDetected(B256, B256),
337    /// Attributes builder error variant, with [`BuilderError`].
338    #[error("Attributes builder error: {0}")]
339    AttributesBuilder(#[from] BuilderError),
340    /// A Holocene activation temporary error.
341    #[error("Holocene activation reset")]
342    HoloceneActivation,
343    /// The next l1 block provided to the managed traversal stage is not the expected one.
344    #[error("Next L1 block hash mismatch: expected {0}, got {1}")]
345    NextL1BlockHashMismatch(B256, B256),
346}
347
348impl ResetError {
349    /// Wrap [`ResetError`] as a [PipelineErrorKind::Reset].
350    pub const fn reset(self) -> PipelineErrorKind {
351        PipelineErrorKind::Reset(self)
352    }
353}
354
355/// A decoding error.
356#[derive(Error, Debug, PartialEq, Eq)]
357pub enum PipelineEncodingError {
358    /// The buffer is empty.
359    #[error("Empty buffer")]
360    EmptyBuffer,
361    /// Deposit decoding error.
362    #[error("Error decoding deposit: {0}")]
363    DepositError(#[from] DepositError),
364    /// Alloy RLP Encoding Error.
365    #[error("RLP error: {0}")]
366    AlloyRlpError(alloy_rlp::Error),
367    /// Span Batch Error.
368    #[error("{0}")]
369    SpanBatchError(#[from] SpanBatchError),
370}
371
372#[cfg(test)]
373mod tests {
374    use super::*;
375    use core::error::Error;
376
377    #[test]
378    fn test_pipeline_error_kind_source() {
379        let err = PipelineErrorKind::Temporary(PipelineError::Eof);
380        assert!(err.source().is_some());
381
382        let err = PipelineErrorKind::Critical(PipelineError::Eof);
383        assert!(err.source().is_some());
384
385        let err = PipelineErrorKind::Reset(ResetError::BadParentHash(
386            Default::default(),
387            Default::default(),
388        ));
389        assert!(err.source().is_some());
390    }
391
392    #[test]
393    fn test_pipeline_error_source() {
394        let err = PipelineError::AttributesBuilder(BuilderError::BlockMismatch(
395            Default::default(),
396            Default::default(),
397        ));
398        assert!(err.source().is_some());
399
400        let encoding_err = PipelineEncodingError::EmptyBuffer;
401        let err: PipelineError = encoding_err.into();
402        assert!(err.source().is_some());
403
404        let err = PipelineError::Eof;
405        assert!(err.source().is_none());
406    }
407
408    #[test]
409    fn test_pipeline_encoding_error_source() {
410        let err = PipelineEncodingError::DepositError(DepositError::UnexpectedTopicsLen(0));
411        assert!(err.source().is_some());
412
413        let err = SpanBatchError::TooBigSpanBatchSize;
414        let err: PipelineEncodingError = err.into();
415        assert!(err.source().is_some());
416
417        let err = PipelineEncodingError::EmptyBuffer;
418        assert!(err.source().is_none());
419    }
420
421    #[test]
422    fn test_reset_error_kinds() {
423        let reset_errors = [
424            ResetError::BadParentHash(Default::default(), Default::default()),
425            ResetError::BadTimestamp(0, 0),
426            ResetError::L1OriginMismatch(0, 0),
427            ResetError::ReorgDetected(Default::default(), Default::default()),
428            ResetError::AttributesBuilder(BuilderError::BlockMismatch(
429                Default::default(),
430                Default::default(),
431            )),
432            ResetError::HoloceneActivation,
433        ];
434        for error in reset_errors.into_iter() {
435            let expected = PipelineErrorKind::Reset(error.clone());
436            assert_eq!(error.reset(), expected);
437        }
438    }
439}