1use crate::{InteropProvider, SafetyLevel};
4use alloc::vec::Vec;
5use alloy_primitives::{Address, B256};
6use thiserror::Error;
7
8const UNKNOWN_CHAIN_MSG: &str = "unknown chain: ";
11const MINIMUM_SAFETY_MSG: &str = "does not meet the minimum safety";
14
15#[derive(Debug, Clone, PartialEq, Eq, Error)]
19pub enum MessageGraphError<E> {
20 #[error("Dependency set is impossibly empty")]
22 EmptyDependencySet,
23 #[error("Remote message not found on chain ID {0} with message hash {1}")]
25 RemoteMessageNotFound(u64, B256),
26 #[error("Invalid message origin. Expected {0}, got {1}")]
28 InvalidMessageOrigin(Address, Address),
29 #[error("Invalid message hash. Expected {0}, got {1}")]
31 InvalidMessageHash(B256, B256),
32 #[error("Invalid message timestamp. Expected {0}, got {1}")]
34 InvalidMessageTimestamp(u64, u64),
35 #[error("Message is in the future. Expected timestamp to be <= {0}, got {1}")]
37 MessageInFuture(u64, u64),
38 #[error("Invalid messages found on chains: {0:?}")]
40 InvalidMessages(Vec<u64>),
41 #[error("Missing a RollupConfig for chain ID {0}")]
45 MissingRollupConfig(u64),
46 #[error("Interop provider: {0}")]
48 InteropProviderError(#[from] E),
49}
50
51#[allow(type_alias_bounds)]
53pub type MessageGraphResult<T, P: InteropProvider> =
54 core::result::Result<T, MessageGraphError<P::Error>>;
55
56#[derive(Debug, Clone, Error)]
60pub enum SuperRootError {
61 #[error("Invalid super root version byte")]
63 InvalidVersionByte,
64 #[error("Unexpected encoded super root length")]
66 UnexpectedLength,
67 #[error("Slice conversion error: {0}")]
69 SliceConversionError(#[from] core::array::TryFromSliceError),
70}
71
72pub type SuperRootResult<T> = core::result::Result<T, SuperRootError>;
74
75#[derive(Error, Debug)]
77pub enum InvalidExecutingMessage {
78 #[error("message does not meet min safety level, got: {got}, expected: {expected}")]
80 MinimumSafety {
81 got: SafetyLevel,
83 expected: SafetyLevel,
85 },
86 #[error("unsupported chain id: {0}")]
88 UnknownChain(u64),
89}
90
91impl InvalidExecutingMessage {
92 pub fn parse_err_msg(err_msg: &str) -> Option<Self> {
95 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 } 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 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 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}