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> {
159 if data.is_empty() {
160 return Ok(ParsedInitMessage {
161 chain_id: U256::ZERO,
162 initial_l1_base_fee: U256::from(DEFAULT_INITIAL_L1_BASE_FEE),
163 serialized_chain_config: Vec::new(),
164 });
165 }
166
167 let mut reader = Cursor::new(data);
168
169 let mut version_buf = [0u8; 1];
171 reader.read_exact(&mut version_buf)?;
172 let version = version_buf[0];
173
174 match version {
175 0 => {
176 let chain_id = uint256_from_reader(&mut reader)?;
177 let mut serialized_chain_config = Vec::new();
178 reader.read_to_end(&mut serialized_chain_config)?;
179 Ok(ParsedInitMessage {
180 chain_id,
181 initial_l1_base_fee: U256::from(DEFAULT_INITIAL_L1_BASE_FEE),
182 serialized_chain_config,
183 })
184 }
185 1 => {
186 let chain_id = uint256_from_reader(&mut reader)?;
187 let initial_l1_base_fee = uint256_from_reader(&mut reader)?;
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,
193 serialized_chain_config,
194 })
195 }
196 _ => Err(io::Error::new(
197 io::ErrorKind::InvalidData,
198 format!("unsupported init message version: {version}"),
199 )),
200 }
201}
202
203pub fn get_data_stats(data: &[u8]) -> BatchDataStats {
205 let non_zeros = data.iter().filter(|&&b| b != 0).count() as u64;
206 BatchDataStats {
207 length: data.len() as u64,
208 non_zeros,
209 }
210}
211
212pub fn legacy_cost_for_stats(stats: &BatchDataStats) -> u64 {
214 let zeros = stats.length.saturating_sub(stats.non_zeros);
215 let mut gas = zeros * 4 + stats.non_zeros * 16;
217 let keccak_words = stats.length.div_ceil(32);
219 gas += 30 + keccak_words * 6; gas += 2 * 20_000; gas
222}
223
224pub fn parse_batch_posting_report_fields(data: &[u8]) -> io::Result<BatchPostingReportFields> {
226 let mut reader = Cursor::new(data);
227
228 let batch_timestamp_u256 = uint256_from_reader(&mut reader)?;
229 let batch_timestamp: u64 = batch_timestamp_u256
230 .try_into()
231 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch timestamp too large"))?;
232
233 let batch_poster = address_from_reader(&mut reader)?;
234 let data_hash = hash_from_reader(&mut reader)?;
235
236 let batch_number_u256 = uint256_from_reader(&mut reader)?;
237 let batch_number: u64 = batch_number_u256
238 .try_into()
239 .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "batch number too large"))?;
240
241 let l1_base_fee_estimate = uint256_from_reader(&mut reader)?;
242
243 let extra_gas = match uint64_from_reader(&mut reader) {
244 Ok(v) => v,
245 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => 0,
246 Err(e) => return Err(e),
247 };
248
249 Ok(BatchPostingReportFields {
250 batch_timestamp,
251 batch_poster,
252 data_hash,
253 batch_number,
254 l1_base_fee_estimate,
255 extra_gas,
256 })
257}
258
259#[derive(Debug, Clone)]
261pub struct BatchPostingReportFields {
262 pub batch_timestamp: u64,
263 pub batch_poster: Address,
264 pub data_hash: B256,
265 pub batch_number: u64,
266 pub l1_base_fee_estimate: U256,
267 pub extra_gas: u64,
268}