arb_node/
genesis.rs

1//! ArbOS genesis state initialization.
2//!
3//! Initializes the ArbOS system state in the database when the chain boots.
4//! This runs when the first message (Kind=11, Initialize) is received from
5//! the Nitro 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).
59pub fn initialize_arbos_state<D: Database>(
60    state: &mut State<D>,
61    init_msg: &ParsedInitMessage,
62    chain_id: u64,
63    target_arbos_version: u64,
64    chain_owner: Address,
65) -> Result<(), String> {
66    let state_ptr: *mut State<D> = state as *mut State<D>;
67
68    // Check if already initialized (version != 0 means state exists).
69    let backing = Storage::new(state_ptr, B256::ZERO);
70    if backing.get_uint64_by_uint64(0).unwrap_or(0) != 0 {
71        return Err("ArbOS state already initialized".into());
72    }
73
74    info!(
75        target: "arb::genesis",
76        chain_id,
77        target_arbos_version,
78        initial_l1_base_fee = %init_msg.initial_l1_base_fee,
79        "Initializing ArbOS state"
80    );
81
82    // 0. Set ArbOS state account nonce to 1.
83    set_account_nonce(state, ARBOS_STATE_ADDRESS, 1);
84
85    // 1. Set version to 1 (base version before upgrades).
86    backing
87        .set_by_uint64(0, B256::from(U256::from(1u64)))
88        .map_err(|_| "failed to set initial version")?;
89
90    // 2. Set chain ID.
91    StorageBackedBigUint::new(state_ptr, B256::ZERO, 4)
92        .set(U256::from(chain_id))
93        .map_err(|_| "failed to set chain ID")?;
94
95    // 3. Install precompile code markers for version-0 precompiles only.
96    for addr in &GENESIS_PRECOMPILE_ADDRESSES {
97        set_account_code(state, *addr, Bytes::from_static(&[0xFE]));
98    }
99
100    // 3b. Set network fee account (chain owner for version >= 2).
101    if target_arbos_version >= 2 {
102        let mut hash = B256::ZERO;
103        hash[12..32].copy_from_slice(chain_owner.as_slice());
104        backing
105            .set_by_uint64(3, hash)
106            .map_err(|_| "failed to set network fee account")?;
107    }
108
109    // 3c. Store serialized chain config.
110    if !init_msg.serialized_chain_config.is_empty() {
111        let cc_sto = backing.open_sub_storage(&[7]); // CHAIN_CONFIG_SUBSPACE
112        let cc_bytes = StorageBackedBytes::new(cc_sto);
113        cc_bytes
114            .set(&init_msg.serialized_chain_config)
115            .map_err(|_| "failed to store chain config")?;
116    }
117
118    // 4. Initialize L1 pricing state.
119    let l1_sto = backing.open_sub_storage(&[0]); // L1_PRICING_SUBSPACE
120    let rewards_recipient = if target_arbos_version >= 2 {
121        chain_owner
122    } else {
123        Address::ZERO
124    };
125    l1_pricing::L1PricingState::initialize(
126        &l1_sto,
127        rewards_recipient,
128        init_msg.initial_l1_base_fee,
129    );
130
131    // 5. Initialize L2 pricing state.
132    let l2_sto = backing.open_sub_storage(&[1]); // L2_PRICING_SUBSPACE
133    l2_pricing::L2PricingState::initialize(&l2_sto);
134
135    // 6. Initialize retryable state.
136    let ret_sto = backing.open_sub_storage(&[2]); // RETRYABLES_SUBSPACE
137    arbos::retryables::RetryableState::initialize(&ret_sto)
138        .map_err(|_| "failed to initialize retryable state")?;
139
140    // 7. Initialize address table (no-op but call for consistency).
141    let at_sto = backing.open_sub_storage(&[3]); // ADDRESS_TABLE_SUBSPACE
142    arbos::address_table::initialize_address_table(&at_sto);
143
144    // 8. Initialize chain owners.
145    let co_sto = backing.open_sub_storage(&[4]); // CHAIN_OWNER_SUBSPACE
146    arbos::address_set::initialize_address_set(&co_sto)
147        .map_err(|_| "failed to initialize chain owners")?;
148
149    // 9. Initialize merkle accumulator.
150    let ma_sto = backing.open_sub_storage(&[5]); // SEND_MERKLE_SUBSPACE
151    arbos::merkle_accumulator::initialize_merkle_accumulator(&ma_sto);
152
153    // 10. Initialize blockhashes.
154    let bh_sto = backing.open_sub_storage(&[6]); // BLOCKHASHES_SUBSPACE
155    arbos::blockhash::initialize_blockhashes(&bh_sto);
156
157    // 11. Initialize features.
158    let _feat_sto = backing.open_sub_storage(&[9]); // FEATURES_SUBSPACE
159
160    // Now open ArbOS state and run the upgrade path from v1 to target version.
161    // The open() method reads version from storage (we set it to 1 above).
162    let mut arb_state = ArbosState::open(state_ptr, SystemBurner::new(None, false))
163        .map_err(|_| "failed to open ArbOS state after initial setup")?;
164
165    // Add chain owner.
166    if chain_owner != Address::ZERO {
167        arb_state
168            .chain_owners
169            .add(chain_owner)
170            .map_err(|_| "failed to add chain owner")?;
171    }
172
173    // Run version upgrade from 1 to target (first_time=true).
174    if target_arbos_version > 1 {
175        arb_state
176            .upgrade_arbos_version(target_arbos_version, true)
177            .map_err(|_| format!("failed to upgrade ArbOS to version {target_arbos_version}"))?;
178    }
179
180    info!(
181        target: "arb::genesis",
182        final_version = arb_state.arbos_version(),
183        "ArbOS state initialized"
184    );
185
186    Ok(())
187}
188
189/// Check if ArbOS state is already initialized in the given state database.
190pub fn is_arbos_initialized<D: Database>(state: &mut State<D>) -> bool {
191    let state_ptr: *mut State<D> = state as *mut State<D>;
192    let backing = Storage::new(state_ptr, B256::ZERO);
193    backing.get_uint64_by_uint64(0).unwrap_or(0) != 0
194}