1use crate::{SUPER_ROOT_VERSION, SuperRootError, SuperRootResult};
6use alloc::vec::Vec;
7use alloy_primitives::{B256, Bytes, U256, keccak256};
8use alloy_rlp::{Buf, BufMut};
9
10#[derive(Debug, Clone, Eq, PartialEq)]
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
13#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
14pub struct SuperRoot {
15 pub timestamp: u64,
17 pub output_roots: Vec<OutputRootWithChain>,
19}
20
21impl SuperRoot {
22 pub fn new(timestamp: u64, mut output_roots: Vec<OutputRootWithChain>) -> Self {
24 output_roots.sort_by_key(|r| r.chain_id);
26 Self { timestamp, output_roots }
27 }
28
29 pub fn decode(buf: &mut &[u8]) -> SuperRootResult<Self> {
31 if buf.is_empty() {
32 return Err(SuperRootError::UnexpectedLength);
33 }
34
35 let version = buf[0];
36 if version != SUPER_ROOT_VERSION {
37 return Err(SuperRootError::InvalidVersionByte);
38 }
39 buf.advance(1);
40
41 if buf.len() < 8 {
42 return Err(SuperRootError::UnexpectedLength);
43 }
44 let timestamp = u64::from_be_bytes(buf[0..8].try_into()?);
45 buf.advance(8);
46
47 let mut output_roots = Vec::new();
48 while !buf.is_empty() {
49 if buf.len() < 64 {
50 return Err(SuperRootError::UnexpectedLength);
51 }
52
53 let chain_id = U256::from_be_bytes::<32>(buf[0..32].try_into()?);
54 buf.advance(32);
55 let output_root = B256::from_slice(&buf[0..32]);
56 buf.advance(32);
57 output_roots.push(OutputRootWithChain::new(chain_id.to(), output_root));
58 }
59
60 Ok(Self { timestamp, output_roots })
61 }
62
63 pub fn encode(&self, out: &mut dyn BufMut) {
65 out.put_u8(SUPER_ROOT_VERSION);
66
67 out.put_u64(self.timestamp);
68 for output_root in &self.output_roots {
69 out.put_slice(U256::from(output_root.chain_id).to_be_bytes::<32>().as_slice());
70 out.put_slice(output_root.output_root.as_slice());
71 }
72 }
73
74 pub fn encoded_length(&self) -> usize {
76 1 + 8 + 64 * self.output_roots.len()
77 }
78
79 pub fn hash(&self) -> B256 {
81 let mut rlp_buf = Vec::with_capacity(self.encoded_length());
82 self.encode(&mut rlp_buf);
83 keccak256(&rlp_buf)
84 }
85}
86
87#[derive(Debug, Clone, Eq, PartialEq)]
89#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
90#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
91pub struct ChainRootInfo {
92 #[cfg_attr(feature = "serde", serde(rename = "chainID"))]
94 pub chain_id: u64,
95 pub canonical: B256,
97 pub pending: Bytes,
105}
106
107#[derive(Debug, Clone, Eq, PartialEq)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
110#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
111pub struct SuperRootResponse {
112 pub timestamp: u64,
114 pub super_root: B256,
116 pub chains: Vec<ChainRootInfo>,
119}
120
121#[derive(Debug, Clone, Eq, PartialEq, Hash)]
123#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
124#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
125pub struct OutputRootWithChain {
126 pub chain_id: u64,
128 pub output_root: B256,
130}
131
132impl OutputRootWithChain {
133 pub const fn new(chain_id: u64, output_root: B256) -> Self {
135 Self { chain_id, output_root }
136 }
137}
138
139#[cfg(test)]
140mod test {
141 use crate::{SUPER_ROOT_VERSION, errors::SuperRootError};
142
143 use super::{OutputRootWithChain, SuperRoot};
144 use alloy_primitives::{B256, b256};
145
146 #[test]
147 fn test_super_root_sorts_outputs() {
148 let super_root = SuperRoot::new(
149 10,
150 vec![
151 (OutputRootWithChain::new(3, B256::default())),
152 (OutputRootWithChain::new(2, B256::default())),
153 (OutputRootWithChain::new(1, B256::default())),
154 ],
155 );
156
157 assert!(super_root.output_roots.windows(2).all(|w| w[0].chain_id <= w[1].chain_id));
158 }
159
160 #[test]
161 fn test_super_root_empty_buf() {
162 let buf: Vec<u8> = Vec::new();
163 assert!(matches!(
164 SuperRoot::decode(&mut buf.as_slice()).unwrap_err(),
165 SuperRootError::UnexpectedLength
166 ));
167 }
168
169 #[test]
170 fn test_super_root_invalid_version() {
171 let buf = vec![0xFF];
172 assert!(matches!(
173 SuperRoot::decode(&mut buf.as_slice()).unwrap_err(),
174 SuperRootError::InvalidVersionByte
175 ));
176 }
177
178 #[test]
179 fn test_super_root_invalid_length_at_timestamp() {
180 let buf = vec![SUPER_ROOT_VERSION, 0x00];
181 assert!(matches!(
182 SuperRoot::decode(&mut buf.as_slice()).unwrap_err(),
183 SuperRootError::UnexpectedLength
184 ));
185 }
186
187 #[test]
188 fn test_super_root_invalid_length_malformed_output_roots() {
189 let buf = [&[SUPER_ROOT_VERSION], 64u64.to_be_bytes().as_ref(), &[0xbe, 0xef]].concat();
190 assert!(matches!(
191 SuperRoot::decode(&mut buf.as_slice()).unwrap_err(),
192 SuperRootError::UnexpectedLength
193 ));
194 }
195
196 #[test]
197 fn test_static_hash_super_root() {
198 const EXPECTED: B256 =
199 b256!("0980033cbf4337f614a2401ab7efbfdc66ab647812f1c98d891d92ddfb376541");
200
201 let super_root = SuperRoot::new(
202 10,
203 vec![
204 (OutputRootWithChain::new(1, B256::default())),
205 (OutputRootWithChain::new(2, B256::default())),
206 ],
207 );
208 assert_eq!(super_root.hash(), EXPECTED);
209 }
210
211 #[test]
212 fn test_static_super_root_roundtrip() {
213 let super_root = SuperRoot::new(
214 10,
215 vec![
216 (OutputRootWithChain::new(1, B256::default())),
217 (OutputRootWithChain::new(2, B256::default())),
218 ],
219 );
220
221 let mut rlp_buf = Vec::with_capacity(super_root.encoded_length());
222 super_root.encode(&mut rlp_buf);
223 assert_eq!(super_root, SuperRoot::decode(&mut rlp_buf.as_slice()).unwrap());
224 }
225
226 #[test]
227 fn test_arbitrary_super_root_roundtrip() {
228 use arbitrary::Arbitrary;
229 use rand::Rng;
230
231 let mut bytes = [0u8; 1024];
232 rand::rng().fill(bytes.as_mut_slice());
233 let super_root = SuperRoot::arbitrary(&mut arbitrary::Unstructured::new(&bytes)).unwrap();
234
235 let mut rlp_buf = Vec::with_capacity(super_root.encoded_length());
236 super_root.encode(&mut rlp_buf);
237 assert_eq!(super_root, SuperRoot::decode(&mut rlp_buf.as_slice()).unwrap());
238 }
239}