1use std::io::{self, Cursor, Read};
2
3use alloy_primitives::{Address, B256, Bytes, U256};
4use alloy_rlp::{
5 Decodable, Encodable, RlpDecodable, RlpEncodable, bytes::BufMut, length_of_length,
6};
7
8use crate::{
9 rlp::NilList,
10 serialization::{
11 address_from_256_from_reader, address_from_reader, hash_from_reader, uint64_from_reader,
12 uint256_from_reader,
13 },
14};
15
16pub const L1_MESSAGE_TYPE_L2_MESSAGE: u8 = 3;
18pub const L1_MESSAGE_TYPE_END_OF_BLOCK: u8 = 6;
19pub const L1_MESSAGE_TYPE_L2_FUNDED_BY_L1: u8 = 7;
20pub const L1_MESSAGE_TYPE_ROLLUP_EVENT: u8 = 8;
21pub const L1_MESSAGE_TYPE_SUBMIT_RETRYABLE: u8 = 9;
22pub const L1_MESSAGE_TYPE_BATCH_FOR_GAS_ESTIMATION: u8 = 10;
23pub const L1_MESSAGE_TYPE_INITIALIZE: u8 = 11;
24pub const L1_MESSAGE_TYPE_ETH_DEPOSIT: u8 = 12;
25pub const L1_MESSAGE_TYPE_BATCH_POSTING_REPORT: u8 = 13;
26pub const L1_MESSAGE_TYPE_INVALID: u8 = 0xFF;
27
28pub const MAX_L2_MESSAGE_SIZE: usize = 256 * 1024;
30
31pub const DEFAULT_INITIAL_L1_BASE_FEE: u64 = 50_000_000_000; #[derive(Debug, Clone, Default)]
36pub struct L1IncomingMessageHeader {
37 pub kind: u8,
38 pub poster: Address,
39 pub block_number: u64,
40 pub timestamp: u64,
41 pub request_id: Option<B256>,
42 pub l1_base_fee: Option<U256>,
43}
44
45impl L1IncomingMessageHeader {
46 fn rlp_payload_length(&self) -> usize {
47 self.kind.length()
48 + self.poster.length()
49 + self.block_number.length()
50 + self.timestamp.length()
51 + NilList(self.request_id).length()
52 + self.l1_base_fee.unwrap_or_default().length()
53 }
54}
55
56impl Encodable for L1IncomingMessageHeader {
57 fn encode(&self, out: &mut dyn BufMut) {
58 let payload_length = self.rlp_payload_length();
59 alloy_rlp::Header {
60 list: true,
61 payload_length,
62 }
63 .encode(out);
64 self.kind.encode(out);
65 self.poster.encode(out);
66 self.block_number.encode(out);
67 self.timestamp.encode(out);
68 NilList(self.request_id).encode(out);
69 self.l1_base_fee.unwrap_or_default().encode(out);
70 }
71
72 fn length(&self) -> usize {
73 let payload_length = self.rlp_payload_length();
74 length_of_length(payload_length) + payload_length
75 }
76}
77
78impl Decodable for L1IncomingMessageHeader {
79 fn decode(buf: &mut &[u8]) -> alloy_rlp::Result<Self> {
80 let header = alloy_rlp::Header::decode(buf)?;
81 if !header.list {
82 return Err(alloy_rlp::Error::UnexpectedString);
83 }
84 let started_len = buf.len();
85
86 let kind = u8::decode(buf)?;
87 let poster = Address::decode(buf)?;
88 let block_number = u64::decode(buf)?;
89 let timestamp = u64::decode(buf)?;
90 let request_id = NilList::<B256>::decode(buf)?.0;
91 let l1_base_fee = {
92 let value = U256::decode(buf)?;
93 (!value.is_zero()).then_some(value)
94 };
95
96 let consumed = started_len - buf.len();
97 if consumed != header.payload_length {
98 return Err(alloy_rlp::Error::ListLengthMismatch {
99 expected: header.payload_length,
100 got: consumed,
101 });
102 }
103
104 Ok(Self {
105 kind,
106 poster,
107 block_number,
108 timestamp,
109 request_id,
110 l1_base_fee,
111 })
112 }
113}
114
115#[derive(Debug, Clone, Copy, Default, RlpEncodable, RlpDecodable)]
117pub struct BatchDataStats {
118 pub length: u64,
119 pub non_zeros: u64,
120}
121
122#[derive(Debug, Clone, Default, RlpEncodable, RlpDecodable)]
124#[rlp(trailing)]
125pub struct L1IncomingMessage {
126 pub header: L1IncomingMessageHeader,
127 pub l2_msg: Bytes,
128 pub legacy_batch_gas_cost: Option<u64>,
132 pub batch_data_stats: Option<BatchDataStats>,
133}
134
135pub fn invalid_l1_message() -> L1IncomingMessage {
137 let header = L1IncomingMessageHeader {
138 kind: L1_MESSAGE_TYPE_INVALID,
139 ..Default::default()
140 };
141 L1IncomingMessage {
142 header,
143 l2_msg: Bytes::new(),
144 legacy_batch_gas_cost: None,
145 batch_data_stats: None,
146 }
147}
148
149#[derive(Debug, Clone)]
151pub struct ParsedInitMessage {
152 pub chain_id: U256,
153 pub initial_l1_base_fee: U256,
154 pub serialized_chain_config: Vec<u8>,
156}
157
158impl L1IncomingMessageHeader {
159 pub fn seq_num(&self) -> Option<u64> {
161 self.request_id.map(|id| {
162 let bytes = id.as_slice();
163 u64::from_be_bytes(bytes[24..32].try_into().unwrap_or([0; 8]))
164 })
165 }
166}
167
168impl L1IncomingMessage {
169 pub fn past_batches_required(&self) -> io::Result<Vec<u64>> {
174 if self.header.kind != L1_MESSAGE_TYPE_BATCH_POSTING_REPORT {
175 return Ok(Vec::new());
176 }
177 let fields = parse_batch_posting_report_fields(&self.l2_msg)?;
178 Ok(vec![fields.batch_number])
179 }
180
181 pub fn serialize(&self) -> Vec<u8> {
183 let mut buf = Vec::new();
184 buf.push(self.header.kind);
185 buf.extend_from_slice(B256::left_padding_from(self.header.poster.as_slice()).as_slice());
187 buf.extend_from_slice(&self.header.block_number.to_be_bytes());
189 buf.extend_from_slice(&self.header.timestamp.to_be_bytes());
191 match &self.header.request_id {
193 Some(id) => buf.extend_from_slice(id.as_slice()),
194 None => buf.extend_from_slice(&[0u8; 32]),
195 }
196 match &self.header.l1_base_fee {
198 Some(fee) => buf.extend_from_slice(&fee.to_be_bytes::<32>()),
199 None => buf.extend_from_slice(&[0u8; 32]),
200 }
201 buf.extend_from_slice(&self.l2_msg);
203 buf
204 }
205}
206
207pub fn parse_incoming_l1_message(data: &[u8]) -> io::Result<L1IncomingMessage> {
209 if data.is_empty() {
210 return Err(io::Error::new(io::ErrorKind::InvalidData, "empty message"));
211 }
212 let mut reader = Cursor::new(data);
213
214 let mut kind_buf = [0u8; 1];
215 reader.read_exact(&mut kind_buf)?;
216 let kind = kind_buf[0];
217
218 let poster = address_from_256_from_reader(&mut reader)?;
219 let block_number = uint64_from_reader(&mut reader)?;
220 let timestamp = uint64_from_reader(&mut reader)?;
221 let request_id = Some(hash_from_reader(&mut reader)?);
225 let l1_base_fee = Some(uint256_from_reader(&mut reader)?);
226
227 let mut l2_msg = Vec::new();
228 reader.read_to_end(&mut l2_msg)?;
229
230 Ok(L1IncomingMessage {
231 header: L1IncomingMessageHeader {
232 kind,
233 poster,
234 block_number,
235 timestamp,
236 request_id,
237 l1_base_fee,
238 },
239 l2_msg: l2_msg.into(),
240 legacy_batch_gas_cost: None,
241 batch_data_stats: None,
242 })
243}
244
245pub fn parse_init_message(data: &[u8]) -> io::Result<ParsedInitMessage> {
253 let default_base_fee = U256::from(DEFAULT_INITIAL_L1_BASE_FEE);
254
255 if data.len() == 32 {
256 return Ok(ParsedInitMessage {
257 chain_id: U256::from_be_slice(data),
258 initial_l1_base_fee: default_base_fee,
259 serialized_chain_config: Vec::new(),
260 });
261 }
262 if data.len() < 33 {
263 return Err(io::Error::new(
264 io::ErrorKind::InvalidData,
265 format!("invalid init message length: {}", data.len()),
266 ));
267 }
268
269 let chain_id = U256::from_be_slice(&data[..32]);
270 let version = data[32];
271 let mut reader = Cursor::new(&data[33..]);
272
273 match version {
274 0 => {
275 let mut serialized_chain_config = Vec::new();
276 reader.read_to_end(&mut serialized_chain_config)?;
277 Ok(ParsedInitMessage {
278 chain_id,
279 initial_l1_base_fee: default_base_fee,
280 serialized_chain_config,
281 })
282 }
283 1 => {
284 let initial_l1_base_fee = uint256_from_reader(&mut reader)?;
285 let mut serialized_chain_config = Vec::new();
286 reader.read_to_end(&mut serialized_chain_config)?;
287 Ok(ParsedInitMessage {
288 chain_id,
289 initial_l1_base_fee,
290 serialized_chain_config,
291 })
292 }
293 _ => Err(io::Error::new(
294 io::ErrorKind::InvalidData,
295 format!("unsupported init message version: {version}"),
296 )),
297 }
298}
299
300pub fn get_data_stats(data: &[u8]) -> BatchDataStats {
302 let non_zeros = data.iter().filter(|&&b| b != 0).count() as u64;
303 BatchDataStats {
304 length: data.len() as u64,
305 non_zeros,
306 }
307}
308
309pub fn legacy_cost_for_stats(stats: &BatchDataStats) -> u64 {
311 let zeros = stats.length.saturating_sub(stats.non_zeros);
312 let mut gas = zeros * 4 + stats.non_zeros * 16;
314 let keccak_words = stats.length.div_ceil(32);
316 gas += 30 + keccak_words * 6; gas += 2 * 20_000; gas
319}
320
321pub fn parse_batch_posting_report_fields(data: &[u8]) -> io::Result<BatchPostingReportFields> {
323 let mut reader = Cursor::new(data);
324
325 let batch_timestamp_u256 = uint256_from_reader(&mut reader)?;
326 let batch_timestamp: u64 = batch_timestamp_u256
327 .try_into()
328 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch timestamp too large"))?;
329
330 let batch_poster = address_from_reader(&mut reader)?;
331 let data_hash = hash_from_reader(&mut reader)?;
332
333 let batch_number_u256 = uint256_from_reader(&mut reader)?;
334 let batch_number: u64 = batch_number_u256
335 .try_into()
336 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch number too large"))?;
337
338 let l1_base_fee_estimate = uint256_from_reader(&mut reader)?;
339
340 let extra_gas = match uint64_from_reader(&mut reader) {
341 Ok(v) => v,
342 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => 0,
343 Err(e) => return Err(e),
344 };
345
346 Ok(BatchPostingReportFields {
347 batch_timestamp,
348 batch_poster,
349 data_hash,
350 batch_number,
351 l1_base_fee_estimate,
352 extra_gas,
353 })
354}
355
356#[derive(Debug, Clone)]
358pub struct BatchPostingReportFields {
359 pub batch_timestamp: u64,
360 pub batch_poster: Address,
361 pub data_hash: B256,
362 pub batch_number: u64,
363 pub l1_base_fee_estimate: U256,
364 pub extra_gas: u64,
365}