1use alloy_primitives::{Address, B256, Bytes, U256, address};
8use arb_storage::{
9 ARBOS_STATE_ADDRESS, Storage, StorageBackedBigUint, StorageBackedBytes,
10 layout::{
11 ADDRESS_TABLE_SUBSPACE, BLOCKHASHES_SUBSPACE, CHAIN_CONFIG_SUBSPACE, CHAIN_OWNER_SUBSPACE,
12 FEATURES_SUBSPACE, L1_PRICING_SUBSPACE, L2_PRICING_SUBSPACE, RETRYABLES_SUBSPACE,
13 SEND_MERKLE_SUBSPACE,
14 },
15 set_account_code, set_account_nonce,
16};
17use arbos::{
18 arbos_state::ArbosState, burn::SystemBurner, l1_pricing, l2_pricing, types::ParsedInitMessage,
19};
20use revm::{Database, database::State};
21use tracing::info;
22
23use crate::error::GenesisError;
24
25const GENESIS_PRECOMPILE_ADDRESSES: [Address; 14] = [
30 address!("0000000000000000000000000000000000000064"), address!("0000000000000000000000000000000000000065"), address!("0000000000000000000000000000000000000066"), address!("0000000000000000000000000000000000000067"), address!("0000000000000000000000000000000000000068"), address!("0000000000000000000000000000000000000069"), address!("000000000000000000000000000000000000006b"), address!("000000000000000000000000000000000000006c"), address!("000000000000000000000000000000000000006d"), address!("000000000000000000000000000000000000006e"), address!("000000000000000000000000000000000000006f"), address!("0000000000000000000000000000000000000070"), address!("00000000000000000000000000000000000000ff"), address!("00000000000000000000000000000000000a4b05"), ];
45
46pub const INITIAL_ARBOS_VERSION: u64 = 10;
49
50pub const DEFAULT_CHAIN_OWNER: Address = address!("0000000000000000000000000000000000000000");
52
53#[derive(Debug, Clone, Copy, Default)]
65pub struct ArbOSInit {
66 pub native_token_supply_management_enabled: bool,
67 pub transaction_filtering_enabled: bool,
68}
69
70pub fn initialize_arbos_state<D: Database>(
71 state: &mut State<D>,
72 init_msg: &ParsedInitMessage,
73 chain_id: u64,
74 target_arbos_version: u64,
75 chain_owner: Address,
76 arbos_init: ArbOSInit,
77) -> Result<(), GenesisError> {
78 let backing = Storage::new(state, B256::ZERO);
79 if backing.get_uint64_by_uint64(0).unwrap_or(0) != 0 {
80 return Err(GenesisError::AlreadyInitialized);
81 }
82
83 info!(
84 target: "arb::genesis",
85 chain_id,
86 target_arbos_version,
87 initial_l1_base_fee = %init_msg.initial_l1_base_fee,
88 "Initializing ArbOS state"
89 );
90
91 set_account_nonce(unsafe { backing.state_mut() }, ARBOS_STATE_ADDRESS, 1);
94
95 backing
97 .set_by_uint64(0, B256::from(U256::from(1u64)))
98 .map_err(|source| GenesisError::StorageWrite {
99 what: "initial version",
100 source,
101 })?;
102
103 StorageBackedBigUint::new(B256::ZERO, 4)
106 .set(unsafe { backing.state_mut() }, U256::from(chain_id))
107 .map_err(|source| GenesisError::StorageWrite {
108 what: "chain id",
109 source,
110 })?;
111
112 for addr in &GENESIS_PRECOMPILE_ADDRESSES {
114 set_account_code(
116 unsafe { backing.state_mut() },
117 *addr,
118 Bytes::from_static(&[0xFE]),
119 );
120 }
121
122 if target_arbos_version >= 2 {
124 let mut hash = B256::ZERO;
125 hash[12..32].copy_from_slice(chain_owner.as_slice());
126 backing
127 .set_by_uint64(3, hash)
128 .map_err(|source| GenesisError::StorageWrite {
129 what: "network fee account",
130 source,
131 })?;
132 }
133
134 if !init_msg.serialized_chain_config.is_empty() {
136 let cc_sto = backing.open_sub_storage(CHAIN_CONFIG_SUBSPACE);
137 let cc_bytes = StorageBackedBytes::new(cc_sto.base_key());
138 cc_bytes
140 .set(
141 unsafe { backing.state_mut() },
142 &init_msg.serialized_chain_config,
143 )
144 .map_err(|source| GenesisError::StorageWrite {
145 what: "chain config",
146 source,
147 })?;
148 }
149
150 let l1_sto = backing.open_sub_storage(L1_PRICING_SUBSPACE);
152 let rewards_recipient = if target_arbos_version >= 2 {
153 chain_owner
154 } else {
155 Address::ZERO
156 };
157 l1_pricing::L1PricingState::initialize(
159 &l1_sto,
160 unsafe { backing.state_mut() },
161 rewards_recipient,
162 init_msg.initial_l1_base_fee,
163 )
164 .map_err(|e| GenesisError::InitSubsystem {
165 subsystem: "L1 pricing",
166 source: e.into(),
167 })?;
168
169 let l2_sto = backing.open_sub_storage(L2_PRICING_SUBSPACE);
171 l2_pricing::L2PricingState::initialize(&l2_sto, unsafe { backing.state_mut() }).map_err(
173 |e| GenesisError::InitSubsystem {
174 subsystem: "L2 pricing",
175 source: e.into(),
176 },
177 )?;
178
179 let ret_sto = backing.open_sub_storage(RETRYABLES_SUBSPACE);
181 arbos::retryables::RetryableState::initialize(&ret_sto).map_err(|e| {
182 GenesisError::InitSubsystem {
183 subsystem: "retryables",
184 source: e.into(),
185 }
186 })?;
187
188 let at_sto = backing.open_sub_storage(ADDRESS_TABLE_SUBSPACE);
190 arbos::address_table::initialize_address_table(&at_sto);
191
192 let co_sto = backing.open_sub_storage(CHAIN_OWNER_SUBSPACE);
194 arbos::address_set::initialize_address_set(&co_sto).map_err(|e| {
195 GenesisError::InitSubsystem {
196 subsystem: "chain owners",
197 source: e.into(),
198 }
199 })?;
200
201 let ma_sto = backing.open_sub_storage(SEND_MERKLE_SUBSPACE);
203 arbos::merkle_accumulator::initialize_merkle_accumulator(&ma_sto);
204
205 let bh_sto = backing.open_sub_storage(BLOCKHASHES_SUBSPACE);
207 arbos::blockhash::initialize_blockhashes(&bh_sto);
208
209 let _feat_sto = backing.open_sub_storage(FEATURES_SUBSPACE);
211
212 let mut arb_state = ArbosState::open(
217 unsafe { backing.state_mut() },
218 SystemBurner::new(None, false),
219 )
220 .expect("open ArbOS state after genesis initial setup");
221
222 arb_state
224 .chain_owners
225 .add(unsafe { backing.state_mut() }, chain_owner)
226 .map_err(|e| GenesisError::InitSubsystem {
227 subsystem: "chain owner",
228 source: e.into(),
229 })?;
230
231 if arbos_init.native_token_supply_management_enabled {
232 arb_state
234 .set_native_token_management_from_time(unsafe { backing.state_mut() }, 1)
235 .map_err(|source| GenesisError::InitSubsystem {
236 subsystem: "native token management",
237 source,
238 })?;
239 }
240 if arbos_init.transaction_filtering_enabled {
241 arb_state
243 .set_transaction_filtering_from_time(unsafe { backing.state_mut() }, 1)
244 .map_err(|source| GenesisError::InitSubsystem {
245 subsystem: "transaction filtering",
246 source,
247 })?;
248 }
249
250 if target_arbos_version > 1 {
251 arb_state
253 .upgrade_arbos_version(unsafe { backing.state_mut() }, target_arbos_version, true)
254 .map_err(|source| GenesisError::Upgrade {
255 target: target_arbos_version,
256 source,
257 })?;
258 }
259
260 info!(
261 target: "arb::genesis",
262 final_version = arb_state.arbos_version(),
263 "ArbOS state initialized"
264 );
265
266 Ok(())
267}
268
269pub fn is_arbos_initialized<D: Database>(state: &mut State<D>) -> bool {
271 let backing = Storage::new(state, B256::ZERO);
272 backing.get_uint64_by_uint64(0).unwrap_or(0) != 0
273}