1use std::io::{self, Cursor, Read};
2
3use alloy_primitives::{Address, B256, U256};
4
5use crate::util::{
6 address_from_256_from_reader, address_from_reader, hash_from_reader, uint64_from_reader,
7 uint256_from_reader,
8};
9
10pub const L1_MESSAGE_TYPE_L2_MESSAGE: u8 = 3;
12pub const L1_MESSAGE_TYPE_END_OF_BLOCK: u8 = 6;
13pub const L1_MESSAGE_TYPE_L2_FUNDED_BY_L1: u8 = 7;
14pub const L1_MESSAGE_TYPE_ROLLUP_EVENT: u8 = 8;
15pub const L1_MESSAGE_TYPE_SUBMIT_RETRYABLE: u8 = 9;
16pub const L1_MESSAGE_TYPE_BATCH_FOR_GAS_ESTIMATION: u8 = 10;
17pub const L1_MESSAGE_TYPE_INITIALIZE: u8 = 11;
18pub const L1_MESSAGE_TYPE_ETH_DEPOSIT: u8 = 12;
19pub const L1_MESSAGE_TYPE_BATCH_POSTING_REPORT: u8 = 13;
20pub const L1_MESSAGE_TYPE_INVALID: u8 = 0xFF;
21
22pub const MAX_L2_MESSAGE_SIZE: usize = 256 * 1024;
24
25pub const DEFAULT_INITIAL_L1_BASE_FEE: u64 = 50_000_000_000; #[derive(Debug, Clone, Default)]
30pub struct L1IncomingMessageHeader {
31 pub kind: u8,
32 pub poster: Address,
33 pub block_number: u64,
34 pub timestamp: u64,
35 pub request_id: Option<B256>,
36 pub l1_base_fee: Option<U256>,
37}
38
39#[derive(Debug, Clone, Copy, Default)]
41pub struct BatchDataStats {
42 pub length: u64,
43 pub non_zeros: u64,
44}
45
46#[derive(Debug, Clone, Default)]
48pub struct L1IncomingMessage {
49 pub header: L1IncomingMessageHeader,
50 pub l2_msg: Vec<u8>,
51 pub legacy_batch_gas_cost: Option<u64>,
55 pub batch_data_stats: Option<BatchDataStats>,
56}
57
58pub fn invalid_l1_message() -> L1IncomingMessage {
60 let header = L1IncomingMessageHeader {
61 kind: L1_MESSAGE_TYPE_INVALID,
62 ..Default::default()
63 };
64 L1IncomingMessage {
65 header,
66 l2_msg: Vec::new(),
67 legacy_batch_gas_cost: None,
68 batch_data_stats: None,
69 }
70}
71
72#[derive(Debug, Clone)]
74pub struct ParsedInitMessage {
75 pub chain_id: U256,
76 pub initial_l1_base_fee: U256,
77 pub serialized_chain_config: Vec<u8>,
79}
80
81impl L1IncomingMessageHeader {
82 pub fn seq_num(&self) -> Option<u64> {
84 self.request_id.map(|id| {
85 let bytes = id.as_slice();
86 u64::from_be_bytes(bytes[24..32].try_into().unwrap_or([0; 8]))
87 })
88 }
89}
90
91impl L1IncomingMessage {
92 pub fn past_batches_required(&self) -> io::Result<Vec<u64>> {
97 if self.header.kind != L1_MESSAGE_TYPE_BATCH_POSTING_REPORT {
98 return Ok(Vec::new());
99 }
100 let fields = parse_batch_posting_report_fields(&self.l2_msg)?;
101 Ok(vec![fields.batch_number])
102 }
103
104 pub fn serialize(&self) -> Vec<u8> {
106 let mut buf = Vec::new();
107 buf.push(self.header.kind);
108 buf.extend_from_slice(B256::left_padding_from(self.header.poster.as_slice()).as_slice());
110 buf.extend_from_slice(&self.header.block_number.to_be_bytes());
112 buf.extend_from_slice(&self.header.timestamp.to_be_bytes());
114 match &self.header.request_id {
116 Some(id) => buf.extend_from_slice(id.as_slice()),
117 None => buf.extend_from_slice(&[0u8; 32]),
118 }
119 match &self.header.l1_base_fee {
121 Some(fee) => buf.extend_from_slice(&fee.to_be_bytes::<32>()),
122 None => buf.extend_from_slice(&[0u8; 32]),
123 }
124 buf.extend_from_slice(&self.l2_msg);
126 buf
127 }
128}
129
130pub fn parse_incoming_l1_message(data: &[u8]) -> io::Result<L1IncomingMessage> {
132 if data.is_empty() {
133 return Err(io::Error::new(io::ErrorKind::InvalidData, "empty message"));
134 }
135 let mut reader = Cursor::new(data);
136
137 let mut kind_buf = [0u8; 1];
138 reader.read_exact(&mut kind_buf)?;
139 let kind = kind_buf[0];
140
141 let poster = address_from_256_from_reader(&mut reader)?;
142 let block_number = uint64_from_reader(&mut reader)?;
143 let timestamp = uint64_from_reader(&mut reader)?;
144 let request_id = hash_from_reader(&mut reader)?;
145 let l1_base_fee = uint256_from_reader(&mut reader)?;
146
147 let request_id = if request_id == B256::ZERO {
148 None
149 } else {
150 Some(request_id)
151 };
152 let l1_base_fee = if l1_base_fee == U256::ZERO {
153 None
154 } else {
155 Some(l1_base_fee)
156 };
157
158 let mut l2_msg = Vec::new();
159 reader.read_to_end(&mut l2_msg)?;
160
161 Ok(L1IncomingMessage {
162 header: L1IncomingMessageHeader {
163 kind,
164 poster,
165 block_number,
166 timestamp,
167 request_id,
168 l1_base_fee,
169 },
170 l2_msg,
171 legacy_batch_gas_cost: None,
172 batch_data_stats: None,
173 })
174}
175
176pub fn parse_init_message(data: &[u8]) -> io::Result<ParsedInitMessage> {
184 let default_base_fee = U256::from(DEFAULT_INITIAL_L1_BASE_FEE);
185
186 if data.len() == 32 {
187 return Ok(ParsedInitMessage {
188 chain_id: U256::from_be_slice(data),
189 initial_l1_base_fee: default_base_fee,
190 serialized_chain_config: Vec::new(),
191 });
192 }
193 if data.len() < 33 {
194 return Err(io::Error::new(
195 io::ErrorKind::InvalidData,
196 format!("invalid init message length: {}", data.len()),
197 ));
198 }
199
200 let chain_id = U256::from_be_slice(&data[..32]);
201 let version = data[32];
202 let mut reader = Cursor::new(&data[33..]);
203
204 match version {
205 0 => {
206 let mut serialized_chain_config = Vec::new();
207 reader.read_to_end(&mut serialized_chain_config)?;
208 Ok(ParsedInitMessage {
209 chain_id,
210 initial_l1_base_fee: default_base_fee,
211 serialized_chain_config,
212 })
213 }
214 1 => {
215 let initial_l1_base_fee = uint256_from_reader(&mut reader)?;
216 let mut serialized_chain_config = Vec::new();
217 reader.read_to_end(&mut serialized_chain_config)?;
218 Ok(ParsedInitMessage {
219 chain_id,
220 initial_l1_base_fee,
221 serialized_chain_config,
222 })
223 }
224 _ => Err(io::Error::new(
225 io::ErrorKind::InvalidData,
226 format!("unsupported init message version: {version}"),
227 )),
228 }
229}
230
231pub fn get_data_stats(data: &[u8]) -> BatchDataStats {
233 let non_zeros = data.iter().filter(|&&b| b != 0).count() as u64;
234 BatchDataStats {
235 length: data.len() as u64,
236 non_zeros,
237 }
238}
239
240pub fn legacy_cost_for_stats(stats: &BatchDataStats) -> u64 {
242 let zeros = stats.length.saturating_sub(stats.non_zeros);
243 let mut gas = zeros * 4 + stats.non_zeros * 16;
245 let keccak_words = stats.length.div_ceil(32);
247 gas += 30 + keccak_words * 6; gas += 2 * 20_000; gas
250}
251
252pub fn parse_batch_posting_report_fields(data: &[u8]) -> io::Result<BatchPostingReportFields> {
254 let mut reader = Cursor::new(data);
255
256 let batch_timestamp_u256 = uint256_from_reader(&mut reader)?;
257 let batch_timestamp: u64 = batch_timestamp_u256
258 .try_into()
259 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch timestamp too large"))?;
260
261 let batch_poster = address_from_reader(&mut reader)?;
262 let data_hash = hash_from_reader(&mut reader)?;
263
264 let batch_number_u256 = uint256_from_reader(&mut reader)?;
265 let batch_number: u64 = batch_number_u256
266 .try_into()
267 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch number too large"))?;
268
269 let l1_base_fee_estimate = uint256_from_reader(&mut reader)?;
270
271 let extra_gas = match uint64_from_reader(&mut reader) {
272 Ok(v) => v,
273 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => 0,
274 Err(e) => return Err(e),
275 };
276
277 Ok(BatchPostingReportFields {
278 batch_timestamp,
279 batch_poster,
280 data_hash,
281 batch_number,
282 l1_base_fee_estimate,
283 extra_gas,
284 })
285}
286
287#[derive(Debug, Clone)]
289pub struct BatchPostingReportFields {
290 pub batch_timestamp: u64,
291 pub batch_poster: Address,
292 pub data_hash: B256,
293 pub batch_number: u64,
294 pub l1_base_fee_estimate: U256,
295 pub extra_gas: u64,
296}