1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{keccak256, Address, Log, B256, U256};
3use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
4
5use std::{cell::RefCell, collections::HashMap, sync::Mutex};
6
7use crate::storage_slot::{
8 derive_subspace_key, map_slot, root_slot, ARBOS_STATE_ADDRESS, NATIVE_TOKEN_SUBSPACE,
9 ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE,
10};
11
12pub const ARBSYS_ADDRESS: Address = Address::new([
14 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
15 0x00, 0x00, 0x00, 0x64,
16]);
17
18const WITHDRAW_ETH: [u8; 4] = [0x25, 0xe1, 0x60, 0x63]; const SEND_TX_TO_L1: [u8; 4] = [0x92, 0x8c, 0x16, 0x9a]; const ARB_BLOCK_NUMBER: [u8; 4] = [0xa3, 0xb1, 0xb3, 0x1d]; const ARB_BLOCK_HASH: [u8; 4] = [0x2b, 0x40, 0x7a, 0x82]; const ARB_CHAIN_ID: [u8; 4] = [0xd1, 0x27, 0xf5, 0x4a]; const ARB_OS_VERSION: [u8; 4] = [0x05, 0x10, 0x38, 0xf2]; const GET_STORAGE_GAS_AVAILABLE: [u8; 4] = [0xa9, 0x45, 0x97, 0xff]; const IS_TOP_LEVEL_CALL: [u8; 4] = [0x08, 0xbd, 0x62, 0x4c]; const MAP_L1_SENDER: [u8; 4] = [0x4d, 0xbb, 0xd5, 0x06]; const WAS_ALIASED: [u8; 4] = [0x17, 0x5a, 0x26, 0x0b]; const CALLER_WITHOUT_ALIAS: [u8; 4] = [0xd7, 0x45, 0x23, 0xb3]; const SEND_MERKLE_TREE_STATE: [u8; 4] = [0x7a, 0xee, 0xcd, 0x2a]; const L1_ALIAS_OFFSET: Address = Address::new([
34 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
35 0x00, 0x00, 0x11, 0x11,
36]);
37
38const COPY_GAS: u64 = 3; const LOG_GAS: u64 = 375;
43const LOG_TOPIC_GAS: u64 = 375;
44const LOG_DATA_GAS: u64 = 8; const STORAGE_READ_COST: u64 = 800; const STORAGE_WRITE_COST: u64 = 20_000; const STORAGE_WRITE_ZERO_COST: u64 = 5_000; fn storage_write_cost(value: U256) -> u64 {
52 if value.is_zero() {
53 STORAGE_WRITE_ZERO_COST
54 } else {
55 STORAGE_WRITE_COST
56 }
57}
58
59fn words_for_bytes(n: u64) -> u64 {
60 n.div_ceil(32)
61}
62
63fn keccak_gas(byte_count: u64) -> u64 {
65 30 + 6 * words_for_bytes(byte_count)
66}
67
68fn l2_to_l1_tx_topic() -> B256 {
70 keccak256(b"L2ToL1Tx(address,address,uint256,uint256,uint256,uint256,uint256,uint256,bytes)")
71}
72
73fn send_merkle_update_topic() -> B256 {
74 keccak256(b"SendMerkleUpdate(uint256,bytes32,uint256)")
75}
76
77#[derive(Debug, Clone, Default)]
79pub struct ArbSysMerkleState {
80 pub new_size: u64,
81 pub partials: Vec<(u64, B256)>,
82 pub send_hash: B256,
83 pub leaf_num: u64,
84 pub value_to_burn: U256,
85 pub block_number: u64,
86}
87
88thread_local! {
89 static ARBSYS_STATE: RefCell<Option<ArbSysMerkleState>> = const { RefCell::new(None) };
90 static TX_IS_ALIASED: RefCell<bool> = const { RefCell::new(false) };
93}
94
95static L1_BLOCK_CACHE: Mutex<Option<HashMap<u64, u64>>> = Mutex::new(None);
96static CURRENT_L2_BLOCK: Mutex<u64> = Mutex::new(0);
97
98pub fn store_arbsys_state(state: ArbSysMerkleState) {
100 ARBSYS_STATE.with(|cell| *cell.borrow_mut() = Some(state));
101}
102
103pub fn take_arbsys_state() -> Option<ArbSysMerkleState> {
105 ARBSYS_STATE.with(|cell| cell.borrow_mut().take())
106}
107
108pub fn set_tx_is_aliased(aliased: bool) {
110 TX_IS_ALIASED.with(|cell| *cell.borrow_mut() = aliased);
111}
112
113pub fn get_tx_is_aliased() -> bool {
115 TX_IS_ALIASED.with(|cell| *cell.borrow())
116}
117
118pub fn set_cached_l1_block_number(l2_block: u64, l1_block: u64) {
120 let mut cache = L1_BLOCK_CACHE.lock().expect("L1 block cache lock poisoned");
121 let map = cache.get_or_insert_with(HashMap::new);
122 map.insert(l2_block, l1_block);
123 if l2_block > 100 {
124 map.retain(|&k, _| k >= l2_block - 100);
125 }
126}
127
128pub fn get_cached_l1_block_number(l2_block: u64) -> Option<u64> {
130 let cache = L1_BLOCK_CACHE.lock().expect("L1 block cache lock poisoned");
131 cache.as_ref().and_then(|m| m.get(&l2_block).copied())
132}
133
134pub fn set_current_l2_block(l2_block: u64) {
138 *CURRENT_L2_BLOCK.lock().expect("L2 block lock poisoned") = l2_block;
139}
140
141pub fn get_current_l2_block() -> u64 {
143 *CURRENT_L2_BLOCK.lock().expect("L2 block lock poisoned")
144}
145
146pub fn create_arbsys_precompile() -> DynPrecompile {
147 DynPrecompile::new_stateful(PrecompileId::custom("arbsys"), handler)
148}
149
150fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
151 let gas_limit = input.gas;
152 let data = input.data;
153 if data.len() < 4 {
154 return Err(PrecompileError::other("input too short"));
155 }
156
157 let selector: [u8; 4] = [data[0], data[1], data[2], data[3]];
158
159 let result = match selector {
160 ARB_BLOCK_NUMBER => handle_arb_block_number(&mut input),
161 ARB_BLOCK_HASH => handle_arb_block_hash(&mut input),
162 ARB_CHAIN_ID => handle_arb_chain_id(&mut input),
163 ARB_OS_VERSION => handle_arbos_version(&mut input),
164 IS_TOP_LEVEL_CALL => handle_is_top_level_call(&mut input),
165 WAS_ALIASED => handle_was_aliased(&mut input),
166 CALLER_WITHOUT_ALIAS => handle_caller_without_alias(&mut input),
167 MAP_L1_SENDER => handle_map_l1_sender(&mut input),
168 GET_STORAGE_GAS_AVAILABLE => handle_get_storage_gas(&mut input),
169 WITHDRAW_ETH => handle_withdraw_eth(&mut input),
170 SEND_TX_TO_L1 => handle_send_tx_to_l1(&mut input),
171 SEND_MERKLE_TREE_STATE => handle_send_merkle_tree_state(&mut input),
172 _ => Err(PrecompileError::other("unknown ArbSys selector")),
173 };
174 crate::gas_check(gas_limit, result)
175}
176
177fn handle_arb_block_number(input: &mut PrecompileInput<'_>) -> PrecompileResult {
180 let block_num = U256::from(get_current_l2_block());
181 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
182 let result_cost = COPY_GAS * words_for_bytes(32);
183 Ok(PrecompileOutput::new(
184 STORAGE_READ_COST + args_cost + result_cost,
185 block_num.to_be_bytes::<32>().to_vec().into(),
186 ))
187}
188
189fn handle_arb_block_hash(input: &mut PrecompileInput<'_>) -> PrecompileResult {
190 let data = input.data;
191 if data.len() < 4 + 32 {
192 return Err(PrecompileError::other("input too short"));
193 }
194
195 let requested: u64 = U256::from_be_slice(&data[4..36])
196 .try_into()
197 .unwrap_or(u64::MAX);
198 let current = get_current_l2_block();
199
200 if requested >= current || requested + 256 < current {
202 return Err(PrecompileError::other("invalid block number"));
203 }
204
205 let hash = crate::get_l2_block_hash(requested).unwrap_or(alloy_primitives::B256::ZERO);
210
211 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
212 let result_cost = COPY_GAS * words_for_bytes(32);
213 Ok(PrecompileOutput::new(
214 STORAGE_READ_COST + args_cost + result_cost,
215 hash.0.to_vec().into(),
216 ))
217}
218
219fn handle_arb_chain_id(input: &mut PrecompileInput<'_>) -> PrecompileResult {
220 let chain_id = input.internals().chain_id();
221 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
222 let result_cost = COPY_GAS * words_for_bytes(32);
223 Ok(PrecompileOutput::new(
224 STORAGE_READ_COST + args_cost + result_cost,
225 U256::from(chain_id).to_be_bytes::<32>().to_vec().into(),
226 ))
227}
228
229fn handle_arbos_version(input: &mut PrecompileInput<'_>) -> PrecompileResult {
230 let internals = input.internals_mut();
231
232 internals
233 .load_account(ARBOS_STATE_ADDRESS)
234 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
235
236 let raw_version = internals
239 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
240 .map_err(|_| PrecompileError::other("sload failed"))?;
241 let version = raw_version.data + U256::from(55);
242
243 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
244 let result_cost = COPY_GAS * words_for_bytes(32);
245 Ok(PrecompileOutput::new(
246 STORAGE_READ_COST + args_cost + result_cost,
247 version.to_be_bytes::<32>().to_vec().into(),
248 ))
249}
250
251fn handle_is_top_level_call(input: &mut PrecompileInput<'_>) -> PrecompileResult {
252 let depth = crate::get_evm_depth();
255 let is_top = depth <= 2;
256 let val = if is_top { U256::from(1) } else { U256::ZERO };
257 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
258 let result_cost = COPY_GAS * words_for_bytes(32);
259 Ok(PrecompileOutput::new(
260 STORAGE_READ_COST + args_cost + result_cost,
261 val.to_be_bytes::<32>().to_vec().into(),
262 ))
263}
264
265fn handle_was_aliased(input: &mut PrecompileInput<'_>) -> PrecompileResult {
266 let tx_origin = input.internals().tx_origin();
267 let caller = input.caller;
268
269 let internals = input.internals_mut();
271 internals
272 .load_account(ARBOS_STATE_ADDRESS)
273 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
274 let raw_version = internals
275 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
276 .map_err(|_| PrecompileError::other("sload failed"))?
277 .data;
278 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
279
280 let depth = crate::get_evm_depth();
284 let is_top_level = if arbos_version < 6 {
285 depth == 2
286 } else {
287 depth <= 2 || tx_origin == caller
288 };
289
290 let aliased = is_top_level && get_tx_is_aliased();
291 let val = if aliased { U256::from(1) } else { U256::ZERO };
292 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
293 let result_cost = COPY_GAS * words_for_bytes(32);
294 Ok(PrecompileOutput::new(
295 STORAGE_READ_COST + args_cost + result_cost,
296 val.to_be_bytes::<32>().to_vec().into(),
297 ))
298}
299
300fn handle_caller_without_alias(input: &mut PrecompileInput<'_>) -> PrecompileResult {
301 let tx_origin = input.internals().tx_origin();
306 let address = tx_origin;
307
308 let result_addr = if get_tx_is_aliased() {
309 undo_l1_alias(address)
310 } else {
311 address
312 };
313
314 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
315 let result_cost = COPY_GAS * words_for_bytes(32);
316 let mut out = [0u8; 32];
317 out[12..32].copy_from_slice(result_addr.as_slice());
318 Ok(PrecompileOutput::new(
319 STORAGE_READ_COST + args_cost + result_cost,
320 out.to_vec().into(),
321 ))
322}
323
324fn handle_map_l1_sender(input: &mut PrecompileInput<'_>) -> PrecompileResult {
325 let data = input.data;
326 if data.len() < 4 + 64 {
327 return Err(PrecompileError::other("input too short"));
328 }
329 let l1_addr = Address::from_slice(&data[16..36]);
331 let aliased = apply_l1_alias(l1_addr);
332 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
333 let result_cost = COPY_GAS * words_for_bytes(32);
334 let mut out = [0u8; 32];
335 out[12..32].copy_from_slice(aliased.as_slice());
336 Ok(PrecompileOutput::new(
337 args_cost + result_cost,
338 out.to_vec().into(),
339 ))
340}
341
342fn handle_get_storage_gas(input: &mut PrecompileInput<'_>) -> PrecompileResult {
343 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
345 let result_cost = COPY_GAS * words_for_bytes(32);
346 Ok(PrecompileOutput::new(
347 STORAGE_READ_COST + args_cost + result_cost,
348 U256::ZERO.to_be_bytes::<32>().to_vec().into(),
349 ))
350}
351
352fn handle_withdraw_eth(input: &mut PrecompileInput<'_>) -> PrecompileResult {
355 if input.is_static {
356 return Err(PrecompileError::other(
357 "cannot call withdrawEth in static context",
358 ));
359 }
360
361 let data = input.data;
362 if data.len() < 4 + 32 {
363 return Err(PrecompileError::other("input too short"));
364 }
365
366 let destination = Address::from_slice(&data[16..36]);
367 do_send_tx_to_l1(input, destination, &[])
369}
370
371fn handle_send_tx_to_l1(input: &mut PrecompileInput<'_>) -> PrecompileResult {
372 if input.is_static {
373 return Err(PrecompileError::other(
374 "cannot call sendTxToL1 in static context",
375 ));
376 }
377
378 let data = input.data;
379 if data.len() < 4 + 64 {
380 return Err(PrecompileError::other("input too short"));
381 }
382
383 let destination = Address::from_slice(&data[16..36]);
385
386 let offset = U256::from_be_slice(&data[36..68])
388 .try_into()
389 .unwrap_or(0usize);
390 let abs_offset = 4 + offset;
391 if abs_offset + 32 > data.len() {
392 return Err(PrecompileError::other("calldata offset out of bounds"));
393 }
394 let length: usize = U256::from_be_slice(&data[abs_offset..abs_offset + 32])
395 .try_into()
396 .unwrap_or(0);
397 let calldata_start = abs_offset + 32;
398 let calldata_end = calldata_start + length;
399 if calldata_end > data.len() {
400 return Err(PrecompileError::other("calldata length out of bounds"));
401 }
402 let calldata = &data[calldata_start..calldata_end];
403
404 do_send_tx_to_l1(input, destination, calldata)
405}
406
407fn do_send_tx_to_l1(
408 input: &mut PrecompileInput<'_>,
409 destination: Address,
410 calldata: &[u8],
411) -> PrecompileResult {
412 let caller = input.caller;
413 let value = input.value;
414 let l1_block_number = U256::from(crate::get_l1_block_number_for_evm());
419 let l2_block_number = U256::from(get_current_l2_block());
420 let timestamp = input.internals().block_timestamp();
421
422 let mut gas_used = 0u64;
424 gas_used += COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
426 gas_used += STORAGE_READ_COST;
428
429 let internals = input.internals_mut();
430
431 internals
433 .load_account(ARBOS_STATE_ADDRESS)
434 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
435
436 if !value.is_zero() {
438 let raw_version = internals
440 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
441 .map_err(|_| PrecompileError::other("sload failed"))?
442 .data;
443 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
444 if arbos_version >= 41 {
445 let nt_key = derive_subspace_key(ROOT_STORAGE_KEY, NATIVE_TOKEN_SUBSPACE);
446 let nt_size_slot = map_slot(nt_key.as_slice(), 0);
447 gas_used += STORAGE_READ_COST;
448 let num_owners = internals
449 .sload(ARBOS_STATE_ADDRESS, nt_size_slot)
450 .map_err(|_| PrecompileError::other("sload failed"))?
451 .data;
452 if !num_owners.is_zero() {
453 return Err(PrecompileError::other(
454 "not allowed to send value when native token owners exist",
455 ));
456 }
457 }
458 }
459
460 let merkle_key = derive_subspace_key(ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE);
462 let size_slot = map_slot(merkle_key.as_slice(), 0);
463 gas_used += STORAGE_READ_COST;
464 let current_size = internals
465 .sload(ARBOS_STATE_ADDRESS, size_slot)
466 .map_err(|_| PrecompileError::other("sload failed"))?
467 .data;
468 let old_size: u64 = current_size.try_into().unwrap_or(0);
469
470 let send_hash_input_len = 20 + 20 + 32 * 4 + calldata.len() as u64;
474 gas_used += keccak_gas(send_hash_input_len);
475 let send_hash = compute_send_hash(
476 caller,
477 destination,
478 l2_block_number,
479 l1_block_number,
480 timestamp,
481 value,
482 calldata,
483 );
484
485 let (new_size, merkle_events, partials) =
487 update_merkle_accumulator(internals, &merkle_key, send_hash, old_size, &mut gas_used)?;
488
489 gas_used += STORAGE_READ_COST;
491
492 let new_size_val = U256::from(new_size);
494 gas_used += storage_write_cost(new_size_val);
495 internals
496 .sstore(ARBOS_STATE_ADDRESS, size_slot, new_size_val)
497 .map_err(|_| PrecompileError::other("sstore failed"))?;
498
499 let update_topic = send_merkle_update_topic();
501 for evt in &merkle_events {
502 let position: U256 = (U256::from(evt.level) << 192) | U256::from(evt.num_leaves);
504 internals.log(Log::new_unchecked(
505 ARBSYS_ADDRESS,
506 vec![
507 update_topic,
508 B256::from(U256::ZERO.to_be_bytes::<32>()), B256::from(evt.hash.to_be_bytes::<32>()), B256::from(position.to_be_bytes::<32>()), ],
512 Default::default(), ));
514 gas_used += LOG_GAS + LOG_TOPIC_GAS * 4;
516 }
517
518 let leaf_num = new_size - 1;
519
520 let l2l1_topic = l2_to_l1_tx_topic();
524 let dest_topic = B256::left_padding_from(destination.as_slice());
525 let hash_topic = B256::from(U256::from_be_bytes(send_hash.0).to_be_bytes::<32>());
526 let position_topic = B256::from(U256::from(leaf_num).to_be_bytes::<32>());
527
528 let mut event_data = Vec::with_capacity(256);
529 let mut caller_padded = [0u8; 32];
531 caller_padded[12..32].copy_from_slice(caller.as_slice());
532 event_data.extend_from_slice(&caller_padded);
533 event_data.extend_from_slice(&l2_block_number.to_be_bytes::<32>());
535 event_data.extend_from_slice(&l1_block_number.to_be_bytes::<32>());
537 event_data.extend_from_slice(×tamp.to_be_bytes::<32>());
539 event_data.extend_from_slice(&value.to_be_bytes::<32>());
541 event_data.extend_from_slice(&U256::from(6 * 32).to_be_bytes::<32>()); event_data.extend_from_slice(&U256::from(calldata.len()).to_be_bytes::<32>());
544 event_data.extend_from_slice(calldata);
545 let pad = (32 - calldata.len() % 32) % 32;
547 event_data.extend(std::iter::repeat_n(0u8, pad));
548
549 let l2l1_data_len = event_data.len() as u64;
550 internals.log(Log::new_unchecked(
551 ARBSYS_ADDRESS,
552 vec![l2l1_topic, dest_topic, hash_topic, position_topic],
553 event_data.into(),
554 ));
555 gas_used += LOG_GAS + LOG_TOPIC_GAS * 4 + LOG_DATA_GAS * l2l1_data_len;
557
558 store_arbsys_state(ArbSysMerkleState {
560 new_size,
561 partials: partials
562 .iter()
563 .enumerate()
564 .map(|(i, h)| (i as u64, *h))
565 .collect(),
566 send_hash,
567 leaf_num,
568 value_to_burn: value,
569 block_number: l2_block_number.try_into().unwrap_or(0),
570 });
571
572 let raw_version = internals
574 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
575 .map_err(|_| PrecompileError::other("sload failed"))?
576 .data;
577 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
578
579 let return_val = if arbos_version >= 4 {
581 U256::from(leaf_num)
582 } else {
583 U256::from_be_bytes(send_hash.0)
584 };
585
586 let output = return_val.to_be_bytes::<32>().to_vec();
588 gas_used += COPY_GAS * words_for_bytes(output.len() as u64);
589
590 Ok(PrecompileOutput::new(gas_used, output.into()))
591}
592
593fn handle_send_merkle_tree_state(input: &mut PrecompileInput<'_>) -> PrecompileResult {
594 if input.caller != Address::ZERO {
596 return Err(PrecompileError::other(
597 "method can only be called by address zero",
598 ));
599 }
600 let mut gas_used = 0u64;
601 let internals = input.internals_mut();
602
603 internals
604 .load_account(ARBOS_STATE_ADDRESS)
605 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
606
607 let merkle_key = derive_subspace_key(ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE);
608 let size_slot = map_slot(merkle_key.as_slice(), 0);
609 gas_used += STORAGE_READ_COST;
610 let size = internals
611 .sload(ARBOS_STATE_ADDRESS, size_slot)
612 .map_err(|_| PrecompileError::other("sload failed"))?
613 .data;
614
615 let size_u64: u64 = size.try_into().unwrap_or(0);
616
617 let num_partials = calc_num_partials(size_u64);
619 let mut partials = Vec::new();
620 for i in 0..num_partials {
621 let slot = map_slot(merkle_key.as_slice(), 2 + i);
622 gas_used += STORAGE_READ_COST;
623 let val = internals
624 .sload(ARBOS_STATE_ADDRESS, slot)
625 .map_err(|_| PrecompileError::other("sload failed"))?
626 .data;
627 partials.push(val);
628 }
629
630 let b256_partials: Vec<B256> = partials
631 .iter()
632 .map(|p| B256::from(p.to_be_bytes::<32>()))
633 .collect();
634 let root = compute_merkle_root(&b256_partials, size_u64);
635
636 let num_partials = partials.len();
639 let mut out = Vec::with_capacity(96 + num_partials * 32);
640 out.extend_from_slice(&size.to_be_bytes::<32>());
641 out.extend_from_slice(&root.0);
642 out.extend_from_slice(&U256::from(96u64).to_be_bytes::<32>());
644 out.extend_from_slice(&U256::from(num_partials).to_be_bytes::<32>());
645 for p in &partials {
646 out.extend_from_slice(&p.to_be_bytes::<32>());
647 }
648
649 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
650 let result_cost = COPY_GAS * words_for_bytes(out.len() as u64);
651 Ok(PrecompileOutput::new(
652 gas_used + args_cost + result_cost,
653 out.into(),
654 ))
655}
656
657fn compute_send_hash(
660 sender: Address,
661 dest: Address,
662 arb_block_num: U256,
663 eth_block_num: U256,
664 timestamp: U256,
665 value: U256,
666 data: &[u8],
667) -> B256 {
668 let mut preimage = Vec::with_capacity(200 + data.len());
670 preimage.extend_from_slice(sender.as_slice()); preimage.extend_from_slice(dest.as_slice()); preimage.extend_from_slice(&arb_block_num.to_be_bytes::<32>());
673 preimage.extend_from_slice(ð_block_num.to_be_bytes::<32>());
674 preimage.extend_from_slice(×tamp.to_be_bytes::<32>());
675 preimage.extend_from_slice(&value.to_be_bytes::<32>());
676 preimage.extend_from_slice(data);
677 keccak256(&preimage)
678}
679
680struct MerkleTreeNodeEvent {
682 level: u64,
683 num_leaves: u64,
684 hash: U256,
685}
686
687fn update_merkle_accumulator(
691 internals: &mut alloy_evm::EvmInternals<'_>,
692 merkle_key: &B256,
693 item_hash: B256,
694 old_size: u64,
695 gas_used: &mut u64,
696) -> Result<(u64, Vec<MerkleTreeNodeEvent>, Vec<B256>), PrecompileError> {
697 let new_size = old_size + 1;
698 let mut events = Vec::new();
699
700 let mut so_far = keccak256(item_hash.as_slice()).to_vec();
702
703 let num_partials_old = calc_num_partials(old_size);
704 let mut level = 0u64;
705
706 loop {
707 if level == num_partials_old {
708 let h = U256::from_be_slice(&so_far);
710 let slot = map_slot(merkle_key.as_slice(), 2 + level);
711 *gas_used += storage_write_cost(h);
712 internals
713 .sstore(ARBOS_STATE_ADDRESS, slot, h)
714 .map_err(|_| PrecompileError::other("sstore failed"))?;
715 break;
716 }
717
718 let slot = map_slot(merkle_key.as_slice(), 2 + level);
720 *gas_used += STORAGE_READ_COST;
721 let this_level = internals
722 .sload(ARBOS_STATE_ADDRESS, slot)
723 .map_err(|_| PrecompileError::other("sload failed"))?
724 .data;
725
726 if this_level.is_zero() {
727 let h = U256::from_be_slice(&so_far);
729 *gas_used += storage_write_cost(h);
730 internals
731 .sstore(ARBOS_STATE_ADDRESS, slot, h)
732 .map_err(|_| PrecompileError::other("sstore failed"))?;
733 break;
734 }
735
736 *gas_used += keccak_gas(64);
739 let mut preimage = [0u8; 64];
740 preimage[..32].copy_from_slice(&this_level.to_be_bytes::<32>());
741 preimage[32..].copy_from_slice(&so_far);
742 so_far = keccak256(preimage).to_vec();
743
744 *gas_used += STORAGE_WRITE_ZERO_COST;
746 internals
747 .sstore(ARBOS_STATE_ADDRESS, slot, U256::ZERO)
748 .map_err(|_| PrecompileError::other("sstore failed"))?;
749
750 level += 1;
751
752 events.push(MerkleTreeNodeEvent {
754 level,
755 num_leaves: new_size - 1,
756 hash: U256::from_be_slice(&so_far),
757 });
758 }
759
760 let num_partials = calc_num_partials(new_size);
764 let mut partials = Vec::with_capacity(num_partials as usize);
765 for i in 0..num_partials {
766 let pslot = map_slot(merkle_key.as_slice(), 2 + i);
767 let val = internals
768 .sload(ARBOS_STATE_ADDRESS, pslot)
769 .map_err(|_| PrecompileError::other("sload failed"))?
770 .data;
771 partials.push(B256::from(val.to_be_bytes::<32>()));
772 }
773
774 Ok((new_size, events, partials))
775}
776
777fn calc_num_partials(size: u64) -> u64 {
779 if size == 0 {
780 return 0;
781 }
782 64 - size.leading_zeros() as u64
783}
784
785fn compute_merkle_root(partials: &[B256], size: u64) -> B256 {
789 if partials.is_empty() || size == 0 {
790 return B256::ZERO;
791 }
792
793 let num_partials = calc_num_partials(size);
794 let mut hash_so_far: Option<B256> = None;
795 let mut capacity_in_hash: u64 = 0;
796 let mut capacity: u64 = 1;
797
798 for level in 0..num_partials {
799 let partial = if (level as usize) < partials.len() {
800 partials[level as usize]
801 } else {
802 B256::ZERO
803 };
804
805 if partial != B256::ZERO {
806 match hash_so_far {
807 None => {
808 hash_so_far = Some(partial);
809 capacity_in_hash = capacity;
810 }
811 Some(ref h) => {
812 let mut current = *h;
814 let mut cap = capacity_in_hash;
815 while cap < capacity {
816 let mut preimage = [0u8; 64];
817 preimage[..32].copy_from_slice(current.as_slice());
818 current = keccak256(preimage);
820 cap *= 2;
821 }
822 let mut preimage = [0u8; 64];
824 preimage[..32].copy_from_slice(partial.as_slice());
825 preimage[32..].copy_from_slice(current.as_slice());
826 let combined = keccak256(preimage);
827 hash_so_far = Some(combined);
828 capacity_in_hash = 2 * capacity;
829 }
830 }
831 }
832 capacity *= 2;
833 }
834
835 hash_so_far.unwrap_or(B256::ZERO)
836}
837
838fn apply_l1_alias(addr: Address) -> Address {
841 let mut bytes = [0u8; 20];
842 for (i, byte) in bytes.iter_mut().enumerate() {
843 *byte = addr.0[i].wrapping_add(L1_ALIAS_OFFSET.0[i]);
844 }
845 Address::new(bytes)
846}
847
848fn undo_l1_alias(addr: Address) -> Address {
849 let mut bytes = [0u8; 20];
850 for (i, byte) in bytes.iter_mut().enumerate() {
851 *byte = addr.0[i].wrapping_sub(L1_ALIAS_OFFSET.0[i]);
852 }
853 Address::new(bytes)
854}