1use std::sync::Arc;
2
3use alloy_evm::precompiles::{DynPrecompile, PrecompileInput};
4use alloy_primitives::{Address, B256, Log, U256, keccak256};
5use alloy_sol_types::{SolError, SolEvent, SolInterface};
6use arb_context::ArbPrecompileCtx;
7use arb_storage::ARBOS_STATE_ADDRESS;
8use arbos::merkle_accumulator::calc_num_partials;
9use revm::precompile::{PrecompileId, PrecompileOutput, PrecompileResult};
10
11use crate::{ArbPrecompileError, interfaces::IArbSys};
12
13pub const ARBSYS_ADDRESS: Address = Address::new([
15 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
16 0x00, 0x00, 0x00, 0x64,
17]);
18
19const L1_ALIAS_OFFSET: Address = Address::new([
21 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
22 0x00, 0x00, 0x11, 0x11,
23]);
24
25const COPY_GAS: u64 = 3; const LOG_GAS: u64 = 375;
30const LOG_TOPIC_GAS: u64 = 375;
31const 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 words_for_bytes(n: u64) -> u64 {
39 n.div_ceil(32)
40}
41
42fn keccak_gas(byte_count: u64) -> u64 {
44 30 + 6 * words_for_bytes(byte_count)
45}
46
47pub fn l2_to_l1_tx_topic() -> B256 {
48 IArbSys::L2ToL1Tx::SIGNATURE_HASH
49}
50
51pub fn send_merkle_update_topic() -> B256 {
52 IArbSys::SendMerkleUpdate::SIGNATURE_HASH
53}
54
55pub fn create_arbsys_precompile(ctx: Arc<ArbPrecompileCtx>) -> DynPrecompile {
56 DynPrecompile::new_stateful(PrecompileId::custom("arbsys"), move |input| {
57 handler(input, &ctx)
58 })
59}
60
61fn handler(mut input: PrecompileInput<'_>, ctx: &ArbPrecompileCtx) -> PrecompileResult {
62 let mut gas_used = 0u64;
63 let gas_limit = input.gas;
64 let data = input.data;
65
66 let call = match IArbSys::ArbSysCalls::abi_decode(data) {
67 Ok(c) => c,
68 Err(_) => return crate::burn_all_revert(gas_limit),
69 };
70
71 if let Some(r) = crate::reject_nonpayable_value(
73 input.value,
74 data,
75 gas_limit,
76 &[[0x25, 0xe1, 0x60, 0x63], [0x92, 0x8c, 0x16, 0x9a]],
77 ) {
78 return r;
79 }
80 if let Some(r) = crate::reject_static_write(
81 input.is_static,
82 input.data,
83 gas_limit,
84 &[[0x25, 0xe1, 0x60, 0x63], [0x92, 0x8c, 0x16, 0x9a]],
85 ) {
86 return r;
87 }
88 if let Some(r) = crate::reject_delegate_nonpure(
89 input.target_address != input.bytecode_address,
90 input.data,
91 gas_limit,
92 &[[0x4d, 0xbb, 0xd5, 0x06]],
93 ) {
94 return r;
95 }
96
97 let is_pure = matches!(
101 call,
102 IArbSys::ArbSysCalls::mapL1SenderContractAddressToL2Alias(_)
103 );
104 if is_pure {
105 crate::init_precompile_gas_pure(&mut gas_used, ctx, data.len());
106 } else {
107 crate::init_precompile_gas(&mut gas_used, ctx, data.len());
108 }
109
110 use IArbSys::ArbSysCalls;
111 let result = match call {
112 ArbSysCalls::arbBlockNumber(_) => handle_arb_block_number(&mut input, &mut gas_used, ctx),
113 ArbSysCalls::arbBlockHash(c) => {
114 handle_arb_block_hash(&mut input, &mut gas_used, ctx, c.arbBlockNum)
115 }
116 ArbSysCalls::arbChainID(_) => handle_arb_chain_id(&mut input, &mut gas_used, ctx),
117 ArbSysCalls::arbOSVersion(_) => handle_arbos_version(&mut input, &mut gas_used, ctx),
118 ArbSysCalls::getStorageGasAvailable(_) => {
119 handle_get_storage_gas(&mut input, &mut gas_used, ctx)
120 }
121 ArbSysCalls::isTopLevelCall(_) => handle_is_top_level_call(&mut input, &mut gas_used, ctx),
122 ArbSysCalls::mapL1SenderContractAddressToL2Alias(c) => {
123 handle_map_l1_sender(&mut input, &mut gas_used, ctx, c.sender)
124 }
125 ArbSysCalls::wasMyCallersAddressAliased(_) => {
126 handle_was_aliased(&mut input, &mut gas_used, ctx)
127 }
128 ArbSysCalls::myCallersAddressWithoutAliasing(_) => {
129 handle_caller_without_alias(&mut input, &mut gas_used, ctx)
130 }
131 ArbSysCalls::withdrawEth(c) => {
132 handle_withdraw_eth(&mut input, &mut gas_used, ctx, c.destination)
133 }
134 ArbSysCalls::sendTxToL1(c) => handle_send_tx_to_l1(
135 &mut input,
136 &mut gas_used,
137 ctx,
138 c.destination,
139 c.data.as_ref(),
140 ),
141 ArbSysCalls::sendMerkleTreeState(_) => {
142 handle_send_merkle_tree_state(&mut input, &mut gas_used, ctx)
143 }
144 };
145 crate::gas_check(ctx, gas_limit, gas_used, result)
146}
147
148fn handle_arb_block_number(
151 input: &mut PrecompileInput<'_>,
152 gas_used: &mut u64,
153 ctx: &ArbPrecompileCtx,
154) -> PrecompileResult {
155 let block_num = U256::from(ctx.block.l2_block_number);
156 let gas_limit = input.gas;
157 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
158 Ok(PrecompileOutput::new(
159 (*gas_used).min(gas_limit),
160 block_num.to_be_bytes::<32>().to_vec().into(),
161 ))
162}
163
164#[derive(Debug, thiserror::Error)]
165#[error("arbBlockHash: L2 block {requested} hash unavailable (current {current})")]
166struct MissingL2BlockHash {
167 requested: u64,
168 current: u64,
169}
170
171fn handle_arb_block_hash(
172 input: &mut PrecompileInput<'_>,
173 gas_used: &mut u64,
174 ctx: &ArbPrecompileCtx,
175 requested_u256: U256,
176) -> PrecompileResult {
177 let requested: u64 = requested_u256.try_into().unwrap_or(u64::MAX);
178 let current = ctx.block.l2_block_number;
179 let gas_limit = input.gas;
180
181 if requested >= current || requested + 256 < current {
182 let arbos_version = ctx.block.arbos_version;
183 if arbos_version >= 11 {
184 let revert_data = IArbSys::InvalidBlockNumber {
185 requested: requested_u256,
186 current: U256::from(current),
187 }
188 .abi_encode();
189 crate::charge_computation(
190 gas_used,
191 ctx,
192 COPY_GAS * words_for_bytes(revert_data.len() as u64),
193 );
194 return Ok(PrecompileOutput::new_reverted(
195 (*gas_used).min(gas_limit),
196 revert_data.into(),
197 ));
198 }
199 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
200 }
201
202 let hash = match ctx.block.cached_l2_block_hash(requested) {
205 Some(hash) => hash,
206 None => {
207 return Err(
208 ArbPrecompileError::fatal(MissingL2BlockHash { requested, current }).into(),
209 );
210 }
211 };
212
213 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
214 Ok(PrecompileOutput::new(
215 (*gas_used).min(gas_limit),
216 hash.0.to_vec().into(),
217 ))
218}
219
220fn handle_arb_chain_id(
221 input: &mut PrecompileInput<'_>,
222 gas_used: &mut u64,
223 ctx: &ArbPrecompileCtx,
224) -> PrecompileResult {
225 let chain_id = input.internals().chain_id();
226 let gas_limit = input.gas;
227 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
228 Ok(PrecompileOutput::new(
229 (*gas_used).min(gas_limit),
230 U256::from(chain_id).to_be_bytes::<32>().to_vec().into(),
231 ))
232}
233
234fn arbos_version_from_format(format_version: U256) -> U256 {
236 format_version + U256::from(55)
237}
238
239fn handle_arbos_version(
240 input: &mut PrecompileInput<'_>,
241 gas_used: &mut u64,
242 ctx: &ArbPrecompileCtx,
243) -> PrecompileResult {
244 let gas_limit = input.gas;
245 let internals = input.internals_mut();
246
247 internals
248 .load_account(ARBOS_STATE_ADDRESS)
249 .map_err(ArbPrecompileError::fatal)?;
250
251 let arb_state = ctx
252 .block
253 .arbos_state(internals)
254 .map_err(ArbPrecompileError::fatal)?;
255 let version = arbos_version_from_format(U256::from(arb_state.arbos_version()));
256
257 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
258 Ok(PrecompileOutput::new(
259 (*gas_used).min(gas_limit),
260 version.to_be_bytes::<32>().to_vec().into(),
261 ))
262}
263
264fn handle_is_top_level_call(
265 input: &mut PrecompileInput<'_>,
266 gas_used: &mut u64,
267 ctx: &ArbPrecompileCtx,
268) -> PrecompileResult {
269 let depth = ctx.evm_depth();
270 let is_top = depth <= 2;
271 let val = if is_top { U256::from(1) } else { U256::ZERO };
272 let gas_limit = input.gas;
273 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
274 Ok(PrecompileOutput::new(
275 (*gas_used).min(gas_limit),
276 val.to_be_bytes::<32>().to_vec().into(),
277 ))
278}
279
280fn handle_was_aliased(
281 input: &mut PrecompileInput<'_>,
282 gas_used: &mut u64,
283 ctx: &ArbPrecompileCtx,
284) -> PrecompileResult {
285 let gas_limit = input.gas;
286 let internals = input.internals_mut();
287 internals
288 .load_account(ARBOS_STATE_ADDRESS)
289 .map_err(ArbPrecompileError::fatal)?;
290 let arb_state = ctx
291 .block
292 .arbos_state(internals)
293 .map_err(ArbPrecompileError::fatal)?;
294 let arbos_version = arb_state.arbos_version();
295
296 let tx_origin = input.internals().tx_origin();
297 let depth = ctx.evm_depth();
298 let is_top_level = if arbos_version < 6 {
299 depth == 2
300 } else if depth <= 2 {
301 true
302 } else {
303 ctx.caller_at_depth(depth - 1)
304 .map(|c| tx_origin == c)
305 .unwrap_or(false)
306 };
307
308 let aliased = is_top_level && ctx.tx_is_aliased();
309 let val = if aliased { U256::from(1) } else { U256::ZERO };
310 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
311 Ok(PrecompileOutput::new(
312 (*gas_used).min(gas_limit),
313 val.to_be_bytes::<32>().to_vec().into(),
314 ))
315}
316
317fn handle_caller_without_alias(
318 input: &mut PrecompileInput<'_>,
319 gas_used: &mut u64,
320 ctx: &ArbPrecompileCtx,
321) -> PrecompileResult {
322 let gas_limit = input.gas;
323 let depth = ctx.evm_depth();
324 let address = if depth > 1 {
325 ctx.caller_at_depth(depth - 1).unwrap_or(Address::ZERO)
326 } else {
327 Address::ZERO
328 };
329
330 let arbos_version = ctx.block.arbos_version;
331 let is_top_level = if arbos_version < 6 {
332 depth == 2
333 } else if depth <= 2 {
334 true
335 } else {
336 let tx_origin = input.internals().tx_origin();
337 ctx.caller_at_depth(depth - 1)
338 .map(|c| tx_origin == c)
339 .unwrap_or(false)
340 };
341 let aliased = is_top_level && ctx.tx_is_aliased();
342 let result_addr = if aliased {
343 undo_l1_alias(address)
344 } else {
345 address
346 };
347
348 let mut out = [0u8; 32];
349 out[12..32].copy_from_slice(result_addr.as_slice());
350 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
351 Ok(PrecompileOutput::new(
352 (*gas_used).min(gas_limit),
353 out.to_vec().into(),
354 ))
355}
356
357fn handle_map_l1_sender(
358 input: &mut PrecompileInput<'_>,
359 gas_used: &mut u64,
360 ctx: &ArbPrecompileCtx,
361 l1_addr: Address,
362) -> PrecompileResult {
363 let aliased = apply_l1_alias(l1_addr);
364 let gas_limit = input.gas;
365 let mut out = [0u8; 32];
366 out[12..32].copy_from_slice(aliased.as_slice());
367 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
370 Ok(PrecompileOutput::new(
371 (*gas_used).min(gas_limit),
372 out.to_vec().into(),
373 ))
374}
375
376fn handle_get_storage_gas(
377 input: &mut PrecompileInput<'_>,
378 gas_used: &mut u64,
379 ctx: &ArbPrecompileCtx,
380) -> PrecompileResult {
381 let gas_limit = input.gas;
382 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(32));
383 Ok(PrecompileOutput::new(
384 (*gas_used).min(gas_limit),
385 U256::ZERO.to_be_bytes::<32>().to_vec().into(),
386 ))
387}
388
389fn handle_withdraw_eth(
392 input: &mut PrecompileInput<'_>,
393 gas_used: &mut u64,
394 ctx: &ArbPrecompileCtx,
395 destination: Address,
396) -> PrecompileResult {
397 if input.is_static {
398 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
399 }
400 do_send_tx_to_l1(input, gas_used, ctx, destination, &[])
401}
402
403fn handle_send_tx_to_l1(
404 input: &mut PrecompileInput<'_>,
405 gas_used: &mut u64,
406 ctx: &ArbPrecompileCtx,
407 destination: Address,
408 calldata: &[u8],
409) -> PrecompileResult {
410 if input.is_static {
411 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
412 }
413 do_send_tx_to_l1(input, gas_used, ctx, destination, calldata)
414}
415
416fn do_send_tx_to_l1(
417 input: &mut PrecompileInput<'_>,
418 gas_used: &mut u64,
419 ctx: &ArbPrecompileCtx,
420 destination: Address,
421 calldata: &[u8],
422) -> PrecompileResult {
423 let caller = input.caller;
424 let value = input.value;
425 let gas_limit = input.gas;
426 let l1_block_number = U256::from(ctx.block.l1_block_number_for_evm);
429 let l2_block_number = U256::from(ctx.block.l2_block_number);
430 let timestamp = input.internals().block_timestamp();
431
432 let internals = input.internals_mut();
433
434 internals
435 .load_account(ARBOS_STATE_ADDRESS)
436 .map_err(ArbPrecompileError::fatal)?;
437
438 let arb_state = ctx
439 .block
440 .arbos_state(internals)
441 .map_err(ArbPrecompileError::fatal)?;
442 let arbos_version = arb_state.arbos_version();
443
444 if !value.is_zero() && arbos_version >= 41 {
446 crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST);
447 let num_owners = arb_state
448 .native_token_owners
449 .size(internals)
450 .map_err(ArbPrecompileError::fatal)?;
451 if num_owners != 0 {
452 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
453 }
454 }
455
456 crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST);
458 let old_size = arb_state
459 .send_merkle_accumulator
460 .size(internals)
461 .map_err(ArbPrecompileError::fatal)?;
462
463 let send_hash_input_len = 20 + 20 + 32 * 4 + calldata.len() as u64;
465 crate::charge_computation(gas_used, ctx, keccak_gas(send_hash_input_len));
466 let send_hash = compute_send_hash(
467 caller,
468 destination,
469 l2_block_number,
470 l1_block_number,
471 timestamp,
472 value,
473 calldata,
474 );
475
476 let merkle_events = arb_state
477 .send_merkle_accumulator
478 .append(internals, send_hash)
479 .map_err(ArbPrecompileError::fatal)?;
480 let new_size = old_size + 1;
481
482 let num_partials_old = calc_num_partials(old_size);
486 let n_events = merkle_events.len() as u64;
487 let per_merge_keccak = keccak_gas(64);
488 crate::charge_storage_read(gas_used, ctx, n_events * STORAGE_READ_COST);
489 crate::charge_computation(gas_used, ctx, n_events * per_merge_keccak);
490 crate::charge_storage_write(gas_used, ctx, n_events * STORAGE_WRITE_ZERO_COST);
491 if n_events == num_partials_old {
492 crate::charge_storage_write(gas_used, ctx, STORAGE_WRITE_COST);
493 } else {
494 crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST);
495 crate::charge_storage_write(gas_used, ctx, STORAGE_WRITE_COST);
496 }
497 crate::charge_storage_write(gas_used, ctx, STORAGE_WRITE_COST); crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST); let update_topic = send_merkle_update_topic();
502 for evt in &merkle_events {
503 let position: U256 = (U256::from(evt.level) << 192) | U256::from(evt.num_leaves);
505 internals.log(Log::new_unchecked(
506 ARBSYS_ADDRESS,
507 vec![
508 update_topic,
509 B256::from(U256::ZERO.to_be_bytes::<32>()),
510 evt.hash,
511 B256::from(position.to_be_bytes::<32>()),
512 ],
513 Default::default(),
514 ));
515 crate::charge_history_growth(gas_used, ctx, LOG_GAS + LOG_TOPIC_GAS * 4);
517 }
518
519 let leaf_num = new_size - 1;
520
521 let l2l1_topic = l2_to_l1_tx_topic();
525 let dest_topic = B256::left_padding_from(destination.as_slice());
526 let hash_topic = B256::from(U256::from_be_bytes(send_hash.0).to_be_bytes::<32>());
527 let position_topic = B256::from(U256::from(leaf_num).to_be_bytes::<32>());
528
529 let mut event_data = Vec::with_capacity(256);
530 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>());
534 event_data.extend_from_slice(&l1_block_number.to_be_bytes::<32>());
535 event_data.extend_from_slice(×tamp.to_be_bytes::<32>());
536 event_data.extend_from_slice(&value.to_be_bytes::<32>());
537 event_data.extend_from_slice(&U256::from(6 * 32).to_be_bytes::<32>());
538 event_data.extend_from_slice(&U256::from(calldata.len()).to_be_bytes::<32>());
539 event_data.extend_from_slice(calldata);
540 let pad = (32 - calldata.len() % 32) % 32;
541 event_data.extend(std::iter::repeat_n(0u8, pad));
542
543 let l2l1_data_len = event_data.len() as u64;
544 internals.log(Log::new_unchecked(
545 ARBSYS_ADDRESS,
546 vec![l2l1_topic, dest_topic, hash_topic, position_topic],
547 event_data.into(),
548 ));
549 crate::charge_history_growth(
550 gas_used,
551 ctx,
552 LOG_GAS + LOG_TOPIC_GAS * 4 + LOG_DATA_GAS * l2l1_data_len,
553 );
554
555 let return_val = if arbos_version >= 4 {
556 U256::from(leaf_num)
557 } else {
558 U256::from_be_bytes(send_hash.0)
559 };
560
561 let output = return_val.to_be_bytes::<32>().to_vec();
562 crate::charge_computation(
563 gas_used,
564 ctx,
565 COPY_GAS * words_for_bytes(output.len() as u64),
566 );
567
568 Ok(PrecompileOutput::new(
569 (*gas_used).min(gas_limit),
570 output.into(),
571 ))
572}
573
574fn handle_send_merkle_tree_state(
575 input: &mut PrecompileInput<'_>,
576 gas_used: &mut u64,
577 ctx: &ArbPrecompileCtx,
578) -> PrecompileResult {
579 if input.caller != Address::ZERO {
581 return Err(ArbPrecompileError::empty_revert(*gas_used).into());
582 }
583 let gas_limit = input.gas;
584 let internals = input.internals_mut();
585
586 internals
587 .load_account(ARBOS_STATE_ADDRESS)
588 .map_err(ArbPrecompileError::fatal)?;
589
590 let arb_state = ctx
591 .block
592 .arbos_state(internals)
593 .map_err(ArbPrecompileError::fatal)?;
594
595 crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST);
596 let size_u64 = arb_state
597 .send_merkle_accumulator
598 .size(internals)
599 .map_err(ArbPrecompileError::fatal)?;
600 let size = U256::from(size_u64);
601
602 let num_partials = calc_num_partials(size_u64);
603 let mut partials = Vec::new();
604 for i in 0..num_partials {
605 crate::charge_storage_read(gas_used, ctx, STORAGE_READ_COST);
606 let val = arb_state
607 .send_merkle_accumulator
608 .partial_at(internals, i)
609 .map_err(ArbPrecompileError::fatal)?;
610 partials.push(val);
611 }
612
613 let root = compute_merkle_root(&partials, size_u64);
614
615 let num_partials = partials.len();
617 let mut out = Vec::with_capacity(96 + num_partials * 32);
618 out.extend_from_slice(&size.to_be_bytes::<32>());
619 out.extend_from_slice(&root.0);
620 out.extend_from_slice(&U256::from(96u64).to_be_bytes::<32>());
621 out.extend_from_slice(&U256::from(num_partials).to_be_bytes::<32>());
622 for p in &partials {
623 out.extend_from_slice(p.0.as_slice());
624 }
625
626 crate::charge_computation(gas_used, ctx, COPY_GAS * words_for_bytes(out.len() as u64));
627 Ok(PrecompileOutput::new(
628 (*gas_used).min(gas_limit),
629 out.into(),
630 ))
631}
632
633fn compute_send_hash(
636 sender: Address,
637 dest: Address,
638 arb_block_num: U256,
639 eth_block_num: U256,
640 timestamp: U256,
641 value: U256,
642 data: &[u8],
643) -> B256 {
644 let mut preimage = Vec::with_capacity(200 + data.len());
646 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>());
649 preimage.extend_from_slice(ð_block_num.to_be_bytes::<32>());
650 preimage.extend_from_slice(×tamp.to_be_bytes::<32>());
651 preimage.extend_from_slice(&value.to_be_bytes::<32>());
652 preimage.extend_from_slice(data);
653 keccak256(&preimage)
654}
655
656fn compute_merkle_root(partials: &[B256], size: u64) -> B256 {
660 if partials.is_empty() || size == 0 {
661 return B256::ZERO;
662 }
663
664 let num_partials = calc_num_partials(size);
665 let mut hash_so_far: Option<B256> = None;
666 let mut capacity_in_hash: u64 = 0;
667 let mut capacity: u64 = 1;
668
669 for level in 0..num_partials {
670 let partial = if (level as usize) < partials.len() {
671 partials[level as usize]
672 } else {
673 B256::ZERO
674 };
675
676 if partial != B256::ZERO {
677 match hash_so_far {
678 None => {
679 hash_so_far = Some(partial);
680 capacity_in_hash = capacity;
681 }
682 Some(ref h) => {
683 let mut current = *h;
685 let mut cap = capacity_in_hash;
686 while cap < capacity {
687 let mut preimage = [0u8; 64];
688 preimage[..32].copy_from_slice(current.as_slice());
689 current = keccak256(preimage);
691 cap *= 2;
692 }
693 let mut preimage = [0u8; 64];
695 preimage[..32].copy_from_slice(partial.as_slice());
696 preimage[32..].copy_from_slice(current.as_slice());
697 let combined = keccak256(preimage);
698 hash_so_far = Some(combined);
699 capacity_in_hash = 2 * capacity;
700 }
701 }
702 }
703 capacity *= 2;
704 }
705
706 hash_so_far.unwrap_or(B256::ZERO)
707}
708
709fn alias_offset_u256() -> U256 {
712 U256::from_be_slice(L1_ALIAS_OFFSET.as_slice())
713}
714
715fn truncate_to_address(v: U256) -> Address {
716 let bytes = v.to_be_bytes::<32>();
717 Address::from_slice(&bytes[12..])
718}
719
720fn apply_l1_alias(addr: Address) -> Address {
721 let val = U256::from_be_slice(addr.as_slice());
722 truncate_to_address(val.wrapping_add(alias_offset_u256()))
723}
724
725fn undo_l1_alias(addr: Address) -> Address {
726 let val = U256::from_be_slice(addr.as_slice());
727 truncate_to_address(val.wrapping_sub(alias_offset_u256()))
728}
729
730#[cfg(test)]
731mod alias_tests {
732 use alloy_primitives::address;
733
734 use super::*;
735
736 #[test]
737 fn alias_simple_no_carry() {
738 let l1 = address!("0000000000000000000000000000000000000000");
739 let aliased = apply_l1_alias(l1);
740 assert_eq!(aliased, L1_ALIAS_OFFSET);
741 assert_eq!(undo_l1_alias(aliased), l1);
742 }
743
744 #[test]
745 fn alias_carry_propagates_across_bytes() {
746 let l1 = address!("00ef000000000000000000000000000000000000");
747 let expected = address!("1200000000000000000000000000000000001111");
748 assert_eq!(apply_l1_alias(l1), expected);
749 assert_eq!(undo_l1_alias(expected), l1);
750 }
751
752 #[test]
753 fn alias_wraps_at_160_bits() {
754 let l1 = address!("ffffffffffffffffffffffffffffffffffffffff");
758 let expected = address!("1111000000000000000000000000000000001110");
759 assert_eq!(apply_l1_alias(l1), expected);
760 assert_eq!(undo_l1_alias(expected), l1);
761 }
762
763 #[test]
764 fn alias_inverse_round_trip() {
765 let cases = [
766 address!("0123456789abcdef0123456789abcdef01234567"),
767 address!("deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"),
768 address!("ffeeffeeffeeffeeffeeffeeffeeffeeffeeffee"),
769 ];
770 for addr in cases {
771 let aliased = apply_l1_alias(addr);
772 let restored = undo_l1_alias(aliased);
773 assert_eq!(restored, addr, "round trip failed for {addr}");
774 }
775 }
776}
777
778#[cfg(test)]
779mod version_tests {
780 use super::*;
781
782 #[test]
783 fn arb_os_version_returns_format_plus_55() {
784 assert_eq!(arbos_version_from_format(U256::from(51)), U256::from(106),);
786 assert_eq!(arbos_version_from_format(U256::from(1)), U256::from(56),);
788 assert_eq!(arbos_version_from_format(U256::ZERO), U256::from(55),);
790 }
791}