kona_interop/
errors.rs

1//! Error types for the `kona-interop` crate.
2
3use crate::{InteropProvider, SafetyLevel};
4use alloc::vec::Vec;
5use alloy_primitives::{Address, B256};
6use thiserror::Error;
7
8/// Derived from op-supervisor
9// todo: rm once resolved <https://github.com/ethereum-optimism/optimism/issues/14603>
10const UNKNOWN_CHAIN_MSG: &str = "unknown chain: ";
11/// Derived from [op-supervisor](https://github.com/ethereum-optimism/optimism/blob/4ba2eb00eafc3d7de2c8ceb6fd83913a8c0a2c0d/op-supervisor/supervisor/backend/backend.go#L479)
12// todo: rm once resolved <https://github.com/ethereum-optimism/optimism/issues/14603>
13const MINIMUM_SAFETY_MSG: &str = "does not meet the minimum safety";
14
15/// An error type for the [MessageGraph] struct.
16///
17/// [MessageGraph]: crate::MessageGraph
18#[derive(Debug, Clone, PartialEq, Eq, Error)]
19pub enum MessageGraphError<E> {
20    /// Dependency set is impossibly empty
21    #[error("Dependency set is impossibly empty")]
22    EmptyDependencySet,
23    /// Remote message not found
24    #[error("Remote message not found on chain ID {0} with message hash {1}")]
25    RemoteMessageNotFound(u64, B256),
26    /// Invalid message origin
27    #[error("Invalid message origin. Expected {0}, got {1}")]
28    InvalidMessageOrigin(Address, Address),
29    /// Invalid message payload hash
30    #[error("Invalid message hash. Expected {0}, got {1}")]
31    InvalidMessageHash(B256, B256),
32    /// Invalid message timestamp
33    #[error("Invalid message timestamp. Expected {0}, got {1}")]
34    InvalidMessageTimestamp(u64, u64),
35    /// Message is in the future
36    #[error("Message is in the future. Expected timestamp to be <= {0}, got {1}")]
37    MessageInFuture(u64, u64),
38    /// Invalid messages were found
39    #[error("Invalid messages found on chains: {0:?}")]
40    InvalidMessages(Vec<u64>),
41    /// Missing a [RollupConfig] for a chain ID
42    ///
43    /// [RollupConfig]: kona_genesis::RollupConfig
44    #[error("Missing a RollupConfig for chain ID {0}")]
45    MissingRollupConfig(u64),
46    /// Interop provider error
47    #[error("Interop provider: {0}")]
48    InteropProviderError(#[from] E),
49}
50
51/// A [Result] alias for the [MessageGraphError] type.
52#[allow(type_alias_bounds)]
53pub type MessageGraphResult<T, P: InteropProvider> =
54    core::result::Result<T, MessageGraphError<P::Error>>;
55
56/// An error type for the [SuperRoot] struct's serialization and deserialization.
57///
58/// [SuperRoot]: crate::SuperRoot
59#[derive(Debug, Clone, Error)]
60pub enum SuperRootError {
61    /// Invalid super root version byte
62    #[error("Invalid super root version byte")]
63    InvalidVersionByte,
64    /// Unexpected encoded super root length
65    #[error("Unexpected encoded super root length")]
66    UnexpectedLength,
67    /// Slice conversion error
68    #[error("Slice conversion error: {0}")]
69    SliceConversionError(#[from] core::array::TryFromSliceError),
70}
71
72/// A [Result] alias for the [SuperRootError] type.
73pub type SuperRootResult<T> = core::result::Result<T, SuperRootError>;
74
75/// Invalid [`ExecutingMessage`](crate::ExecutingMessage) error.
76#[derive(Error, Debug)]
77pub enum InvalidExecutingMessage {
78    /// Message does not meet minimum safety level
79    #[error("message does not meet min safety level, got: {got}, expected: {expected}")]
80    MinimumSafety {
81        /// Actual level of the message
82        got: SafetyLevel,
83        /// Minimum acceptable level that was passed to supervisor
84        expected: SafetyLevel,
85    },
86    /// Invalid chain
87    #[error("unsupported chain id: {0}")]
88    UnknownChain(u64),
89}
90
91impl InvalidExecutingMessage {
92    /// Parses error message. Returns `None`, if message is not recognized.
93    // todo: match on error code instead of message string once resolved <https://github.com/ethereum-optimism/optimism/issues/14603>
94    pub fn parse_err_msg(err_msg: &str) -> Option<Self> {
95        // Check if it's invalid message call, message example:
96        // `failed to check message: failed to check log: unknown chain: 14417`
97        if err_msg.contains(UNKNOWN_CHAIN_MSG) {
98            if let Ok(chain_id) =
99                err_msg.split(' ').last().expect("message contains chain id").parse::<u64>()
100            {
101                return Some(Self::UnknownChain(chain_id))
102            }
103        // Check if it's `does not meet the minimum safety` error, message example:
104        // `message {0x4200000000000000000000000000000000000023 4 1 1728507701 901}
105        // (safety level: unsafe) does not meet the minimum safety cross-unsafe"`
106        } else if err_msg.contains(MINIMUM_SAFETY_MSG) {
107            let message_safety = if err_msg.contains("safety level: safe") {
108                SafetyLevel::Safe
109            } else if err_msg.contains("safety level: local-safe") {
110                SafetyLevel::LocalSafe
111            } else if err_msg.contains("safety level: cross-unsafe") {
112                SafetyLevel::CrossUnsafe
113            } else if err_msg.contains("safety level: unsafe") {
114                SafetyLevel::Unsafe
115            } else if err_msg.contains("safety level: invalid") {
116                SafetyLevel::Invalid
117            } else {
118                // Unexpected level name
119                return None
120            };
121            let expected_safety = if err_msg.contains("safety finalized") {
122                SafetyLevel::Finalized
123            } else if err_msg.contains("safety safe") {
124                SafetyLevel::Safe
125            } else if err_msg.contains("safety local-safe") {
126                SafetyLevel::LocalSafe
127            } else if err_msg.contains("safety cross-unsafe") {
128                SafetyLevel::CrossUnsafe
129            } else if err_msg.contains("safety unsafe") {
130                SafetyLevel::Unsafe
131            } else {
132                // Unexpected level name
133                return None
134            };
135
136            return Some(Self::MinimumSafety { expected: expected_safety, got: message_safety })
137        }
138
139        None
140    }
141}
142
143#[cfg(test)]
144mod tests {
145    use super::*;
146
147    const MIN_SAFETY_CROSS_UNSAFE_ERROR: &str = "message {0x4200000000000000000000000000000000000023 4 1 1728507701 901} (safety level: unsafe) does not meet the minimum safety cross-unsafe";
148    const MIN_SAFETY_UNSAFE_ERROR: &str = "message {0x4200000000000000000000000000000000000023 1091637521 4369 0 901} (safety level: invalid) does not meet the minimum safety unsafe";
149    const MIN_SAFETY_FINALIZED_ERROR: &str = "message {0x4200000000000000000000000000000000000023 1091600001 215 1170 901} (safety level: safe) does not meet the minimum safety finalized";
150    const INVALID_CHAIN: &str =
151        "failed to check message: failed to check log: unknown chain: 14417";
152    const RANDOM_ERROR: &str = "gibberish error";
153
154    #[test]
155    fn test_op_supervisor_error_parsing() {
156        assert!(matches!(
157            InvalidExecutingMessage::parse_err_msg(MIN_SAFETY_CROSS_UNSAFE_ERROR).unwrap(),
158            InvalidExecutingMessage::MinimumSafety {
159                expected: SafetyLevel::CrossUnsafe,
160                got: SafetyLevel::Unsafe
161            }
162        ));
163
164        assert!(matches!(
165            InvalidExecutingMessage::parse_err_msg(MIN_SAFETY_UNSAFE_ERROR).unwrap(),
166            InvalidExecutingMessage::MinimumSafety {
167                expected: SafetyLevel::Unsafe,
168                got: SafetyLevel::Invalid
169            }
170        ));
171
172        assert!(matches!(
173            InvalidExecutingMessage::parse_err_msg(MIN_SAFETY_FINALIZED_ERROR).unwrap(),
174            InvalidExecutingMessage::MinimumSafety {
175                expected: SafetyLevel::Finalized,
176                got: SafetyLevel::Safe,
177            }
178        ));
179
180        assert!(matches!(
181            InvalidExecutingMessage::parse_err_msg(INVALID_CHAIN).unwrap(),
182            InvalidExecutingMessage::UnknownChain(14417)
183        ));
184
185        assert!(InvalidExecutingMessage::parse_err_msg(RANDOM_ERROR).is_none());
186    }
187}