1mod interfaces;
8
9mod arbaddresstable;
10mod arbaggregator;
11mod arbbls;
12mod arbdebug;
13mod arbfilteredtxmanager;
14mod arbfunctiontable;
15mod arbgasinfo;
16mod arbinfo;
17mod arbnativetokenmanager;
18mod arbosacts;
19mod arbostest;
20mod arbowner;
21mod arbownerpublic;
22mod arbretryabletx;
23mod arbstatistics;
24pub mod arbsys;
25mod arbwasm;
26mod arbwasmcache;
27mod nodeinterface;
28mod nodeinterface_debug;
29pub mod storage_slot;
30
31pub use arbaddresstable::{create_arbaddresstable_precompile, ARBADDRESSTABLE_ADDRESS};
32pub use arbaggregator::{create_arbaggregator_precompile, ARBAGGREGATOR_ADDRESS};
33pub use arbbls::{create_arbbls_precompile, ARBBLS_ADDRESS};
34pub use arbdebug::{create_arbdebug_precompile, ARBDEBUG_ADDRESS};
35pub use arbfilteredtxmanager::{
36 create_arbfilteredtxmanager_precompile, ARBFILTEREDTXMANAGER_ADDRESS,
37};
38pub use arbfunctiontable::{create_arbfunctiontable_precompile, ARBFUNCTIONTABLE_ADDRESS};
39pub use arbgasinfo::{create_arbgasinfo_precompile, ARBGASINFO_ADDRESS};
40pub use arbinfo::{create_arbinfo_precompile, ARBINFO_ADDRESS};
41pub use arbnativetokenmanager::{
42 create_arbnativetokenmanager_precompile, ARBNATIVETOKENMANAGER_ADDRESS,
43};
44pub use arbosacts::{create_arbosacts_precompile, ARBOSACTS_ADDRESS};
45pub use arbostest::{create_arbostest_precompile, ARBOSTEST_ADDRESS};
46pub use arbowner::{create_arbowner_precompile, ARBOWNER_ADDRESS};
47pub use arbownerpublic::{create_arbownerpublic_precompile, ARBOWNERPUBLIC_ADDRESS};
48pub use arbretryabletx::{
49 create_arbretryabletx_precompile, redeem_scheduled_topic, ticket_created_topic,
50 ARBRETRYABLETX_ADDRESS,
51};
52pub use arbstatistics::{create_arbstatistics_precompile, ARBSTATISTICS_ADDRESS};
53pub use arbsys::{
54 create_arbsys_precompile, get_cached_l1_block_number, get_current_l2_block, get_tx_is_aliased,
55 set_cached_l1_block_number, set_current_l2_block, set_tx_is_aliased, store_arbsys_state,
56 take_arbsys_state, ArbSysMerkleState, ARBSYS_ADDRESS,
57};
58pub use arbwasm::{create_arbwasm_precompile, ARBWASM_ADDRESS};
59pub use arbwasmcache::{create_arbwasmcache_precompile, ARBWASMCACHE_ADDRESS};
60pub use nodeinterface::{
61 build_fake_tx_bytes, compute_l1_gas_for_estimate, create_nodeinterface_precompile,
62 decode_estimate_args, NODE_INTERFACE_ADDRESS,
63};
64pub use nodeinterface_debug::{
65 create_nodeinterface_debug_precompile, NODE_INTERFACE_DEBUG_ADDRESS,
66};
67pub use storage_slot::ARBOS_STATE_ADDRESS;
68
69use alloy_evm::precompiles::{DynPrecompile, PrecompileInput, PrecompilesMap};
70use revm::precompile::{PrecompileError, PrecompileId, PrecompileOutput, PrecompileResult};
71use std::cell::Cell;
72
73pub const P256VERIFY_ADDRESS: alloy_primitives::Address =
75 alloy_primitives::address!("0000000000000000000000000000000000000100");
76
77const MODEXP_ADDRESS: alloy_primitives::Address =
79 alloy_primitives::address!("0000000000000000000000000000000000000005");
80
81const BLS12_381_ADDRESSES: [alloy_primitives::Address; 7] = [
83 alloy_primitives::address!("000000000000000000000000000000000000000b"),
84 alloy_primitives::address!("000000000000000000000000000000000000000c"),
85 alloy_primitives::address!("000000000000000000000000000000000000000d"),
86 alloy_primitives::address!("000000000000000000000000000000000000000e"),
87 alloy_primitives::address!("000000000000000000000000000000000000000f"),
88 alloy_primitives::address!("0000000000000000000000000000000000000010"),
89 alloy_primitives::address!("0000000000000000000000000000000000000011"),
90];
91
92fn create_p256verify_precompile() -> DynPrecompile {
93 DynPrecompile::new(PrecompileId::P256Verify, |input: PrecompileInput<'_>| {
94 revm::precompile::secp256r1::p256_verify(input.data, input.gas)
95 })
96}
97
98fn create_modexp_osaka_precompile() -> DynPrecompile {
99 DynPrecompile::new(PrecompileId::ModExp, |input: PrecompileInput<'_>| {
100 revm::precompile::modexp::osaka_run(input.data, input.gas)
101 })
102}
103
104static GLOBAL_ARBOS_VERSION: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
109
110thread_local! {
111 static ARBOS_VERSION: Cell<u64> = const { Cell::new(0) };
113 static L1_BLOCK_NUMBER_FOR_EVM: Cell<u64> = const { Cell::new(0) };
115 static EVM_CALL_DEPTH: Cell<usize> = const { Cell::new(0) };
119 static CALLER_STACK: std::cell::RefCell<Vec<alloy_primitives::Address>> =
123 const { std::cell::RefCell::new(Vec::new()) };
124 static BLOCK_TIMESTAMP: Cell<u64> = const { Cell::new(0) };
127 static CURRENT_GAS_BACKLOG: Cell<u64> = const { Cell::new(0) };
130 static PRECOMPILE_GAS_USED: Cell<u64> = const { Cell::new(0) };
132 static CURRENT_TX_POSTER_FEE: Cell<u128> = const { Cell::new(0) };
135 static CURRENT_RETRYABLE_ID: Cell<[u8; 32]> = const { Cell::new([0u8; 32]) };
137 static CURRENT_REDEEMER: Cell<[u8; 32]> = const { Cell::new([0u8; 32]) };
139 static POSTER_BALANCE_CORRECTION: Cell<u128> = const { Cell::new(0) };
144 static TX_SENDER_LO: Cell<u128> = const { Cell::new(0) };
146 static TX_SENDER_HI: Cell<u32> = const { Cell::new(0) };
147 static STYLUS_ACTIVATION_ADDR: Cell<Option<[u8; 20]>> = const { Cell::new(None) };
148 static STYLUS_KEEPALIVE_HASH: Cell<Option<[u8; 32]>> = const { Cell::new(None) };
149 static STYLUS_ACTIVATION_DATA_FEE: Cell<u128> = const { Cell::new(0) };
150}
151
152use std::cell::RefCell;
153
154thread_local! {
155 static PENDING_PRECOMPILE_LOGS: RefCell<Vec<(alloy_primitives::Address, Vec<alloy_primitives::B256>, Vec<u8>)>> = const { RefCell::new(Vec::new()) };
156 static RECENT_WASMS: RefCell<(Vec<alloy_primitives::B256>, usize)> = const { RefCell::new((Vec::new(), 0)) };
159}
160
161pub fn reset_recent_wasms(capacity: usize) {
163 RECENT_WASMS.with(|c| {
164 let mut cache = c.borrow_mut();
165 cache.0.clear();
166 cache.1 = capacity;
167 });
168}
169
170pub fn insert_recent_wasm(hash: alloy_primitives::B256) -> bool {
173 RECENT_WASMS.with(|c| {
174 let mut cache = c.borrow_mut();
175 let was_present = if let Some(pos) = cache.0.iter().position(|h| *h == hash) {
176 cache.0.remove(pos);
177 true
178 } else {
179 false
180 };
181 cache.0.push(hash);
182 let max = cache.1;
183 if max > 0 && cache.0.len() > max {
184 cache.0.remove(0);
185 }
186 was_present
187 })
188}
189
190use std::sync::Mutex as StdMutex;
191
192static L2_BLOCKHASH_CACHE: StdMutex<
196 Option<std::collections::HashMap<u64, alloy_primitives::B256>>,
197> = StdMutex::new(None);
198
199pub fn set_l2_block_hash(l2_block_number: u64, hash: alloy_primitives::B256) {
201 let mut cache = L2_BLOCKHASH_CACHE
202 .lock()
203 .expect("L2 blockhash cache lock poisoned");
204 let map = cache.get_or_insert_with(std::collections::HashMap::new);
205 map.insert(l2_block_number, hash);
206}
207
208pub fn get_l2_block_hash(l2_block_number: u64) -> Option<alloy_primitives::B256> {
210 let cache = L2_BLOCKHASH_CACHE
211 .lock()
212 .expect("L2 blockhash cache lock poisoned");
213 cache.as_ref()?.get(&l2_block_number).copied()
214}
215
216pub fn set_arbos_version(version: u64) {
218 GLOBAL_ARBOS_VERSION.store(version, std::sync::atomic::Ordering::Relaxed);
219 ARBOS_VERSION.with(|v| v.set(version));
220}
221
222pub fn get_arbos_version() -> u64 {
224 let local = ARBOS_VERSION.with(|v| v.get());
225 if local != 0 {
226 return local;
227 }
228 let global = GLOBAL_ARBOS_VERSION.load(std::sync::atomic::Ordering::Relaxed);
229 if global != 0 {
230 ARBOS_VERSION.with(|v| v.set(global));
231 }
232 global
233}
234
235static ALLOW_DEBUG_PRECOMPILES: std::sync::atomic::AtomicBool =
236 std::sync::atomic::AtomicBool::new(false);
237
238pub fn set_allow_debug_precompiles(allow: bool) {
241 ALLOW_DEBUG_PRECOMPILES.store(allow, std::sync::atomic::Ordering::Relaxed);
242}
243
244pub fn allow_debug_precompiles() -> bool {
245 ALLOW_DEBUG_PRECOMPILES.load(std::sync::atomic::Ordering::Relaxed)
246}
247
248pub fn set_l1_block_number_for_evm(number: u64) {
250 L1_BLOCK_NUMBER_FOR_EVM.with(|v| v.set(number));
251}
252
253pub fn get_l1_block_number_for_evm() -> u64 {
255 L1_BLOCK_NUMBER_FOR_EVM.with(|v| v.get())
256}
257
258pub fn set_current_gas_backlog(backlog: u64) {
260 CURRENT_GAS_BACKLOG.with(|v| v.set(backlog));
261}
262
263pub fn get_current_gas_backlog() -> u64 {
265 CURRENT_GAS_BACKLOG.with(|v| v.get())
266}
267
268pub fn reset_precompile_gas() {
269 PRECOMPILE_GAS_USED.with(|v| v.set(0));
270}
271
272pub fn charge_precompile_gas(gas: u64) {
273 PRECOMPILE_GAS_USED.with(|v| v.set(v.get() + gas));
274}
275
276pub fn get_precompile_gas() -> u64 {
277 PRECOMPILE_GAS_USED.with(|v| v.get())
278}
279
280pub fn init_precompile_gas(input_len: usize) {
283 reset_precompile_gas();
284 let args_cost = 3u64 * (input_len as u64).saturating_sub(4).div_ceil(32);
285 charge_precompile_gas(args_cost + 800);
286}
287
288pub fn set_current_tx_poster_fee(fee_wei: u128) {
290 CURRENT_TX_POSTER_FEE.with(|v| v.set(fee_wei));
291}
292
293pub fn get_current_tx_poster_fee() -> u128 {
295 CURRENT_TX_POSTER_FEE.with(|v| v.get())
296}
297
298pub fn set_current_retryable_id(id: alloy_primitives::B256) {
299 CURRENT_RETRYABLE_ID.with(|v| v.set(id.0));
300}
301
302pub fn get_current_retryable_id() -> alloy_primitives::U256 {
303 alloy_primitives::U256::from_be_bytes(CURRENT_RETRYABLE_ID.with(|v| v.get()))
304}
305
306pub fn set_current_redeemer(addr: alloy_primitives::Address) {
307 let padded = alloy_primitives::B256::left_padding_from(addr.as_slice());
308 CURRENT_REDEEMER.with(|v| v.set(padded.0));
309}
310
311pub fn get_current_redeemer() -> alloy_primitives::U256 {
312 alloy_primitives::U256::from_be_bytes(CURRENT_REDEEMER.with(|v| v.get()))
313}
314
315pub fn clear_tx_scratch() {
316 CURRENT_TX_POSTER_FEE.with(|v| v.set(0));
317 CURRENT_RETRYABLE_ID.with(|v| v.set([0u8; 32]));
318 CURRENT_REDEEMER.with(|v| v.set([0u8; 32]));
319}
320
321pub fn set_poster_balance_correction(correction: alloy_primitives::U256) {
323 let val: u128 = correction.try_into().unwrap_or(u128::MAX);
324 POSTER_BALANCE_CORRECTION.with(|v| v.set(val));
325}
326
327pub fn get_poster_balance_correction() -> alloy_primitives::U256 {
329 alloy_primitives::U256::from(POSTER_BALANCE_CORRECTION.with(|v| v.get()))
330}
331
332pub fn set_current_tx_sender(addr: alloy_primitives::Address) {
334 let bytes = addr.as_slice();
335 let lo = u128::from_be_bytes(bytes[4..20].try_into().unwrap_or([0u8; 16]));
336 let hi = u32::from_be_bytes(bytes[0..4].try_into().unwrap_or([0u8; 4]));
337 TX_SENDER_LO.with(|v| v.set(lo));
338 TX_SENDER_HI.with(|v| v.set(hi));
339}
340
341pub fn get_current_tx_sender() -> alloy_primitives::Address {
343 let lo = TX_SENDER_LO.with(|v| v.get());
344 let hi = TX_SENDER_HI.with(|v| v.get());
345 let mut bytes = [0u8; 20];
346 bytes[0..4].copy_from_slice(&hi.to_be_bytes());
347 bytes[4..20].copy_from_slice(&lo.to_be_bytes());
348 alloy_primitives::Address::new(bytes)
349}
350
351pub fn set_evm_depth(depth: usize) {
354 EVM_CALL_DEPTH.with(|v| v.set(depth));
355}
356
357pub fn get_evm_depth() -> usize {
359 EVM_CALL_DEPTH.with(|v| v.get())
360}
361
362pub fn push_caller_frame(caller: alloy_primitives::Address) {
363 CALLER_STACK.with(|s| s.borrow_mut().push(caller));
364}
365
366pub fn pop_caller_frame() {
367 CALLER_STACK.with(|s| {
368 s.borrow_mut().pop();
369 });
370}
371
372pub fn reset_caller_stack() {
373 CALLER_STACK.with(|s| s.borrow_mut().clear());
374}
375
376pub fn caller_at_depth(depth: usize) -> Option<alloy_primitives::Address> {
378 if depth == 0 {
379 return None;
380 }
381 CALLER_STACK.with(|s| s.borrow().get(depth - 1).copied())
382}
383
384pub fn set_block_timestamp(timestamp: u64) {
386 BLOCK_TIMESTAMP.with(|v| v.set(timestamp));
387}
388
389pub fn get_block_timestamp() -> u64 {
391 BLOCK_TIMESTAMP.with(|v| v.get())
392}
393
394pub fn set_stylus_activation_request(addr: Option<alloy_primitives::Address>) {
395 STYLUS_ACTIVATION_ADDR.with(|v| v.set(addr.map(|a| *a.as_ref())));
396}
397
398pub fn take_stylus_activation_request() -> Option<alloy_primitives::Address> {
399 STYLUS_ACTIVATION_ADDR.with(|v| {
400 let val = v.get();
401 v.set(None);
402 val.map(alloy_primitives::Address::from)
403 })
404}
405
406pub fn set_stylus_keepalive_request(hash: Option<alloy_primitives::B256>) {
407 STYLUS_KEEPALIVE_HASH.with(|v| v.set(hash.map(|h| h.0)));
408}
409
410pub fn take_stylus_keepalive_request() -> Option<alloy_primitives::B256> {
411 STYLUS_KEEPALIVE_HASH.with(|v| {
412 let val = v.get();
413 v.set(None);
414 val.map(alloy_primitives::B256::from)
415 })
416}
417
418pub fn set_stylus_activation_data_fee(fee: alloy_primitives::U256) {
419 STYLUS_ACTIVATION_DATA_FEE.with(|v| v.set(fee.try_into().unwrap_or(u128::MAX)));
420}
421
422pub fn take_stylus_activation_data_fee() -> alloy_primitives::U256 {
423 STYLUS_ACTIVATION_DATA_FEE.with(|v| {
424 let val = v.get();
425 v.set(0);
426 alloy_primitives::U256::from(val)
427 })
428}
429
430pub fn emit_log(
431 address: alloy_primitives::Address,
432 topics: &[alloy_primitives::B256],
433 data: &[u8],
434) {
435 PENDING_PRECOMPILE_LOGS.with(|logs| {
436 logs.borrow_mut()
437 .push((address, topics.to_vec(), data.to_vec()));
438 });
439}
440
441pub fn take_pending_precompile_logs() -> Vec<(
442 alloy_primitives::Address,
443 Vec<alloy_primitives::B256>,
444 Vec<u8>,
445)> {
446 PENDING_PRECOMPILE_LOGS.with(|logs| std::mem::take(&mut *logs.borrow_mut()))
447}
448
449fn check_precompile_version(min_version: u64) -> Option<PrecompileResult> {
450 if get_arbos_version() < min_version {
451 Some(Ok(PrecompileOutput::new(0, Default::default())))
452 } else {
453 None
454 }
455}
456
457fn burn_all_revert(gas_limit: u64) -> PrecompileResult {
459 Ok(PrecompileOutput::new_reverted(
460 gas_limit,
461 Default::default(),
462 ))
463}
464
465pub fn sol_error_revert(payload: Vec<u8>, gas_limit: u64) -> PrecompileResult {
468 let result_cost = 3u64 * (payload.len() as u64).div_ceil(32); charge_precompile_gas(result_cost);
470 let gas = get_precompile_gas();
471 Ok(PrecompileOutput::new_reverted(
472 gas.min(gas_limit),
473 payload.into(),
474 ))
475}
476
477fn gas_check(gas_limit: u64, result: PrecompileResult) -> PrecompileResult {
478 let accumulated_gas = get_precompile_gas();
479 reset_precompile_gas();
480 match result {
481 Ok(ref output) if output.gas_used > gas_limit => Err(PrecompileError::OutOfGas),
482 Err(PrecompileError::Other(_)) if get_arbos_version() >= 11 => Ok(
483 PrecompileOutput::new_reverted(accumulated_gas.min(gas_limit), Default::default()),
484 ),
485 other => other,
486 }
487}
488
489fn check_method_version(
493 gas_limit: u64,
494 min_version: u64,
495 max_version: u64,
496) -> Option<PrecompileResult> {
497 let v = get_arbos_version();
498 if v < min_version || (max_version > 0 && v > max_version) {
499 Some(burn_all_revert(gas_limit))
500 } else {
501 None
502 }
503}
504
505const KZG_POINT_EVALUATION_ADDRESS: alloy_primitives::Address =
506 alloy_primitives::address!("000000000000000000000000000000000000000a");
507
508pub fn register_arb_precompiles(map: &mut PrecompilesMap, arbos_version: u64) {
511 map.extend_precompiles([
512 (ARBSYS_ADDRESS, create_arbsys_precompile()),
513 (ARBGASINFO_ADDRESS, create_arbgasinfo_precompile()),
514 (ARBINFO_ADDRESS, create_arbinfo_precompile()),
515 (ARBSTATISTICS_ADDRESS, create_arbstatistics_precompile()),
516 (
517 ARBFUNCTIONTABLE_ADDRESS,
518 create_arbfunctiontable_precompile(),
519 ),
520 (ARBOSACTS_ADDRESS, create_arbosacts_precompile()),
521 (ARBOSTEST_ADDRESS, create_arbostest_precompile()),
522 (ARBOWNERPUBLIC_ADDRESS, create_arbownerpublic_precompile()),
523 (ARBADDRESSTABLE_ADDRESS, create_arbaddresstable_precompile()),
524 (ARBAGGREGATOR_ADDRESS, create_arbaggregator_precompile()),
525 (ARBRETRYABLETX_ADDRESS, create_arbretryabletx_precompile()),
526 (ARBOWNER_ADDRESS, create_arbowner_precompile()),
527 (ARBBLS_ADDRESS, create_arbbls_precompile()),
528 (ARBDEBUG_ADDRESS, create_arbdebug_precompile()),
529 (ARBWASM_ADDRESS, create_arbwasm_precompile()),
530 (ARBWASMCACHE_ADDRESS, create_arbwasmcache_precompile()),
531 (
532 ARBFILTEREDTXMANAGER_ADDRESS,
533 create_arbfilteredtxmanager_precompile(),
534 ),
535 (
536 ARBNATIVETOKENMANAGER_ADDRESS,
537 create_arbnativetokenmanager_precompile(),
538 ),
539 (NODE_INTERFACE_ADDRESS, create_nodeinterface_precompile()),
540 (
541 NODE_INTERFACE_DEBUG_ADDRESS,
542 create_nodeinterface_debug_precompile(),
543 ),
544 ]);
545
546 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_30 {
547 map.extend_precompiles([(P256VERIFY_ADDRESS, create_p256verify_precompile())]);
550 } else {
551 map.apply_precompile(&KZG_POINT_EVALUATION_ADDRESS, |_| None);
552 map.apply_precompile(&P256VERIFY_ADDRESS, |_| None);
553 }
554
555 if arbos_version >= arb_chainspec::arbos_version::ARBOS_VERSION_50 {
556 map.extend_precompiles([(MODEXP_ADDRESS, create_modexp_osaka_precompile())]);
558 } else {
559 for addr in &BLS12_381_ADDRESSES {
561 map.apply_precompile(addr, |_| None);
562 }
563 }
564}