1use alloy_primitives::{Address, B256, U256};
2use arb_storage::{
3 ARBOS_STATE_ADDRESS, Storage, StorageBackedAddress, StorageBackedBigUint, StorageBackend,
4 set_account_nonce,
5};
6use revm::{Database, database::State};
7
8use super::{ArbosState, ArbosStateError};
9use crate::{
10 burn::Burner,
11 l1_pricing::L1PricingState,
12 l2_pricing::L2PricingState,
13 retryables::{self, RetryableState},
14};
15
16#[derive(Debug, Clone)]
18pub struct InitRetryableData {
19 pub id: B256,
20 pub timeout: u64,
21 pub from: Address,
22 pub to: Option<Address>,
23 pub callvalue: U256,
24 pub beneficiary: Address,
25 pub calldata: Vec<u8>,
26}
27
28#[derive(Debug, Clone)]
30pub struct AccountInitInfo {
31 pub addr: Address,
32 pub nonce: u64,
33 pub balance: U256,
34 pub contract_info: Option<ContractInitInfo>,
35 pub aggregator_info: Option<AggregatorInitInfo>,
36}
37
38#[derive(Debug, Clone)]
40pub struct ContractInitInfo {
41 pub code: Vec<u8>,
42 pub storage: Vec<(U256, U256)>,
43}
44
45#[derive(Debug, Clone)]
47pub struct AggregatorInitInfo {
48 pub fee_collector: Address,
49}
50
51#[derive(Debug, Clone)]
57pub struct GenesisBlockInfo {
58 pub parent_hash: B256,
59 pub block_number: u64,
60 pub timestamp: u64,
61 pub state_root: B256,
62 pub gas_limit: u64,
63 pub base_fee: u64,
64 pub nonce: u64,
65 pub arbos_format_version: u64,
66}
67
68pub fn make_genesis_block(
70 parent_hash: B256,
71 block_number: u64,
72 timestamp: u64,
73 state_root: B256,
74 initial_arbos_version: u64,
75) -> GenesisBlockInfo {
76 use crate::l2_pricing;
77
78 GenesisBlockInfo {
79 parent_hash,
80 block_number,
81 timestamp,
82 state_root,
83 gas_limit: l2_pricing::GETH_BLOCK_GAS_LIMIT,
84 base_fee: l2_pricing::INITIAL_BASE_FEE_WEI,
85 nonce: 1, arbos_format_version: initial_arbos_version,
87 }
88}
89
90pub fn initialize_retryables<D: Database, C: StorageBackend>(
100 backend: &mut C,
101 rs: &RetryableState<D>,
102 mut retryables_data: Vec<InitRetryableData>,
103 current_timestamp: u64,
104) -> Result<(Vec<(Address, U256)>, Vec<(Address, U256)>), ArbosStateError> {
105 let mut balance_credits = Vec::new();
106 let mut active_retryables = Vec::new();
107
108 for r in retryables_data.drain(..) {
109 if r.timeout <= current_timestamp {
110 balance_credits.push((r.beneficiary, r.callvalue));
111 continue;
112 }
113 active_retryables.push(r);
114 }
115
116 active_retryables.sort_by(|a, b| a.timeout.cmp(&b.timeout).then_with(|| a.id.cmp(&b.id)));
117
118 let mut escrow_credits = Vec::new();
119
120 for r in &active_retryables {
121 let escrow_addr = retryables::retryable_escrow_address(r.id);
122 escrow_credits.push((escrow_addr, r.callvalue));
123 rs.create_retryable(
124 backend,
125 r.id,
126 r.timeout,
127 r.from,
128 r.to,
129 r.callvalue,
130 r.beneficiary,
131 &r.calldata,
132 )?;
133 }
134
135 Ok((balance_credits, escrow_credits))
136}
137
138pub fn initialize_arbos_account<D: Database, B: Burner, C: StorageBackend>(
143 backend: &mut C,
144 arbos_state: &ArbosState<'_, D, B>,
145 account: &AccountInitInfo,
146) -> Result<(), ArbosStateError> {
147 if let Some(ref aggregator) = account.aggregator_info {
148 let poster_table = arbos_state.l1_pricing_state.batch_poster_table();
149 let is_poster = poster_table.contains_poster(backend, account.addr)?;
150 if is_poster {
151 let poster = poster_table.open_poster(backend, account.addr, false)?;
152 poster.set_pay_to(backend, aggregator.fee_collector)?;
153 }
154 }
155 Ok(())
156}
157
158#[derive(Debug)]
171pub struct GenesisInitResult {
172 pub balance_credits: Vec<(Address, U256)>,
174 pub escrow_credits: Vec<(Address, U256)>,
176 pub accounts: Vec<AccountInitInfo>,
178}
179
180pub fn initialize_arbos_in_database<D: Database, B: Burner, C: StorageBackend>(
186 backend: &mut C,
187 arbos_state: &ArbosState<'_, D, B>,
188 chain_owner: Address,
189 address_table_entries: Vec<Address>,
190 retryable_data: Vec<InitRetryableData>,
191 accounts: Vec<AccountInitInfo>,
192 current_timestamp: u64,
193) -> Result<GenesisInitResult, ArbosStateError> {
194 if chain_owner != Address::ZERO {
195 arbos_state.chain_owners.add(backend, chain_owner)?;
196 }
197
198 let table_size = arbos_state.address_table.size(backend)?;
199 if table_size != 0 {
200 return Err(ArbosStateError::AddressTableNotEmpty);
201 }
202 for (i, addr) in address_table_entries.iter().enumerate() {
203 let (slot, _) = arbos_state.address_table.register(backend, *addr)?;
204 if slot != i as u64 {
205 return Err(ArbosStateError::AddressTableSlotMismatch);
206 }
207 }
208
209 let (balance_credits, escrow_credits) = initialize_retryables(
210 backend,
211 &arbos_state.retryable_state,
212 retryable_data,
213 current_timestamp,
214 )?;
215
216 for account in &accounts {
217 initialize_arbos_account(backend, arbos_state, account)?;
218 }
219
220 Ok(GenesisInitResult {
221 balance_credits,
222 escrow_credits,
223 accounts,
224 })
225}
226
227pub fn bootstrap<'a, D: Database, B: Burner>(
230 state: &'a mut State<D>,
231 chain_id: u64,
232 network_fee_account: Address,
233 infra_fee_account: Address,
234 l1_initial_base_fee: U256,
235 target_arbos_version: u64,
236 burner: B,
237) -> Result<ArbosState<'a, D, B>, ArbosStateError> {
238 set_account_nonce(state, ARBOS_STATE_ADDRESS, 1);
239
240 {
241 let backing = Storage::<D>::new(state, B256::ZERO);
242 backing.set_by_uint64(super::VERSION_OFFSET, B256::from(U256::from(1u64)))?;
243 let s = unsafe { backing.state_mut() };
247 StorageBackedBigUint::new(B256::ZERO, super::CHAIN_ID_OFFSET)
248 .set(s, U256::from(chain_id))?;
249 let s = unsafe { backing.state_mut() };
251 StorageBackedAddress::new(B256::ZERO, super::NETWORK_FEE_ACCOUNT_OFFSET)
252 .set(s, network_fee_account)?;
253 let s = unsafe { backing.state_mut() };
255 StorageBackedAddress::new(B256::ZERO, super::INFRA_FEE_ACCOUNT_OFFSET)
256 .set(s, infra_fee_account)?;
257
258 let l1_sto = backing.open_sub_storage(super::L1_PRICING_SUBSPACE);
259 let s = unsafe { backing.state_mut() };
261 L1PricingState::initialize(&l1_sto, s, network_fee_account, l1_initial_base_fee)?;
262 let l2_sto = backing.open_sub_storage(super::L2_PRICING_SUBSPACE);
263 let s = unsafe { backing.state_mut() };
265 L2PricingState::<D>::initialize(&l2_sto, s)?;
266 RetryableState::<D>::initialize(&backing.open_sub_storage(super::RETRYABLES_SUBSPACE))?;
267 }
268
269 let mut arbos = ArbosState::open(state, burner)?;
270 let s = unsafe { arbos.backing_storage.state_mut() };
272 arbos.upgrade_arbos_version(s, target_arbos_version, true)?;
273 Ok(arbos)
274}