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