1pub mod initialize;
2
3use alloy_primitives::{Address, Bytes, B256, U256};
4use revm::Database;
5
6use arb_primitives::arbos_versions::{
7 HISTORY_STORAGE_ADDRESS, HISTORY_STORAGE_CODE_ARBITRUM, PRECOMPILE_MIN_ARBOS_VERSIONS,
8};
9use arb_storage::{
10 get_account_balance, set_account_code, set_account_nonce, Storage, StorageBackedAddress,
11 StorageBackedBigUint, StorageBackedBytes, StorageBackedUint64, FILTERED_TX_STATE_ADDRESS,
12};
13
14use crate::{
15 address_set::{self, AddressSet},
16 address_table::{self, AddressTable},
17 blockhash::{self, Blockhashes},
18 burn::Burner,
19 features::{self, Features},
20 filtered_transactions::FilteredTransactionsState,
21 l1_pricing::{self, L1PricingState},
22 l2_pricing::{self, L2PricingState},
23 merkle_accumulator::{self, MerkleAccumulator},
24 programs::Programs,
25 retryables::RetryableState,
26};
27
28const VERSION_OFFSET: u64 = 0;
30const UPGRADE_VERSION_OFFSET: u64 = 1;
31const UPGRADE_TIMESTAMP_OFFSET: u64 = 2;
32const NETWORK_FEE_ACCOUNT_OFFSET: u64 = 3;
33const CHAIN_ID_OFFSET: u64 = 4;
34const GENESIS_BLOCK_NUM_OFFSET: u64 = 5;
35const INFRA_FEE_ACCOUNT_OFFSET: u64 = 6;
36const BROTLI_COMPRESSION_LEVEL_OFFSET: u64 = 7;
37const NATIVE_TOKEN_ENABLED_FROM_TIME_OFFSET: u64 = 8;
38const TRANSACTION_FILTERING_ENABLED_FROM_TIME_OFFSET: u64 = 9;
39const FILTERED_FUNDS_RECIPIENT_OFFSET: u64 = 10;
40
41const L1_PRICING_SUBSPACE: &[u8] = &[0];
43const L2_PRICING_SUBSPACE: &[u8] = &[1];
44const RETRYABLES_SUBSPACE: &[u8] = &[2];
45const ADDRESS_TABLE_SUBSPACE: &[u8] = &[3];
46const CHAIN_OWNER_SUBSPACE: &[u8] = &[4];
47const SEND_MERKLE_SUBSPACE: &[u8] = &[5];
48const BLOCKHASHES_SUBSPACE: &[u8] = &[6];
49const CHAIN_CONFIG_SUBSPACE: &[u8] = &[7];
50const PROGRAMS_SUBSPACE: &[u8] = &[8];
51const FEATURES_SUBSPACE: &[u8] = &[9];
52const NATIVE_TOKEN_OWNER_SUBSPACE: &[u8] = &[10];
53const TRANSACTION_FILTERING_SUBSPACE: &[u8] = &[11];
54
55pub const MAX_ARBOS_VERSION_SUPPORTED: u64 = 60;
57
58pub struct ArbosState<D, B: Burner> {
60 pub arbos_version: u64,
61 pub max_arbos_version_supported: u64,
62 pub upgrade_version: StorageBackedUint64<D>,
63 pub upgrade_timestamp: StorageBackedUint64<D>,
64 pub network_fee_account: StorageBackedAddress<D>,
65 pub l1_pricing_state: L1PricingState<D>,
66 pub l2_pricing_state: L2PricingState<D>,
67 pub retryable_state: RetryableState<D>,
68 pub address_table: AddressTable<D>,
69 pub chain_owners: AddressSet<D>,
70 pub send_merkle_accumulator: MerkleAccumulator<D>,
71 pub programs: Programs<D>,
72 pub blockhashes: Blockhashes<D>,
73 pub chain_id: StorageBackedBigUint<D>,
74 pub chain_config: StorageBackedBytes<D>,
75 pub genesis_block_num: StorageBackedUint64<D>,
76 pub infra_fee_account: StorageBackedAddress<D>,
77 pub brotli_compression_level: StorageBackedUint64<D>,
78 pub backing_storage: Storage<D>,
79 pub burner: B,
80 pub native_token_enabled_from_time: StorageBackedUint64<D>,
81 pub native_token_owners: AddressSet<D>,
82 pub transaction_filtering_enabled_from_time: StorageBackedUint64<D>,
83 pub transaction_filterers: AddressSet<D>,
84 pub features: Features<D>,
85 pub filtered_funds_recipient: StorageBackedAddress<D>,
86 pub filtered_transactions: FilteredTransactionsState<D>,
87}
88
89impl<D: Database, B: Burner> ArbosState<D, B> {
90 pub fn open(state: *mut revm::database::State<D>, burner: B) -> Result<Self, ()> {
92 let backing_storage = Storage::new(state, B256::ZERO);
93
94 let arbos_version = backing_storage.get_uint64_by_uint64(VERSION_OFFSET)?;
95 if arbos_version == 0 {
96 return Err(());
97 }
98
99 let chain_config_sto = backing_storage.open_sub_storage(CHAIN_CONFIG_SUBSPACE);
100 let features_sto = backing_storage.open_sub_storage(FEATURES_SUBSPACE);
101
102 Ok(Self {
103 arbos_version,
104 max_arbos_version_supported: MAX_ARBOS_VERSION_SUPPORTED,
105 upgrade_version: StorageBackedUint64::new(state, B256::ZERO, UPGRADE_VERSION_OFFSET),
106 upgrade_timestamp: StorageBackedUint64::new(
107 state,
108 B256::ZERO,
109 UPGRADE_TIMESTAMP_OFFSET,
110 ),
111 network_fee_account: StorageBackedAddress::new(
112 state,
113 B256::ZERO,
114 NETWORK_FEE_ACCOUNT_OFFSET,
115 ),
116 l1_pricing_state: L1PricingState::open(
117 backing_storage.open_sub_storage(L1_PRICING_SUBSPACE),
118 arbos_version,
119 ),
120 l2_pricing_state: L2PricingState::open(
121 backing_storage.open_sub_storage(L2_PRICING_SUBSPACE),
122 arbos_version,
123 ),
124 retryable_state: RetryableState::open(
125 backing_storage.open_sub_storage(RETRYABLES_SUBSPACE),
126 ),
127 address_table: address_table::open_address_table(
128 backing_storage.open_sub_storage(ADDRESS_TABLE_SUBSPACE),
129 ),
130 chain_owners: address_set::open_address_set(
131 backing_storage.open_sub_storage(CHAIN_OWNER_SUBSPACE),
132 ),
133 send_merkle_accumulator: merkle_accumulator::open_merkle_accumulator(
134 backing_storage.open_sub_storage(SEND_MERKLE_SUBSPACE),
135 ),
136 programs: Programs::open(
137 arbos_version,
138 backing_storage.open_sub_storage(PROGRAMS_SUBSPACE),
139 ),
140 blockhashes: blockhash::open_blockhashes(
141 backing_storage.open_sub_storage(BLOCKHASHES_SUBSPACE),
142 ),
143 chain_id: StorageBackedBigUint::new(state, B256::ZERO, CHAIN_ID_OFFSET),
144 chain_config: StorageBackedBytes::new(chain_config_sto),
145 genesis_block_num: StorageBackedUint64::new(
146 state,
147 B256::ZERO,
148 GENESIS_BLOCK_NUM_OFFSET,
149 ),
150 infra_fee_account: StorageBackedAddress::new(
151 state,
152 B256::ZERO,
153 INFRA_FEE_ACCOUNT_OFFSET,
154 ),
155 brotli_compression_level: StorageBackedUint64::new(
156 state,
157 B256::ZERO,
158 BROTLI_COMPRESSION_LEVEL_OFFSET,
159 ),
160 native_token_enabled_from_time: StorageBackedUint64::new(
161 state,
162 B256::ZERO,
163 NATIVE_TOKEN_ENABLED_FROM_TIME_OFFSET,
164 ),
165 native_token_owners: address_set::open_address_set(
166 backing_storage.open_sub_storage(NATIVE_TOKEN_OWNER_SUBSPACE),
167 ),
168 transaction_filtering_enabled_from_time: StorageBackedUint64::new(
169 state,
170 B256::ZERO,
171 TRANSACTION_FILTERING_ENABLED_FROM_TIME_OFFSET,
172 ),
173 transaction_filterers: address_set::open_address_set(
174 backing_storage.open_sub_storage(TRANSACTION_FILTERING_SUBSPACE),
175 ),
176 features: features::open_features(state, features_sto.base_key(), 0),
177 filtered_funds_recipient: StorageBackedAddress::new(
178 state,
179 B256::ZERO,
180 FILTERED_FUNDS_RECIPIENT_OFFSET,
181 ),
182 filtered_transactions: FilteredTransactionsState::open(Storage::new_with_account(
183 state,
184 B256::ZERO,
185 FILTERED_TX_STATE_ADDRESS,
186 )),
187 backing_storage,
188 burner,
189 })
190 }
191
192 pub fn arbos_version(&self) -> u64 {
195 self.arbos_version
196 }
197
198 pub fn backing_storage(&self) -> &Storage<D> {
199 &self.backing_storage
200 }
201
202 pub fn set_format_version(&mut self, version: u64) -> Result<(), ()> {
203 self.arbos_version = version;
204 self.backing_storage
205 .set_by_uint64(VERSION_OFFSET, B256::from(U256::from(version)))
206 }
207
208 pub fn brotli_compression_level(&self) -> Result<u64, ()> {
209 self.brotli_compression_level.get()
210 }
211
212 pub fn set_brotli_compression_level(&self, level: u64) -> Result<(), ()> {
213 self.brotli_compression_level.set(level)
214 }
215
216 pub fn chain_id(&self) -> Result<U256, ()> {
217 self.chain_id.get()
218 }
219
220 pub fn chain_config(&self) -> Result<Vec<u8>, ()> {
221 self.chain_config.get()
222 }
223
224 pub fn set_chain_config(&self, config: &[u8]) -> Result<(), ()> {
225 self.chain_config.set(config)
226 }
227
228 pub fn genesis_block_num(&self) -> Result<u64, ()> {
229 self.genesis_block_num.get()
230 }
231
232 pub fn network_fee_account(&self) -> Result<Address, ()> {
233 self.network_fee_account.get()
234 }
235
236 pub fn set_network_fee_account(&self, account: Address) -> Result<(), ()> {
237 self.network_fee_account.set(account)
238 }
239
240 pub fn infra_fee_account(&self) -> Result<Address, ()> {
241 self.infra_fee_account.get()
242 }
243
244 pub fn set_infra_fee_account(&self, account: Address) -> Result<(), ()> {
245 self.infra_fee_account.set(account)
246 }
247
248 pub fn filtered_funds_recipient(&self) -> Result<Address, ()> {
249 self.filtered_funds_recipient.get()
250 }
251
252 pub fn filtered_funds_recipient_or_default(&self) -> Result<Address, ()> {
253 let addr = self.filtered_funds_recipient.get()?;
254 if addr == Address::ZERO {
255 self.network_fee_account()
256 } else {
257 Ok(addr)
258 }
259 }
260
261 pub fn set_filtered_funds_recipient(&self, addr: Address) -> Result<(), ()> {
262 self.filtered_funds_recipient.set(addr)
263 }
264
265 pub fn native_token_management_from_time(&self) -> Result<u64, ()> {
266 self.native_token_enabled_from_time.get()
267 }
268
269 pub fn set_native_token_management_from_time(&self, time: u64) -> Result<(), ()> {
270 self.native_token_enabled_from_time.set(time)
271 }
272
273 pub fn transaction_filtering_from_time(&self) -> Result<u64, ()> {
274 self.transaction_filtering_enabled_from_time.get()
275 }
276
277 pub fn set_transaction_filtering_from_time(&self, time: u64) -> Result<(), ()> {
278 self.transaction_filtering_enabled_from_time.set(time)
279 }
280
281 pub fn get_scheduled_upgrade(&self) -> Result<(u64, u64), ()> {
282 let version = self.upgrade_version.get()?;
283 let timestamp = self.upgrade_timestamp.get()?;
284 Ok((version, timestamp))
285 }
286
287 pub fn schedule_arbos_upgrade(&self, version: u64, timestamp: u64) -> Result<(), ()> {
288 self.upgrade_version.set(version)?;
289 self.upgrade_timestamp.set(timestamp)
290 }
291
292 pub fn upgrade_arbos_version_if_necessary(&mut self, current_timestamp: u64) -> Result<(), ()> {
294 let scheduled_version = self.upgrade_version.get()?;
295 let scheduled_timestamp = self.upgrade_timestamp.get()?;
296
297 if scheduled_version == 0
299 || self.arbos_version >= scheduled_version
300 || current_timestamp < scheduled_timestamp
301 {
302 return Ok(());
303 }
304
305 if scheduled_version > MAX_ARBOS_VERSION_SUPPORTED {
306 return Err(());
307 }
308
309 let old_version = self.arbos_version;
310 self.upgrade_arbos_version(scheduled_version, false)?;
311
312 if old_version != self.arbos_version {
317 tracing::info!(
318 old_version,
319 new_version = self.arbos_version,
320 "ArbOS version upgraded"
321 );
322 }
323
324 Ok(())
325 }
326
327 pub fn upgrade_arbos_version(&mut self, upgrade_to: u64, first_time: bool) -> Result<(), ()> {
332 while self.arbos_version < upgrade_to {
333 let next = self.arbos_version + 1;
334
335 match next {
336 2 => {
337 self.l1_pricing_state.set_last_surplus(U256::ZERO, false)?;
338 }
339 3 => {
340 self.l1_pricing_state.set_per_batch_gas_cost(0)?;
341 self.l1_pricing_state
342 .set_amortized_cost_cap_bips(u64::MAX)?;
343 }
344 4..=9 => {
345 }
347 10 => {
348 let state = unsafe { &mut *self.backing_storage.state };
349 let pool_balance =
350 get_account_balance(state, l1_pricing::L1_PRICER_FUNDS_POOL_ADDRESS);
351 self.l1_pricing_state.set_l1_fees_available(pool_balance)?;
352 }
353 11 => {
354 self.l1_pricing_state
355 .set_per_batch_gas_cost(l1_pricing::INITIAL_PER_BATCH_GAS_COST_V12)?;
356
357 let old_cap = self.l1_pricing_state.amortized_cost_cap_bips()?;
360 if old_cap == u64::MAX {
361 self.l1_pricing_state.set_amortized_cost_cap_bips(0)?;
362 }
363
364 if !first_time {
365 self.chain_owners.clear_list()?;
366 }
367 }
368 12..=19 => {}
370 20 => {
371 self.set_brotli_compression_level(1)?;
372 }
373 21..=29 => {}
375 30 => {
376 Programs::initialize(
377 next,
378 &self.backing_storage.open_sub_storage(PROGRAMS_SUBSPACE),
379 );
380 }
381 31 => {
382 let mut params = self.programs.params()?;
383 params.upgrade_to_version(2).map_err(|_| ())?;
384 params
385 .save(&self.programs.backing_storage.open_sub_storage(&[0]))
386 .map_err(|_| ())?;
387 }
388 32 => {
389 }
391 33..=39 => {}
393 40 => {
394 let state = unsafe { &mut *self.backing_storage.state };
396 set_account_nonce(state, HISTORY_STORAGE_ADDRESS, 1);
397 set_account_code(
398 state,
399 HISTORY_STORAGE_ADDRESS,
400 HISTORY_STORAGE_CODE_ARBITRUM.clone(),
401 );
402 let mut params = self.programs.params()?;
404 params.upgrade_to_arbos_version(next).map_err(|_| ())?;
405 params
406 .save(&self.programs.backing_storage.open_sub_storage(&[0]))
407 .map_err(|_| ())?;
408 }
409 41 => {
410 }
412 42..=49 => {}
414 50 => {
415 let mut params = self.programs.params()?;
416 params.upgrade_to_arbos_version(next).map_err(|_| ())?;
417 params
418 .save(&self.programs.backing_storage.open_sub_storage(&[0]))
419 .map_err(|_| ())?;
420 self.l2_pricing_state
421 .set_max_per_tx_gas_limit(l2_pricing::INITIAL_PER_TX_GAS_LIMIT_V50)?;
422 }
423 51 => {
424 }
426 52..=59 => {}
428 60 => {
429 let mut params = self.programs.params()?;
430 params.upgrade_to_arbos_version(next).map_err(|_| ())?;
431 params
432 .save(&self.programs.backing_storage.open_sub_storage(&[0]))
433 .map_err(|_| ())?;
434 crate::address_set::initialize_address_set(
436 &self
437 .backing_storage
438 .open_sub_storage(TRANSACTION_FILTERING_SUBSPACE),
439 )?;
440 }
441 _ => {
442 tracing::error!(version = next, "unsupported ArbOS version");
443 return Err(());
444 }
445 }
446
447 for &(addr, version) in PRECOMPILE_MIN_ARBOS_VERSIONS {
449 if version == next {
450 let state = unsafe { &mut *self.backing_storage.state };
451 set_account_code(state, addr, Bytes::from_static(&[0xFE])); }
453 }
454
455 self.arbos_version = next;
456 self.programs.arbos_version = next;
457 self.l1_pricing_state.arbos_version = next;
458 self.l2_pricing_state.arbos_version = next;
459 }
460
461 if first_time && upgrade_to >= 6 {
463 if upgrade_to < 11 {
464 self.l1_pricing_state
465 .set_per_batch_gas_cost(l1_pricing::INITIAL_PER_BATCH_GAS_COST_V6)?;
466 }
467 self.l1_pricing_state
468 .set_equilibration_units(U256::from(l1_pricing::INITIAL_EQUILIBRATION_UNITS_V6))?;
469 self.l2_pricing_state
470 .set_speed_limit_per_second(l2_pricing::INITIAL_SPEED_LIMIT_PER_SECOND_V6)?;
471 self.l2_pricing_state
472 .set_max_per_block_gas_limit(l2_pricing::INITIAL_PER_BLOCK_GAS_LIMIT_V6)?;
473 }
474
475 self.set_format_version(self.arbos_version)?;
477
478 Ok(())
479 }
480}