arb_node/
genesis.rs

1//! ArbOS genesis state initialization.
2//!
3//! Initializes the ArbOS system state in the database when the chain boots.
4//! Runs when the first message (Kind=11, Initialize) is received from the
5//! consensus sidecar.
6
7use alloy_primitives::{address, Address, Bytes, B256, U256};
8use revm::{database::State, Database};
9use tracing::info;
10
11use arb_storage::{
12    set_account_code, set_account_nonce, Storage, StorageBackedBigUint, StorageBackedBytes,
13    ARBOS_STATE_ADDRESS,
14};
15use arbos::{
16    arbos_state::ArbosState, arbos_types::ParsedInitMessage, burn::SystemBurner, l1_pricing,
17    l2_pricing,
18};
19
20/// Precompile addresses that exist at genesis (version 0).
21/// Only these get the `[0xFE]` invalid code marker at init time.
22/// Later precompiles (ArbWasm, ArbWasmCache, etc.) get code when their
23/// ArbOS version is reached during the upgrade path.
24const GENESIS_PRECOMPILE_ADDRESSES: [Address; 14] = [
25    address!("0000000000000000000000000000000000000064"), // ArbSys
26    address!("0000000000000000000000000000000000000065"), // ArbInfo
27    address!("0000000000000000000000000000000000000066"), // ArbAddressTable
28    address!("0000000000000000000000000000000000000067"), // ArbBLS
29    address!("0000000000000000000000000000000000000068"), // ArbFunctionTable
30    address!("0000000000000000000000000000000000000069"), // ArbosTest
31    address!("000000000000000000000000000000000000006b"), // ArbOwnerPublic
32    address!("000000000000000000000000000000000000006c"), // ArbGasInfo
33    address!("000000000000000000000000000000000000006d"), // ArbAggregator
34    address!("000000000000000000000000000000000000006e"), // ArbRetryableTx
35    address!("000000000000000000000000000000000000006f"), // ArbStatistics
36    address!("0000000000000000000000000000000000000070"), // ArbOwner
37    address!("00000000000000000000000000000000000000ff"), // ArbDebug
38    address!("00000000000000000000000000000000000a4b05"), // ArbosActs
39];
40
41/// The initial ArbOS version for Arbitrum Sepolia genesis.
42/// The upgrade_arbos_version path handles stepping through all intermediate versions.
43pub const INITIAL_ARBOS_VERSION: u64 = 10;
44
45/// Default chain owner for Arbitrum Sepolia.
46pub const DEFAULT_CHAIN_OWNER: Address = address!("0000000000000000000000000000000000000000");
47
48/// Initialize ArbOS state in a freshly created database.
49///
50/// This sets up:
51/// - ArbOS version (set to 1, then upgrade to target version)
52/// - All precompile accounts with `[0xFE]` invalid code marker
53/// - L1 pricing state (initial base fee, batch poster table)
54/// - L2 pricing state (base fee, gas pool, speed limit)
55/// - Retryable state, address table, merkle accumulator, blockhashes
56/// - Chain owner and chain config
57///
58/// The `init_msg` comes from parsing the L1 Initialize message (Kind=11).
59#[derive(Debug, Clone, Copy, Default)]
60pub struct ArbOSInit {
61    pub native_token_supply_management_enabled: bool,
62    pub transaction_filtering_enabled: bool,
63}
64
65pub fn initialize_arbos_state<D: Database>(
66    state: &mut State<D>,
67    init_msg: &ParsedInitMessage,
68    chain_id: u64,
69    target_arbos_version: u64,
70    chain_owner: Address,
71    arbos_init: ArbOSInit,
72) -> Result<(), String> {
73    let state_ptr: *mut State<D> = state as *mut State<D>;
74
75    // Check if already initialized (version != 0 means state exists).
76    let backing = Storage::new(state_ptr, B256::ZERO);
77    if backing.get_uint64_by_uint64(0).unwrap_or(0) != 0 {
78        return Err("ArbOS state already initialized".into());
79    }
80
81    info!(
82        target: "arb::genesis",
83        chain_id,
84        target_arbos_version,
85        initial_l1_base_fee = %init_msg.initial_l1_base_fee,
86        "Initializing ArbOS state"
87    );
88
89    // 0. Set ArbOS state account nonce to 1.
90    set_account_nonce(state, ARBOS_STATE_ADDRESS, 1);
91
92    // 1. Set version to 1 (base version before upgrades).
93    backing
94        .set_by_uint64(0, B256::from(U256::from(1u64)))
95        .map_err(|_| "failed to set initial version")?;
96
97    // 2. Set chain ID.
98    StorageBackedBigUint::new(state_ptr, B256::ZERO, 4)
99        .set(U256::from(chain_id))
100        .map_err(|_| "failed to set chain ID")?;
101
102    // 3. Install precompile code markers for version-0 precompiles only.
103    for addr in &GENESIS_PRECOMPILE_ADDRESSES {
104        set_account_code(state, *addr, Bytes::from_static(&[0xFE]));
105    }
106
107    // 3b. Set network fee account (chain owner for version >= 2).
108    if target_arbos_version >= 2 {
109        let mut hash = B256::ZERO;
110        hash[12..32].copy_from_slice(chain_owner.as_slice());
111        backing
112            .set_by_uint64(3, hash)
113            .map_err(|_| "failed to set network fee account")?;
114    }
115
116    // 3c. Store serialized chain config.
117    if !init_msg.serialized_chain_config.is_empty() {
118        let cc_sto = backing.open_sub_storage(&[7]); // CHAIN_CONFIG_SUBSPACE
119        let cc_bytes = StorageBackedBytes::new(cc_sto);
120        cc_bytes
121            .set(&init_msg.serialized_chain_config)
122            .map_err(|_| "failed to store chain config")?;
123    }
124
125    // 4. Initialize L1 pricing state.
126    let l1_sto = backing.open_sub_storage(&[0]); // L1_PRICING_SUBSPACE
127    let rewards_recipient = if target_arbos_version >= 2 {
128        chain_owner
129    } else {
130        Address::ZERO
131    };
132    l1_pricing::L1PricingState::initialize(
133        &l1_sto,
134        rewards_recipient,
135        init_msg.initial_l1_base_fee,
136    );
137
138    // 5. Initialize L2 pricing state.
139    let l2_sto = backing.open_sub_storage(&[1]); // L2_PRICING_SUBSPACE
140    l2_pricing::L2PricingState::initialize(&l2_sto);
141
142    // 6. Initialize retryable state.
143    let ret_sto = backing.open_sub_storage(&[2]); // RETRYABLES_SUBSPACE
144    arbos::retryables::RetryableState::initialize(&ret_sto)
145        .map_err(|_| "failed to initialize retryable state")?;
146
147    // 7. Initialize address table (no-op but call for consistency).
148    let at_sto = backing.open_sub_storage(&[3]); // ADDRESS_TABLE_SUBSPACE
149    arbos::address_table::initialize_address_table(&at_sto);
150
151    // 8. Initialize chain owners.
152    let co_sto = backing.open_sub_storage(&[4]); // CHAIN_OWNER_SUBSPACE
153    arbos::address_set::initialize_address_set(&co_sto)
154        .map_err(|_| "failed to initialize chain owners")?;
155
156    // 9. Initialize merkle accumulator.
157    let ma_sto = backing.open_sub_storage(&[5]); // SEND_MERKLE_SUBSPACE
158    arbos::merkle_accumulator::initialize_merkle_accumulator(&ma_sto);
159
160    // 10. Initialize blockhashes.
161    let bh_sto = backing.open_sub_storage(&[6]); // BLOCKHASHES_SUBSPACE
162    arbos::blockhash::initialize_blockhashes(&bh_sto);
163
164    // 11. Initialize features.
165    let _feat_sto = backing.open_sub_storage(&[9]); // FEATURES_SUBSPACE
166
167    // Now open ArbOS state and run the upgrade path from v1 to target version.
168    // The open() method reads version from storage (we set it to 1 above).
169    let mut arb_state = ArbosState::open(state_ptr, SystemBurner::new(None, false))
170        .map_err(|_| "failed to open ArbOS state after initial setup")?;
171
172    arb_state
173        .chain_owners
174        .add(chain_owner)
175        .map_err(|_| "failed to add chain owner")?;
176
177    if arbos_init.native_token_supply_management_enabled {
178        arb_state
179            .set_native_token_management_from_time(1)
180            .map_err(|_| "failed to set native token enabled from time")?;
181    }
182    if arbos_init.transaction_filtering_enabled {
183        arb_state
184            .set_transaction_filtering_from_time(1)
185            .map_err(|_| "failed to set transaction filtering from time")?;
186    }
187
188    // Run version upgrade from 1 to target (first_time=true).
189    if target_arbos_version > 1 {
190        arb_state
191            .upgrade_arbos_version(target_arbos_version, true)
192            .map_err(|_| format!("failed to upgrade ArbOS to version {target_arbos_version}"))?;
193    }
194
195    info!(
196        target: "arb::genesis",
197        final_version = arb_state.arbos_version(),
198        "ArbOS state initialized"
199    );
200
201    Ok(())
202}
203
204/// Check if ArbOS state is already initialized in the given state database.
205pub fn is_arbos_initialized<D: Database>(state: &mut State<D>) -> bool {
206    let state_ptr: *mut State<D> = state as *mut State<D>;
207    let backing = Storage::new(state_ptr, B256::ZERO);
208    backing.get_uint64_by_uint64(0).unwrap_or(0) != 0
209}