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, arbos_types::ParsedInitMessage, burn::SystemBurner, l1_pricing,
19 l2_pricing,
20};
21use revm::{Database, database::State};
22use tracing::info;
23
24use crate::error::GenesisError;
25
26const GENESIS_PRECOMPILE_ADDRESSES: [Address; 14] = [
31 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"), ];
46
47pub const INITIAL_ARBOS_VERSION: u64 = 10;
50
51pub const DEFAULT_CHAIN_OWNER: Address = address!("0000000000000000000000000000000000000000");
53
54#[derive(Debug, Clone, Copy, Default)]
66pub struct ArbOSInit {
67 pub native_token_supply_management_enabled: bool,
68 pub transaction_filtering_enabled: bool,
69}
70
71pub fn initialize_arbos_state<D: Database>(
72 state: &mut State<D>,
73 init_msg: &ParsedInitMessage,
74 chain_id: u64,
75 target_arbos_version: u64,
76 chain_owner: Address,
77 arbos_init: ArbOSInit,
78) -> Result<(), GenesisError> {
79 let backing = Storage::new(state, B256::ZERO);
80 if backing.get_uint64_by_uint64(0).unwrap_or(0) != 0 {
81 return Err(GenesisError::AlreadyInitialized);
82 }
83
84 info!(
85 target: "arb::genesis",
86 chain_id,
87 target_arbos_version,
88 initial_l1_base_fee = %init_msg.initial_l1_base_fee,
89 "Initializing ArbOS state"
90 );
91
92 set_account_nonce(unsafe { backing.state_mut() }, ARBOS_STATE_ADDRESS, 1);
95
96 backing
98 .set_by_uint64(0, B256::from(U256::from(1u64)))
99 .map_err(|source| GenesisError::StorageWrite {
100 what: "initial version",
101 source,
102 })?;
103
104 StorageBackedBigUint::new(B256::ZERO, 4)
107 .set(unsafe { backing.state_mut() }, U256::from(chain_id))
108 .map_err(|source| GenesisError::StorageWrite {
109 what: "chain id",
110 source,
111 })?;
112
113 for addr in &GENESIS_PRECOMPILE_ADDRESSES {
115 set_account_code(
117 unsafe { backing.state_mut() },
118 *addr,
119 Bytes::from_static(&[0xFE]),
120 );
121 }
122
123 if target_arbos_version >= 2 {
125 let mut hash = B256::ZERO;
126 hash[12..32].copy_from_slice(chain_owner.as_slice());
127 backing
128 .set_by_uint64(3, hash)
129 .map_err(|source| GenesisError::StorageWrite {
130 what: "network fee account",
131 source,
132 })?;
133 }
134
135 if !init_msg.serialized_chain_config.is_empty() {
137 let cc_sto = backing.open_sub_storage(CHAIN_CONFIG_SUBSPACE);
138 let cc_bytes = StorageBackedBytes::new(cc_sto.base_key());
139 cc_bytes
141 .set(
142 unsafe { backing.state_mut() },
143 &init_msg.serialized_chain_config,
144 )
145 .map_err(|source| GenesisError::StorageWrite {
146 what: "chain config",
147 source,
148 })?;
149 }
150
151 let l1_sto = backing.open_sub_storage(L1_PRICING_SUBSPACE);
153 let rewards_recipient = if target_arbos_version >= 2 {
154 chain_owner
155 } else {
156 Address::ZERO
157 };
158 l1_pricing::L1PricingState::initialize(
160 &l1_sto,
161 unsafe { backing.state_mut() },
162 rewards_recipient,
163 init_msg.initial_l1_base_fee,
164 )
165 .map_err(|e| GenesisError::InitSubsystem {
166 subsystem: "L1 pricing",
167 source: e.into(),
168 })?;
169
170 let l2_sto = backing.open_sub_storage(L2_PRICING_SUBSPACE);
172 l2_pricing::L2PricingState::initialize(&l2_sto, unsafe { backing.state_mut() }).map_err(
174 |e| GenesisError::InitSubsystem {
175 subsystem: "L2 pricing",
176 source: e.into(),
177 },
178 )?;
179
180 let ret_sto = backing.open_sub_storage(RETRYABLES_SUBSPACE);
182 arbos::retryables::RetryableState::initialize(&ret_sto).map_err(|e| {
183 GenesisError::InitSubsystem {
184 subsystem: "retryables",
185 source: e.into(),
186 }
187 })?;
188
189 let at_sto = backing.open_sub_storage(ADDRESS_TABLE_SUBSPACE);
191 arbos::address_table::initialize_address_table(&at_sto);
192
193 let co_sto = backing.open_sub_storage(CHAIN_OWNER_SUBSPACE);
195 arbos::address_set::initialize_address_set(&co_sto).map_err(|e| {
196 GenesisError::InitSubsystem {
197 subsystem: "chain owners",
198 source: e.into(),
199 }
200 })?;
201
202 let ma_sto = backing.open_sub_storage(SEND_MERKLE_SUBSPACE);
204 arbos::merkle_accumulator::initialize_merkle_accumulator(&ma_sto);
205
206 let bh_sto = backing.open_sub_storage(BLOCKHASHES_SUBSPACE);
208 arbos::blockhash::initialize_blockhashes(&bh_sto);
209
210 let _feat_sto = backing.open_sub_storage(FEATURES_SUBSPACE);
212
213 let mut arb_state = ArbosState::open(
218 unsafe { backing.state_mut() },
219 SystemBurner::new(None, false),
220 )
221 .expect("open ArbOS state after genesis initial setup");
222
223 arb_state
225 .chain_owners
226 .add(unsafe { backing.state_mut() }, chain_owner)
227 .map_err(|e| GenesisError::InitSubsystem {
228 subsystem: "chain owner",
229 source: e.into(),
230 })?;
231
232 if arbos_init.native_token_supply_management_enabled {
233 arb_state
235 .set_native_token_management_from_time(unsafe { backing.state_mut() }, 1)
236 .map_err(|source| GenesisError::InitSubsystem {
237 subsystem: "native token management",
238 source,
239 })?;
240 }
241 if arbos_init.transaction_filtering_enabled {
242 arb_state
244 .set_transaction_filtering_from_time(unsafe { backing.state_mut() }, 1)
245 .map_err(|source| GenesisError::InitSubsystem {
246 subsystem: "transaction filtering",
247 source,
248 })?;
249 }
250
251 if target_arbos_version > 1 {
252 arb_state
254 .upgrade_arbos_version(unsafe { backing.state_mut() }, target_arbos_version, true)
255 .map_err(|source| GenesisError::Upgrade {
256 target: target_arbos_version,
257 source,
258 })?;
259 }
260
261 info!(
262 target: "arb::genesis",
263 final_version = arb_state.arbos_version(),
264 "ArbOS state initialized"
265 );
266
267 Ok(())
268}
269
270pub fn is_arbos_initialized<D: Database>(state: &mut State<D>) -> bool {
272 let backing = Storage::new(state, B256::ZERO);
273 backing.get_uint64_by_uint64(0).unwrap_or(0) != 0
274}