Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 68 additions & 21 deletions crates/consensus/consensus/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,16 @@

extern crate alloc;

use alloc::{boxed::Box, fmt::Debug, string::String, sync::Arc, vec::Vec};
use alloc::{
boxed::Box,
fmt::Debug,
string::{String, ToString},
sync::Arc,
vec::Vec,
};
use alloy_consensus::Header;
use alloy_primitives::{BlockHash, BlockNumber, Bloom, B256};
use core::error::Error;
use core::{error::Error, fmt::Display};

/// Pre-computed receipt root and logs bloom.
///
Expand Down Expand Up @@ -456,19 +462,49 @@ pub enum ConsensusError {
/// EIP-7825: Transaction gas limit exceeds maximum allowed
#[error(transparent)]
TransactionGasLimitTooHigh(Box<TxGasLimitTooHighErr>),
/// Other, likely an injected L2 error.
#[error("{0}")]
Other(String),
/// Other unspecified error.
/// Any additional consensus error, for example L2-specific errors.
#[error(transparent)]
Custom(#[from] Arc<dyn Error + Send + Sync>),
Other(#[from] Arc<dyn Error + Send + Sync>),
}

impl ConsensusError {
/// Returns a new [`ConsensusError::Other`] instance with the given error.
pub fn other<E>(error: E) -> Self
where
E: Error + Send + Sync + 'static,
{
Self::Other(Arc::new(error))
}

/// Returns a new [`ConsensusError::Other`] instance with the given message.
pub fn msg(msg: impl Display) -> Self {
Self::other(MessageError(msg.to_string()))
}

/// Returns `true` if the error is a state root error.
pub const fn is_state_root_error(&self) -> bool {
matches!(self, Self::BodyStateRootDiff(_))
}

/// Returns the arbitrary error if it is [`ConsensusError::Other`].
pub fn as_other(&self) -> Option<&(dyn Error + Send + Sync + 'static)> {
match self {
Self::Other(err) => Some(err.as_ref()),
_ => None,
}
}

/// Returns a reference to the [`ConsensusError::Other`] value if it is of that type.
/// Returns `None` otherwise.
pub fn downcast_other_ref<T: Error + 'static>(&self) -> Option<&T> {
let other = self.as_other()?;
other.downcast_ref()
}

/// Returns `true` if this type is a [`ConsensusError::Other`] of that error type.
pub fn is_other<T: Error + 'static>(&self) -> bool {
self.as_other().map(|err| err.is::<T>()).unwrap_or(false)
}
}

impl From<InvalidTransactionError> for ConsensusError {
Expand Down Expand Up @@ -500,6 +536,10 @@ pub struct TxGasLimitTooHighErr {
pub max_allowed: u64,
}

#[derive(Debug, thiserror::Error)]
#[error("{0}")]
struct MessageError(String);

#[cfg(test)]
mod tests {
use super::*;
Expand All @@ -509,24 +549,31 @@ mod tests {
struct CustomL2Error;

#[test]
fn test_custom_error_conversion() {
// Test conversion from custom error to ConsensusError
let custom_err = CustomL2Error;
let arc_err: Arc<dyn Error + Send + Sync> = Arc::new(custom_err);
let consensus_err: ConsensusError = arc_err.into();

// Verify it's the Custom variant
assert!(matches!(consensus_err, ConsensusError::Custom(_)));
fn test_other_error_conversion() {
let consensus_err = ConsensusError::other(CustomL2Error);
assert!(matches!(consensus_err, ConsensusError::Other(_)));
}

#[test]
fn test_custom_error_display() {
let custom_err = CustomL2Error;
let arc_err: Arc<dyn Error + Send + Sync> = Arc::new(custom_err);
let consensus_err: ConsensusError = arc_err.into();

// Verify the error message is preserved through transparent attribute
fn test_other_error_display() {
let consensus_err = ConsensusError::other(CustomL2Error);
let error_message = format!("{}", consensus_err);
assert_eq!(error_message, "Custom L2 consensus error");
}

#[test]
fn test_other_error_downcast() {
let consensus_err = ConsensusError::other(CustomL2Error);

assert!(consensus_err.is_other::<CustomL2Error>());
assert!(consensus_err.downcast_other_ref::<CustomL2Error>().is_some());
}

#[test]
fn test_other_msg() {
let consensus_err = ConsensusError::msg("consensus message");

assert_eq!(consensus_err.to_string(), "consensus message");
assert!(consensus_err.downcast_other_ref::<MessageError>().is_some());
}
}
Loading