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}