1use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
2use alloy_primitives::{keccak256, Address, Log, B256, U256};
3use alloy_sol_types::{SolError, SolEvent, SolInterface};
4use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
5
6use std::{cell::RefCell, collections::HashMap, sync::Mutex};
7
8use crate::{
9 interfaces::IArbSys,
10 storage_slot::{
11 derive_subspace_key, map_slot, root_slot, ARBOS_STATE_ADDRESS, NATIVE_TOKEN_SUBSPACE,
12 ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE,
13 },
14};
15
16pub const ARBSYS_ADDRESS: Address = Address::new([
18 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
19 0x00, 0x00, 0x00, 0x64,
20]);
21
22const L1_ALIAS_OFFSET: Address = Address::new([
24 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
25 0x00, 0x00, 0x11, 0x11,
26]);
27
28const COPY_GAS: u64 = 3; const LOG_GAS: u64 = 375;
33const LOG_TOPIC_GAS: u64 = 375;
34const 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 {
42 if value.is_zero() {
43 STORAGE_WRITE_ZERO_COST
44 } else {
45 STORAGE_WRITE_COST
46 }
47}
48
49fn words_for_bytes(n: u64) -> u64 {
50 n.div_ceil(32)
51}
52
53fn keccak_gas(byte_count: u64) -> u64 {
55 30 + 6 * words_for_bytes(byte_count)
56}
57
58pub fn l2_to_l1_tx_topic() -> B256 {
59 IArbSys::L2ToL1Tx::SIGNATURE_HASH
60}
61
62pub fn send_merkle_update_topic() -> B256 {
63 IArbSys::SendMerkleUpdate::SIGNATURE_HASH
64}
65
66#[derive(Debug, Clone, Default)]
68pub struct ArbSysMerkleState {
69 pub new_size: u64,
70 pub partials: Vec<(u64, B256)>,
71 pub send_hash: B256,
72 pub leaf_num: u64,
73 pub value_to_burn: U256,
74 pub block_number: u64,
75}
76
77thread_local! {
78 static ARBSYS_STATE: RefCell<Option<ArbSysMerkleState>> = const { RefCell::new(None) };
79 static TX_IS_ALIASED: RefCell<bool> = const { RefCell::new(false) };
82}
83
84static L1_BLOCK_CACHE: Mutex<Option<HashMap<u64, u64>>> = Mutex::new(None);
85static CURRENT_L2_BLOCK: Mutex<u64> = Mutex::new(0);
86
87pub fn store_arbsys_state(state: ArbSysMerkleState) {
89 ARBSYS_STATE.with(|cell| *cell.borrow_mut() = Some(state));
90}
91
92pub fn take_arbsys_state() -> Option<ArbSysMerkleState> {
94 ARBSYS_STATE.with(|cell| cell.borrow_mut().take())
95}
96
97pub fn set_tx_is_aliased(aliased: bool) {
99 TX_IS_ALIASED.with(|cell| *cell.borrow_mut() = aliased);
100}
101
102pub fn get_tx_is_aliased() -> bool {
104 TX_IS_ALIASED.with(|cell| *cell.borrow())
105}
106
107pub fn set_cached_l1_block_number(l2_block: u64, l1_block: u64) {
109 let mut cache = L1_BLOCK_CACHE.lock().expect("L1 block cache lock poisoned");
110 let map = cache.get_or_insert_with(HashMap::new);
111 map.insert(l2_block, l1_block);
112 if l2_block > 100 {
113 map.retain(|&k, _| k >= l2_block - 100);
114 }
115}
116
117pub fn get_cached_l1_block_number(l2_block: u64) -> Option<u64> {
119 let cache = L1_BLOCK_CACHE.lock().expect("L1 block cache lock poisoned");
120 cache.as_ref().and_then(|m| m.get(&l2_block).copied())
121}
122
123pub fn set_current_l2_block(l2_block: u64) {
127 *CURRENT_L2_BLOCK.lock().expect("L2 block lock poisoned") = l2_block;
128}
129
130pub fn get_current_l2_block() -> u64 {
132 *CURRENT_L2_BLOCK.lock().expect("L2 block lock poisoned")
133}
134
135pub fn create_arbsys_precompile() -> DynPrecompile {
136 DynPrecompile::new_stateful(PrecompileId::custom("arbsys"), handler)
137}
138
139fn handler(mut input: PrecompileInput<'_>) -> PrecompileResult {
140 let gas_limit = input.gas;
141 let data = input.data;
142 crate::init_precompile_gas(data.len());
143
144 let call = match IArbSys::ArbSysCalls::abi_decode(data) {
145 Ok(c) => c,
146 Err(_) => return crate::burn_all_revert(gas_limit),
147 };
148
149 use IArbSys::ArbSysCalls;
150 let result = match call {
151 ArbSysCalls::arbBlockNumber(_) => handle_arb_block_number(&mut input),
152 ArbSysCalls::arbBlockHash(c) => handle_arb_block_hash(&mut input, c.arbBlockNum),
153 ArbSysCalls::arbChainID(_) => handle_arb_chain_id(&mut input),
154 ArbSysCalls::arbOSVersion(_) => handle_arbos_version(&mut input),
155 ArbSysCalls::getStorageGasAvailable(_) => handle_get_storage_gas(&mut input),
156 ArbSysCalls::isTopLevelCall(_) => handle_is_top_level_call(&mut input),
157 ArbSysCalls::mapL1SenderContractAddressToL2Alias(c) => {
158 handle_map_l1_sender(&mut input, c.sender)
159 }
160 ArbSysCalls::wasMyCallersAddressAliased(_) => handle_was_aliased(&mut input),
161 ArbSysCalls::myCallersAddressWithoutAliasing(_) => handle_caller_without_alias(&mut input),
162 ArbSysCalls::withdrawEth(c) => handle_withdraw_eth(&mut input, c.destination),
163 ArbSysCalls::sendTxToL1(c) => {
164 handle_send_tx_to_l1(&mut input, c.destination, c.data.as_ref())
165 }
166 ArbSysCalls::sendMerkleTreeState(_) => handle_send_merkle_tree_state(&mut input),
167 };
168 crate::gas_check(gas_limit, result)
169}
170
171fn handle_arb_block_number(input: &mut PrecompileInput<'_>) -> PrecompileResult {
174 let block_num = U256::from(get_current_l2_block());
175 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
176 let result_cost = COPY_GAS * words_for_bytes(32);
177 Ok(PrecompileOutput::new(
178 STORAGE_READ_COST + args_cost + result_cost,
179 block_num.to_be_bytes::<32>().to_vec().into(),
180 ))
181}
182
183fn handle_arb_block_hash(
184 input: &mut PrecompileInput<'_>,
185 requested_u256: U256,
186) -> PrecompileResult {
187 let requested: u64 = requested_u256.try_into().unwrap_or(u64::MAX);
188 let current = get_current_l2_block();
189
190 if requested >= current || requested + 256 < current {
191 let arbos_version = crate::get_arbos_version();
192 if arbos_version >= 11 {
193 let revert_data = IArbSys::InvalidBlockNumber {
194 requested: requested_u256,
195 current: U256::from(current),
196 }
197 .abi_encode();
198 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
199 let result_cost = COPY_GAS * words_for_bytes(revert_data.len() as u64);
200 return Ok(PrecompileOutput::new_reverted(
201 STORAGE_READ_COST + args_cost + result_cost,
202 revert_data.into(),
203 ));
204 }
205 return Err(PrecompileError::other("invalid block number"));
206 }
207
208 let hash = crate::get_l2_block_hash(requested).unwrap_or(B256::ZERO);
211
212 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
213 let result_cost = COPY_GAS * words_for_bytes(32);
214 Ok(PrecompileOutput::new(
215 STORAGE_READ_COST + args_cost + result_cost,
216 hash.0.to_vec().into(),
217 ))
218}
219
220fn handle_arb_chain_id(input: &mut PrecompileInput<'_>) -> PrecompileResult {
221 let chain_id = input.internals().chain_id();
222 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
223 let result_cost = COPY_GAS * words_for_bytes(32);
224 Ok(PrecompileOutput::new(
225 STORAGE_READ_COST + args_cost + result_cost,
226 U256::from(chain_id).to_be_bytes::<32>().to_vec().into(),
227 ))
228}
229
230fn handle_arbos_version(input: &mut PrecompileInput<'_>) -> PrecompileResult {
231 let internals = input.internals_mut();
232
233 internals
234 .load_account(ARBOS_STATE_ADDRESS)
235 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
236
237 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
268 let internals = input.internals_mut();
270 internals
271 .load_account(ARBOS_STATE_ADDRESS)
272 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
273 let raw_version = internals
274 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
275 .map_err(|_| PrecompileError::other("sload failed"))?
276 .data;
277 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
278
279 let depth = crate::get_evm_depth();
280 let is_top_level = if arbos_version < 6 {
281 depth == 2
282 } else if depth <= 2 {
283 true
284 } else {
285 crate::caller_at_depth(depth - 1)
286 .map(|c| tx_origin == c)
287 .unwrap_or(false)
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();
302 let depth = crate::get_evm_depth();
303 let address = if depth <= 2 {
304 tx_origin
305 } else {
306 crate::caller_at_depth(depth - 1).unwrap_or(tx_origin)
307 };
308
309 let result_addr = if get_tx_is_aliased() && address == tx_origin {
310 undo_l1_alias(address)
311 } else {
312 address
313 };
314
315 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
316 let result_cost = COPY_GAS * words_for_bytes(32);
317 let mut out = [0u8; 32];
318 out[12..32].copy_from_slice(result_addr.as_slice());
319 Ok(PrecompileOutput::new(
320 STORAGE_READ_COST + args_cost + result_cost,
321 out.to_vec().into(),
322 ))
323}
324
325fn handle_map_l1_sender(input: &mut PrecompileInput<'_>, l1_addr: Address) -> PrecompileResult {
326 let aliased = apply_l1_alias(l1_addr);
327 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
328 let result_cost = COPY_GAS * words_for_bytes(32);
329 let mut out = [0u8; 32];
330 out[12..32].copy_from_slice(aliased.as_slice());
331 Ok(PrecompileOutput::new(
332 args_cost + result_cost,
333 out.to_vec().into(),
334 ))
335}
336
337fn handle_get_storage_gas(input: &mut PrecompileInput<'_>) -> PrecompileResult {
338 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
340 let result_cost = COPY_GAS * words_for_bytes(32);
341 Ok(PrecompileOutput::new(
342 STORAGE_READ_COST + args_cost + result_cost,
343 U256::ZERO.to_be_bytes::<32>().to_vec().into(),
344 ))
345}
346
347fn handle_withdraw_eth(input: &mut PrecompileInput<'_>, destination: Address) -> PrecompileResult {
350 if input.is_static {
351 return Err(PrecompileError::other(
352 "cannot call withdrawEth in static context",
353 ));
354 }
355 do_send_tx_to_l1(input, destination, &[])
356}
357
358fn handle_send_tx_to_l1(
359 input: &mut PrecompileInput<'_>,
360 destination: Address,
361 calldata: &[u8],
362) -> PrecompileResult {
363 if input.is_static {
364 return Err(PrecompileError::other(
365 "cannot call sendTxToL1 in static context",
366 ));
367 }
368 do_send_tx_to_l1(input, destination, calldata)
369}
370
371fn do_send_tx_to_l1(
372 input: &mut PrecompileInput<'_>,
373 destination: Address,
374 calldata: &[u8],
375) -> PrecompileResult {
376 let caller = input.caller;
377 let value = input.value;
378 let l1_block_number = U256::from(crate::get_l1_block_number_for_evm());
381 let l2_block_number = U256::from(get_current_l2_block());
382 let timestamp = input.internals().block_timestamp();
383
384 let mut gas_used = 0u64;
385 gas_used += COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
387 gas_used += STORAGE_READ_COST;
389
390 let internals = input.internals_mut();
391
392 internals
394 .load_account(ARBOS_STATE_ADDRESS)
395 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
396
397 if !value.is_zero() {
399 let raw_version = internals
401 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
402 .map_err(|_| PrecompileError::other("sload failed"))?
403 .data;
404 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
405 if arbos_version >= 41 {
406 let nt_key = derive_subspace_key(ROOT_STORAGE_KEY, NATIVE_TOKEN_SUBSPACE);
407 let nt_size_slot = map_slot(nt_key.as_slice(), 0);
408 gas_used += STORAGE_READ_COST;
409 let num_owners = internals
410 .sload(ARBOS_STATE_ADDRESS, nt_size_slot)
411 .map_err(|_| PrecompileError::other("sload failed"))?
412 .data;
413 if !num_owners.is_zero() {
414 return Err(PrecompileError::other(
415 "not allowed to send value when native token owners exist",
416 ));
417 }
418 }
419 }
420
421 let merkle_key = derive_subspace_key(ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE);
423 let size_slot = map_slot(merkle_key.as_slice(), 0);
424 gas_used += STORAGE_READ_COST;
425 let current_size = internals
426 .sload(ARBOS_STATE_ADDRESS, size_slot)
427 .map_err(|_| PrecompileError::other("sload failed"))?
428 .data;
429 let old_size: u64 = current_size.try_into().unwrap_or(0);
430
431 let send_hash_input_len = 20 + 20 + 32 * 4 + calldata.len() as u64;
435 gas_used += keccak_gas(send_hash_input_len);
436 let send_hash = compute_send_hash(
437 caller,
438 destination,
439 l2_block_number,
440 l1_block_number,
441 timestamp,
442 value,
443 calldata,
444 );
445
446 let (new_size, merkle_events, partials) =
448 update_merkle_accumulator(internals, &merkle_key, send_hash, old_size, &mut gas_used)?;
449
450 gas_used += STORAGE_READ_COST;
452
453 let new_size_val = U256::from(new_size);
455 gas_used += storage_write_cost(new_size_val);
456 internals
457 .sstore(ARBOS_STATE_ADDRESS, size_slot, new_size_val)
458 .map_err(|_| PrecompileError::other("sstore failed"))?;
459
460 let update_topic = send_merkle_update_topic();
462 for evt in &merkle_events {
463 let position: U256 = (U256::from(evt.level) << 192) | U256::from(evt.num_leaves);
465 internals.log(Log::new_unchecked(
466 ARBSYS_ADDRESS,
467 vec![
468 update_topic,
469 B256::from(U256::ZERO.to_be_bytes::<32>()), B256::from(evt.hash.to_be_bytes::<32>()), B256::from(position.to_be_bytes::<32>()), ],
473 Default::default(), ));
475 gas_used += LOG_GAS + LOG_TOPIC_GAS * 4;
477 }
478
479 let leaf_num = new_size - 1;
480
481 let l2l1_topic = l2_to_l1_tx_topic();
485 let dest_topic = B256::left_padding_from(destination.as_slice());
486 let hash_topic = B256::from(U256::from_be_bytes(send_hash.0).to_be_bytes::<32>());
487 let position_topic = B256::from(U256::from(leaf_num).to_be_bytes::<32>());
488
489 let mut event_data = Vec::with_capacity(256);
490 let mut caller_padded = [0u8; 32];
492 caller_padded[12..32].copy_from_slice(caller.as_slice());
493 event_data.extend_from_slice(&caller_padded);
494 event_data.extend_from_slice(&l2_block_number.to_be_bytes::<32>());
496 event_data.extend_from_slice(&l1_block_number.to_be_bytes::<32>());
498 event_data.extend_from_slice(×tamp.to_be_bytes::<32>());
500 event_data.extend_from_slice(&value.to_be_bytes::<32>());
502 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>());
505 event_data.extend_from_slice(calldata);
506 let pad = (32 - calldata.len() % 32) % 32;
508 event_data.extend(std::iter::repeat_n(0u8, pad));
509
510 let l2l1_data_len = event_data.len() as u64;
511 internals.log(Log::new_unchecked(
512 ARBSYS_ADDRESS,
513 vec![l2l1_topic, dest_topic, hash_topic, position_topic],
514 event_data.into(),
515 ));
516 gas_used += LOG_GAS + LOG_TOPIC_GAS * 4 + LOG_DATA_GAS * l2l1_data_len;
518
519 store_arbsys_state(ArbSysMerkleState {
521 new_size,
522 partials: partials
523 .iter()
524 .enumerate()
525 .map(|(i, h)| (i as u64, *h))
526 .collect(),
527 send_hash,
528 leaf_num,
529 value_to_burn: value,
530 block_number: l2_block_number.try_into().unwrap_or(0),
531 });
532
533 let raw_version = internals
535 .sload(ARBOS_STATE_ADDRESS, root_slot(0))
536 .map_err(|_| PrecompileError::other("sload failed"))?
537 .data;
538 let arbos_version: u64 = raw_version.try_into().unwrap_or(0);
539
540 let return_val = if arbos_version >= 4 {
542 U256::from(leaf_num)
543 } else {
544 U256::from_be_bytes(send_hash.0)
545 };
546
547 let output = return_val.to_be_bytes::<32>().to_vec();
549 gas_used += COPY_GAS * words_for_bytes(output.len() as u64);
550
551 Ok(PrecompileOutput::new(gas_used, output.into()))
552}
553
554fn handle_send_merkle_tree_state(input: &mut PrecompileInput<'_>) -> PrecompileResult {
555 if input.caller != Address::ZERO {
557 return Err(PrecompileError::other(
558 "method can only be called by address zero",
559 ));
560 }
561 let mut gas_used = 0u64;
562 let internals = input.internals_mut();
563
564 internals
565 .load_account(ARBOS_STATE_ADDRESS)
566 .map_err(|e| PrecompileError::other(format!("load_account: {e:?}")))?;
567
568 let merkle_key = derive_subspace_key(ROOT_STORAGE_KEY, SEND_MERKLE_SUBSPACE);
569 let size_slot = map_slot(merkle_key.as_slice(), 0);
570 gas_used += STORAGE_READ_COST;
571 let size = internals
572 .sload(ARBOS_STATE_ADDRESS, size_slot)
573 .map_err(|_| PrecompileError::other("sload failed"))?
574 .data;
575
576 let size_u64: u64 = size.try_into().unwrap_or(0);
577
578 let num_partials = calc_num_partials(size_u64);
580 let mut partials = Vec::new();
581 for i in 0..num_partials {
582 let slot = map_slot(merkle_key.as_slice(), 2 + i);
583 gas_used += STORAGE_READ_COST;
584 let val = internals
585 .sload(ARBOS_STATE_ADDRESS, slot)
586 .map_err(|_| PrecompileError::other("sload failed"))?
587 .data;
588 partials.push(val);
589 }
590
591 let b256_partials: Vec<B256> = partials
592 .iter()
593 .map(|p| B256::from(p.to_be_bytes::<32>()))
594 .collect();
595 let root = compute_merkle_root(&b256_partials, size_u64);
596
597 let num_partials = partials.len();
600 let mut out = Vec::with_capacity(96 + num_partials * 32);
601 out.extend_from_slice(&size.to_be_bytes::<32>());
602 out.extend_from_slice(&root.0);
603 out.extend_from_slice(&U256::from(96u64).to_be_bytes::<32>());
605 out.extend_from_slice(&U256::from(num_partials).to_be_bytes::<32>());
606 for p in &partials {
607 out.extend_from_slice(&p.to_be_bytes::<32>());
608 }
609
610 let args_cost = COPY_GAS * words_for_bytes(input.data.len().saturating_sub(4) as u64);
611 let result_cost = COPY_GAS * words_for_bytes(out.len() as u64);
612 Ok(PrecompileOutput::new(
613 gas_used + args_cost + result_cost,
614 out.into(),
615 ))
616}
617
618fn compute_send_hash(
621 sender: Address,
622 dest: Address,
623 arb_block_num: U256,
624 eth_block_num: U256,
625 timestamp: U256,
626 value: U256,
627 data: &[u8],
628) -> B256 {
629 let mut preimage = Vec::with_capacity(200 + data.len());
631 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>());
634 preimage.extend_from_slice(ð_block_num.to_be_bytes::<32>());
635 preimage.extend_from_slice(×tamp.to_be_bytes::<32>());
636 preimage.extend_from_slice(&value.to_be_bytes::<32>());
637 preimage.extend_from_slice(data);
638 keccak256(&preimage)
639}
640
641struct MerkleTreeNodeEvent {
643 level: u64,
644 num_leaves: u64,
645 hash: U256,
646}
647
648fn update_merkle_accumulator(
652 internals: &mut alloy_evm::EvmInternals<'_>,
653 merkle_key: &B256,
654 item_hash: B256,
655 old_size: u64,
656 gas_used: &mut u64,
657) -> Result<(u64, Vec<MerkleTreeNodeEvent>, Vec<B256>), PrecompileError> {
658 let new_size = old_size + 1;
659 let mut events = Vec::new();
660
661 let mut so_far = keccak256(item_hash.as_slice()).to_vec();
663
664 let num_partials_old = calc_num_partials(old_size);
665 let mut level = 0u64;
666
667 loop {
668 if level == num_partials_old {
669 let h = U256::from_be_slice(&so_far);
671 let slot = map_slot(merkle_key.as_slice(), 2 + level);
672 *gas_used += storage_write_cost(h);
673 internals
674 .sstore(ARBOS_STATE_ADDRESS, slot, h)
675 .map_err(|_| PrecompileError::other("sstore failed"))?;
676 break;
677 }
678
679 let slot = map_slot(merkle_key.as_slice(), 2 + level);
681 *gas_used += STORAGE_READ_COST;
682 let this_level = internals
683 .sload(ARBOS_STATE_ADDRESS, slot)
684 .map_err(|_| PrecompileError::other("sload failed"))?
685 .data;
686
687 if this_level.is_zero() {
688 let h = U256::from_be_slice(&so_far);
690 *gas_used += storage_write_cost(h);
691 internals
692 .sstore(ARBOS_STATE_ADDRESS, slot, h)
693 .map_err(|_| PrecompileError::other("sstore failed"))?;
694 break;
695 }
696
697 *gas_used += keccak_gas(64);
700 let mut preimage = [0u8; 64];
701 preimage[..32].copy_from_slice(&this_level.to_be_bytes::<32>());
702 preimage[32..].copy_from_slice(&so_far);
703 so_far = keccak256(preimage).to_vec();
704
705 *gas_used += STORAGE_WRITE_ZERO_COST;
707 internals
708 .sstore(ARBOS_STATE_ADDRESS, slot, U256::ZERO)
709 .map_err(|_| PrecompileError::other("sstore failed"))?;
710
711 level += 1;
712
713 events.push(MerkleTreeNodeEvent {
715 level,
716 num_leaves: new_size - 1,
717 hash: U256::from_be_slice(&so_far),
718 });
719 }
720
721 let num_partials = calc_num_partials(new_size);
725 let mut partials = Vec::with_capacity(num_partials as usize);
726 for i in 0..num_partials {
727 let pslot = map_slot(merkle_key.as_slice(), 2 + i);
728 let val = internals
729 .sload(ARBOS_STATE_ADDRESS, pslot)
730 .map_err(|_| PrecompileError::other("sload failed"))?
731 .data;
732 partials.push(B256::from(val.to_be_bytes::<32>()));
733 }
734
735 Ok((new_size, events, partials))
736}
737
738fn calc_num_partials(size: u64) -> u64 {
740 if size == 0 {
741 return 0;
742 }
743 64 - size.leading_zeros() as u64
744}
745
746fn compute_merkle_root(partials: &[B256], size: u64) -> B256 {
750 if partials.is_empty() || size == 0 {
751 return B256::ZERO;
752 }
753
754 let num_partials = calc_num_partials(size);
755 let mut hash_so_far: Option<B256> = None;
756 let mut capacity_in_hash: u64 = 0;
757 let mut capacity: u64 = 1;
758
759 for level in 0..num_partials {
760 let partial = if (level as usize) < partials.len() {
761 partials[level as usize]
762 } else {
763 B256::ZERO
764 };
765
766 if partial != B256::ZERO {
767 match hash_so_far {
768 None => {
769 hash_so_far = Some(partial);
770 capacity_in_hash = capacity;
771 }
772 Some(ref h) => {
773 let mut current = *h;
775 let mut cap = capacity_in_hash;
776 while cap < capacity {
777 let mut preimage = [0u8; 64];
778 preimage[..32].copy_from_slice(current.as_slice());
779 current = keccak256(preimage);
781 cap *= 2;
782 }
783 let mut preimage = [0u8; 64];
785 preimage[..32].copy_from_slice(partial.as_slice());
786 preimage[32..].copy_from_slice(current.as_slice());
787 let combined = keccak256(preimage);
788 hash_so_far = Some(combined);
789 capacity_in_hash = 2 * capacity;
790 }
791 }
792 }
793 capacity *= 2;
794 }
795
796 hash_so_far.unwrap_or(B256::ZERO)
797}
798
799fn alias_offset_u256() -> U256 {
802 U256::from_be_slice(L1_ALIAS_OFFSET.as_slice())
803}
804
805fn truncate_to_address(v: U256) -> Address {
806 let bytes = v.to_be_bytes::<32>();
807 Address::from_slice(&bytes[12..])
808}
809
810fn apply_l1_alias(addr: Address) -> Address {
811 let val = U256::from_be_slice(addr.as_slice());
812 truncate_to_address(val.wrapping_add(alias_offset_u256()))
813}
814
815fn undo_l1_alias(addr: Address) -> Address {
816 let val = U256::from_be_slice(addr.as_slice());
817 truncate_to_address(val.wrapping_sub(alias_offset_u256()))
818}
819
820#[cfg(test)]
821mod alias_tests {
822 use super::*;
823 use alloy_primitives::address;
824
825 #[test]
826 fn alias_simple_no_carry() {
827 let l1 = address!("0000000000000000000000000000000000000000");
828 let aliased = apply_l1_alias(l1);
829 assert_eq!(aliased, L1_ALIAS_OFFSET);
830 assert_eq!(undo_l1_alias(aliased), l1);
831 }
832
833 #[test]
834 fn alias_carry_propagates_across_bytes() {
835 let l1 = address!("00ef000000000000000000000000000000000000");
836 let expected = address!("1200000000000000000000000000000000001111");
837 assert_eq!(apply_l1_alias(l1), expected);
838 assert_eq!(undo_l1_alias(expected), l1);
839 }
840
841 #[test]
842 fn alias_wraps_at_160_bits() {
843 let l1 = address!("ffffffffffffffffffffffffffffffffffffffff");
847 let expected = address!("1111000000000000000000000000000000001110");
848 assert_eq!(apply_l1_alias(l1), expected);
849 assert_eq!(undo_l1_alias(expected), l1);
850 }
851
852 #[test]
853 fn alias_inverse_round_trip() {
854 let cases = [
855 address!("0123456789abcdef0123456789abcdef01234567"),
856 address!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
857 address!("ffeeffeeffeeffeeffeeffeeffeeffeeffeeffee"),
858 ];
859 for addr in cases {
860 let aliased = apply_l1_alias(addr);
861 let restored = undo_l1_alias(aliased);
862 assert_eq!(restored, addr, "round trip failed for {addr}");
863 }
864 }
865}