1use alloy_primitives::U256;
2use arb_chainspec::arbos_version as version;
3use arb_primitives::multigas::{MultiGas, NUM_RESOURCE_KIND, ResourceKind};
4use arb_storage::{StorageBackend, SystemStateBackend};
5use revm::Database;
6
7use super::{L2PricingError, L2PricingState};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum GasModel {
12 Unknown,
13 Legacy,
14 SingleGasConstraints,
15 MultiGasConstraints,
16}
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20pub enum BacklogOperation {
21 Shrink,
22 Grow,
23}
24
25pub const MULTI_CONSTRAINT_STATIC_BACKLOG_UPDATE_COST: u64 = 20_800;
28
29impl<D: Database> L2PricingState<'_, D> {
30 pub fn gas_model_to_use<B: SystemStateBackend>(
32 &self,
33 backend: &mut B,
34 ) -> Result<GasModel, L2PricingError> {
35 if self.arbos_version >= version::ARBOS_VERSION_60 {
36 let mgc_len = self.multi_gas_constraints_length(backend)?;
37 if mgc_len > 0 {
38 return Ok(GasModel::MultiGasConstraints);
39 }
40 }
41 if self.arbos_version >= version::ARBOS_VERSION_50 {
42 let gc_len = self.gas_constraints_length(backend)?;
43 if gc_len > 0 {
44 return Ok(GasModel::SingleGasConstraints);
45 }
46 }
47 Ok(GasModel::Legacy)
48 }
49
50 pub fn grow_backlog<B: StorageBackend>(
52 &self,
53 backend: &mut B,
54 used_gas: u64,
55 used_multi_gas: MultiGas,
56 ) -> Result<(), L2PricingError> {
57 self.update_backlog(backend, BacklogOperation::Grow, used_gas, used_multi_gas)
58 }
59
60 pub fn shrink_backlog<B: StorageBackend>(
62 &self,
63 backend: &mut B,
64 used_gas: u64,
65 used_multi_gas: MultiGas,
66 ) -> Result<(), L2PricingError> {
67 self.update_backlog(backend, BacklogOperation::Shrink, used_gas, used_multi_gas)
68 }
69
70 fn update_backlog<B: StorageBackend>(
72 &self,
73 backend: &mut B,
74 op: BacklogOperation,
75 used_gas: u64,
76 used_multi_gas: MultiGas,
77 ) -> Result<(), L2PricingError> {
78 match self.gas_model_to_use(backend)? {
79 GasModel::Legacy | GasModel::Unknown => {
80 self.update_legacy_backlog_op(backend, op, used_gas)
81 }
82 GasModel::SingleGasConstraints => {
83 self.update_single_gas_constraints_backlogs_op(backend, op, used_gas)
84 }
85 GasModel::MultiGasConstraints => {
86 self.update_multi_gas_constraints_backlogs_op(backend, op, used_multi_gas)
87 }
88 }
89 }
90
91 fn update_legacy_backlog_op<B: StorageBackend>(
92 &self,
93 backend: &mut B,
94 op: BacklogOperation,
95 gas: u64,
96 ) -> Result<(), L2PricingError> {
97 let backlog = self.gas_backlog(backend)?;
98 let new_backlog = apply_gas_delta_op(op, backlog, gas);
99 self.set_gas_backlog(backend, new_backlog)
100 }
101
102 fn update_single_gas_constraints_backlogs_op<B: StorageBackend>(
103 &self,
104 backend: &mut B,
105 op: BacklogOperation,
106 gas: u64,
107 ) -> Result<(), L2PricingError> {
108 let len = self.gas_constraints_length(backend)?;
109 for i in 0..len {
110 let c = self.open_gas_constraint_at(i);
111 let backlog = c.backlog(backend)?;
112 c.set_backlog(backend, apply_gas_delta_op(op, backlog, gas))?;
113 }
114 Ok(())
115 }
116
117 fn update_multi_gas_constraints_backlogs_op<B: StorageBackend>(
118 &self,
119 backend: &mut B,
120 op: BacklogOperation,
121 multi_gas: MultiGas,
122 ) -> Result<(), L2PricingError> {
123 let len = self.multi_gas_constraints_length(backend)?;
124 for i in 0..len {
125 let c = self.open_multi_gas_constraint_at(i);
126 match op {
127 BacklogOperation::Grow => c.grow_backlog(backend, multi_gas)?,
128 BacklogOperation::Shrink => c.shrink_backlog(backend, multi_gas)?,
129 }
130 }
131 Ok(())
132 }
133
134 pub fn update_pricing_model<B: StorageBackend>(
136 &self,
137 backend: &mut B,
138 time_passed: u64,
139 arbos_version: u64,
140 ) -> Result<(), L2PricingError> {
141 let _ = arbos_version;
142 match self.gas_model_to_use(backend)? {
143 GasModel::Legacy | GasModel::Unknown => {
144 self.update_pricing_model_legacy(backend, time_passed)
145 }
146 GasModel::SingleGasConstraints => {
147 self.update_pricing_model_single_constraints(backend, time_passed)
148 }
149 GasModel::MultiGasConstraints => {
150 self.update_pricing_model_multi_constraints(backend, time_passed)
151 }
152 }
153 }
154
155 fn update_pricing_model_legacy<B: StorageBackend>(
156 &self,
157 backend: &mut B,
158 time_passed: u64,
159 ) -> Result<(), L2PricingError> {
160 let speed_limit = self.speed_limit_per_second(backend)?;
161 let drain = time_passed.saturating_mul(speed_limit);
162 self.update_legacy_backlog_op(backend, BacklogOperation::Shrink, drain)?;
163
164 let inertia = self.pricing_inertia(backend)?;
165 let tolerance = self.backlog_tolerance(backend)?;
166 let backlog = self.gas_backlog(backend)?;
167 let min_base_fee = self.min_base_fee_wei(backend)?;
168
169 let tolerance_limit = tolerance.wrapping_mul(speed_limit);
170 let base_fee = if backlog > tolerance_limit {
171 let divisor = saturating_cast_to_i64(inertia.saturating_mul(speed_limit));
172 if divisor == 0 {
173 return self.set_base_fee_wei(backend, min_base_fee);
174 }
175 let excess = saturating_cast_to_i64(backlog.wrapping_sub(tolerance_limit));
176 let exponent_bips = natural_to_bips(excess) / divisor;
177 self.calc_base_fee_from_exponent(backend, exponent_bips.max(0) as u64)?
178 } else {
179 min_base_fee
180 };
181
182 self.set_base_fee_wei(backend, base_fee)
183 }
184
185 fn update_pricing_model_single_constraints<B: StorageBackend>(
186 &self,
187 backend: &mut B,
188 time_passed: u64,
189 ) -> Result<(), L2PricingError> {
190 let mut total_exponent: i64 = 0;
191 let len = self.gas_constraints_length(backend)?;
192
193 for i in 0..len {
194 let c = self.open_gas_constraint_at(i);
195 let target = c.target(backend)?;
196
197 let backlog = c.backlog(backend)?;
198 let gas = time_passed.saturating_mul(target);
199 let new_backlog = backlog.saturating_sub(gas);
200 c.set_backlog(backend, new_backlog)?;
201
202 if new_backlog > 0 {
203 let window = c.adjustment_window(backend)?;
204 let divisor = saturating_cast_to_i64(window.saturating_mul(target));
205 if divisor != 0 {
206 let exponent = natural_to_bips(saturating_cast_to_i64(new_backlog)) / divisor;
207 total_exponent = total_exponent.saturating_add(exponent);
208 }
209 }
210 }
211
212 let base_fee = self.calc_base_fee_from_exponent(backend, total_exponent.max(0) as u64)?;
213 self.set_base_fee_wei(backend, base_fee)
214 }
215
216 fn update_pricing_model_multi_constraints<B: StorageBackend>(
217 &self,
218 backend: &mut B,
219 time_passed: u64,
220 ) -> Result<(), L2PricingError> {
221 self.update_multi_gas_constraints_backlogs(backend, time_passed)?;
222
223 let exponent_per_kind = self.calc_multi_gas_constraints_exponents(backend)?;
224
225 let mut max_base_fee = self.min_base_fee_wei(backend)?;
226 let fees = &self.multi_gas_base_fees;
227
228 for (i, &exp) in exponent_per_kind.iter().enumerate() {
229 let base_fee = self.calc_base_fee_from_exponent(backend, exp)?;
230 if let Some(kind) = ResourceKind::from_u8(i as u8) {
231 let mgf = super::multi_gas_fees::open_multi_gas_fees(fees.clone());
232 mgf.set_next_block_fee(backend, kind, base_fee)?;
233 }
234 if base_fee > max_base_fee {
235 max_base_fee = base_fee;
236 }
237 }
238
239 self.set_base_fee_wei(backend, max_base_fee)
240 }
241
242 fn update_multi_gas_constraints_backlogs<B: StorageBackend>(
243 &self,
244 backend: &mut B,
245 time_passed: u64,
246 ) -> Result<(), L2PricingError> {
247 let len = self.multi_gas_constraints_length(backend)?;
248 for i in 0..len {
249 let c = self.open_multi_gas_constraint_at(i);
250 let target = c.target(backend)?;
251 let backlog = c.backlog(backend)?;
252 let gas = time_passed.saturating_mul(target);
253 let new_backlog = backlog.saturating_sub(gas);
254 c.set_backlog(backend, new_backlog)?;
255 }
256 Ok(())
257 }
258
259 pub fn calc_multi_gas_constraints_exponents<B: StorageBackend>(
261 &self,
262 backend: &mut B,
263 ) -> Result<[u64; NUM_RESOURCE_KIND], L2PricingError> {
264 let len = self.multi_gas_constraints_length(backend)?;
265 let mut exponent_per_kind = [0i64; NUM_RESOURCE_KIND];
266
267 for i in 0..len {
268 let c = self.open_multi_gas_constraint_at(i);
269 let target = c.target(backend)?;
270 let backlog = c.backlog(backend)?;
271
272 if backlog == 0 {
273 continue;
274 }
275
276 let window = c.adjustment_window(backend)?;
277 let max_weight = c.max_weight(backend)?;
278
279 if target == 0 || window == 0 || max_weight == 0 {
280 continue;
281 }
282
283 let divisor_u64 = (window as u64).saturating_mul(target.saturating_mul(max_weight));
284 let divisor = saturating_cast_to_i64(divisor_u64);
285 if divisor == 0 {
286 continue;
287 }
288
289 for kind in ResourceKind::ALL {
290 if kind == ResourceKind::SingleDim {
291 continue;
292 }
293 let weight = c.resource_weight(backend, kind)?;
294 if weight == 0 {
295 continue;
296 }
297
298 let product = backlog.saturating_mul(weight);
299 let cast = saturating_cast_to_i64(product);
300 let dividend = natural_to_bips(cast);
301
302 let exp = dividend / divisor;
303 exponent_per_kind[kind as usize] =
304 exponent_per_kind[kind as usize].saturating_add(exp);
305 }
306 }
307
308 let mut result = [0u64; NUM_RESOURCE_KIND];
309 for i in 0..NUM_RESOURCE_KIND {
310 result[i] = exponent_per_kind[i].max(0) as u64;
311 }
312 Ok(result)
313 }
314
315 pub fn calc_base_fee_from_exponent<B: StorageBackend>(
318 &self,
319 backend: &mut B,
320 exponent_bips: u64,
321 ) -> Result<U256, L2PricingError> {
322 let min_base_fee = self.min_base_fee_wei(backend)?;
323 if exponent_bips == 0 {
324 return Ok(min_base_fee);
325 }
326
327 let exp_result = arb_math::approx_exp_basis_points(exponent_bips, 4);
328 let base_fee = (min_base_fee * U256::from(exp_result)) / U256::from(10000u64);
329
330 if base_fee < min_base_fee {
331 Ok(min_base_fee)
332 } else {
333 Ok(base_fee)
334 }
335 }
336
337 pub fn get_multi_gas_base_fee_per_resource<B: SystemStateBackend>(
338 &self,
339 backend: &mut B,
340 ) -> Result<[U256; NUM_RESOURCE_KIND], L2PricingError> {
341 let base_fee = self.base_fee_wei(backend)?;
342 let mgf = super::multi_gas_fees::open_multi_gas_fees(self.multi_gas_base_fees.clone());
343 let mut fees = [U256::ZERO; NUM_RESOURCE_KIND];
344 for kind in ResourceKind::ALL {
345 if kind == ResourceKind::SingleDim {
346 fees[kind as usize] = base_fee;
347 continue;
348 }
349 let fee = mgf.get_current_block_fee(backend, kind)?;
350 fees[kind as usize] = if fee.is_zero() { base_fee } else { fee };
351 }
352 Ok(fees)
353 }
354
355 pub fn get_current_multi_gas_fees<B: SystemStateBackend>(
361 &self,
362 backend: &mut B,
363 ) -> Result<[U256; NUM_RESOURCE_KIND], L2PricingError> {
364 let mgf = super::multi_gas_fees::open_multi_gas_fees(self.multi_gas_base_fees.clone());
365 let mut fees = [U256::ZERO; NUM_RESOURCE_KIND];
366 for kind in ResourceKind::ALL {
367 if kind == ResourceKind::SingleDim {
368 continue;
369 }
370 fees[kind as usize] = mgf.get_current_block_fee(backend, kind)?;
371 }
372 Ok(fees)
373 }
374
375 pub fn commit_multi_gas_fees<B: StorageBackend>(
377 &self,
378 backend: &mut B,
379 ) -> Result<(), L2PricingError> {
380 if self.gas_model_to_use(backend)? != GasModel::MultiGasConstraints {
381 return Ok(());
382 }
383 let mgf = super::multi_gas_fees::open_multi_gas_fees(self.multi_gas_base_fees.clone());
384 mgf.commit_next_to_current(backend)
385 }
386
387 pub fn backlog_update_cost<B: SystemStateBackend>(
389 &self,
390 backend: &mut B,
391 ) -> Result<u64, L2PricingError> {
392 use super::{STORAGE_READ_COST, STORAGE_WRITE_COST};
393
394 if self.arbos_version >= version::ARBOS_VERSION_60 {
395 return Ok(MULTI_CONSTRAINT_STATIC_BACKLOG_UPDATE_COST);
396 }
397
398 let mut result = 0u64;
399
400 if self.arbos_version >= version::ARBOS_VERSION_50 {
401 result += STORAGE_READ_COST;
402 }
403
404 if self.arbos_version >= version::ARBOS_VERSION_MULTI_CONSTRAINT_FIX {
405 let constraints_length = self.gas_constraints_length(backend)?;
406 if constraints_length > 0 {
407 result += STORAGE_READ_COST;
408 result += constraints_length * (STORAGE_READ_COST + STORAGE_WRITE_COST);
409 return Ok(result);
410 }
411 }
412
413 result += STORAGE_READ_COST + STORAGE_WRITE_COST;
414
415 Ok(result)
416 }
417
418 pub fn set_gas_constraints_from_legacy<B: StorageBackend>(
420 &self,
421 backend: &mut B,
422 ) -> Result<(), L2PricingError> {
423 self.clear_gas_constraints(backend)?;
424 let target = self.speed_limit_per_second(backend)?;
425 let adjustment_window = self.pricing_inertia(backend)?;
426 let old_backlog = self.gas_backlog(backend)?;
427 let backlog_tolerance = self.backlog_tolerance(backend)?;
428
429 let backlog = old_backlog.saturating_sub(backlog_tolerance.saturating_mul(target));
430 self.add_gas_constraint(backend, target, adjustment_window, backlog)
431 }
432
433 pub fn set_multi_gas_constraints_from_single_gas_constraints<B: StorageBackend>(
435 &self,
436 backend: &mut B,
437 ) -> Result<(), L2PricingError> {
438 self.clear_multi_gas_constraints(backend)?;
439
440 let length = self.gas_constraints_length(backend)?;
441
442 for i in 0..length {
443 let c = self.open_gas_constraint_at(i);
444
445 let target = c.target(backend)?;
446 let window = c.adjustment_window(backend)?;
447 let backlog = c.backlog(backend)?;
448
449 let weights = [1u64; NUM_RESOURCE_KIND];
450
451 let adjustment_window: u32 = if window > u32::MAX as u64 {
452 u32::MAX
453 } else {
454 window as u32
455 };
456
457 self.add_multi_gas_constraint(backend, target, adjustment_window, backlog, &weights)?;
458 }
459 Ok(())
460 }
461
462 pub fn multi_dimensional_price_for_refund<B: SystemStateBackend>(
466 &self,
467 backend: &mut B,
468 gas_used: MultiGas,
469 ) -> Result<U256, L2PricingError> {
470 let fees = self.get_multi_gas_base_fee_per_resource(backend)?;
471 let mut total = U256::ZERO;
472 for kind in ResourceKind::ALL {
473 let amount = gas_used.get(kind);
474 if amount == 0 {
475 continue;
476 }
477 total = total.saturating_add(U256::from(amount).saturating_mul(fees[kind as usize]));
478 }
479 Ok(total)
480 }
481
482 pub fn multi_dimensional_price_for_refund_with_fees<B: SystemStateBackend>(
491 &self,
492 backend: &mut B,
493 gas_used: MultiGas,
494 cached_fees: &[U256; NUM_RESOURCE_KIND],
495 ) -> Result<U256, L2PricingError> {
496 let base_fee = self.base_fee_wei(backend)?;
497 let mut total = U256::ZERO;
498 for kind in ResourceKind::ALL {
499 let amount = gas_used.get(kind);
500 if amount == 0 {
501 continue;
502 }
503 let fee = if kind == ResourceKind::SingleDim {
504 base_fee
505 } else {
506 let cached = cached_fees[kind as usize];
507 if cached.is_zero() { base_fee } else { cached }
508 };
509 total = total.saturating_add(U256::from(amount).saturating_mul(fee));
510 }
511 Ok(total)
512 }
513}
514
515fn saturating_cast_to_i64(value: u64) -> i64 {
517 if value > i64::MAX as u64 {
518 i64::MAX
519 } else {
520 value as i64
521 }
522}
523
524fn natural_to_bips(natural: i64) -> i64 {
526 natural.saturating_mul(10000)
527}
528
529pub fn apply_gas_delta(backlog: u64, delta: i64) -> u64 {
531 if delta > 0 {
532 backlog.saturating_add(delta as u64)
533 } else {
534 backlog.saturating_sub((-delta) as u64)
535 }
536}
537
538fn apply_gas_delta_op(op: BacklogOperation, backlog: u64, delta: u64) -> u64 {
540 match op {
541 BacklogOperation::Grow => backlog.saturating_add(delta),
542 BacklogOperation::Shrink => backlog.saturating_sub(delta),
543 }
544}
545
546#[cfg(test)]
547mod tests {
548 use alloy_primitives::{Address, B256, U256, address, keccak256};
549 use arb_primitives::multigas::MultiGas;
550 use arb_storage::Storage;
551 use revm::{Database, database::StateBuilder};
552
553 const ARBOS_STATE_ADDRESS: Address = address!("A4B05FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
554
555 #[derive(Default)]
556 struct EmptyDb;
557
558 impl Database for EmptyDb {
559 type Error = std::convert::Infallible;
560 fn basic(
561 &mut self,
562 _address: Address,
563 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
564 Ok(None)
565 }
566 fn code_by_hash(&mut self, _code_hash: B256) -> Result<revm::state::Bytecode, Self::Error> {
567 Ok(revm::state::Bytecode::default())
568 }
569 fn storage(&mut self, _address: Address, _index: U256) -> Result<U256, Self::Error> {
570 Ok(U256::ZERO)
571 }
572 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
573 Ok(B256::ZERO)
574 }
575 }
576
577 fn ensure_cache_account(state: &mut revm::database::State<EmptyDb>, addr: Address) {
579 use revm::database::{PlainAccount, states::account_status::AccountStatus};
580
581 let _ = state.load_cache_account(addr);
582 if let Some(cached) = state.cache.accounts.get_mut(&addr)
583 && cached.account.is_none()
584 {
585 cached.account = Some(PlainAccount {
586 info: revm::state::AccountInfo {
587 balance: U256::ZERO,
588 nonce: 0,
589 code_hash: keccak256([]),
590 code: None,
591 account_id: None,
592 },
593 storage: Default::default(),
594 });
595 cached.status = AccountStatus::InMemoryChange;
596 }
597 }
598
599 #[test]
600 fn test_grow_backlog_through_l2_pricing_state() {
601 let mut state = StateBuilder::new()
602 .with_database(EmptyDb)
603 .with_bundle_update()
604 .build();
605
606 ensure_cache_account(&mut state, ARBOS_STATE_ADDRESS);
608 arb_storage::set_account_nonce(&mut state, ARBOS_STATE_ADDRESS, 1);
609
610 let state_ptr: *mut revm::database::State<EmptyDb> = &mut state;
611
612 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
614 let l2_sto = backing.open_sub_storage(&[1]);
615
616 super::super::initialize_l2_pricing_state(&l2_sto, unsafe { &mut *state_ptr }).unwrap();
618
619 let l2_pricing = super::super::open_l2_pricing_state(
621 backing.open_sub_storage(&[1]),
622 10, );
624 let initial_backlog = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
625 assert_eq!(initial_backlog, 0, "Initial gasBacklog should be 0");
626
627 let result =
629 l2_pricing.grow_backlog(unsafe { &mut *state_ptr }, 100_000, MultiGas::default());
630 assert!(result.is_ok(), "grow_backlog should succeed");
631
632 let after_grow = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
634 assert_eq!(
635 after_grow, 100_000,
636 "gasBacklog should be 100000 after grow"
637 );
638
639 let result =
641 l2_pricing.grow_backlog(unsafe { &mut *state_ptr }, 50_000, MultiGas::default());
642 assert!(result.is_ok(), "second grow_backlog should succeed");
643
644 let after_second_grow = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
645 assert_eq!(
646 after_second_grow, 150_000,
647 "gasBacklog should be 150000 after second grow"
648 );
649
650 let result =
652 l2_pricing.shrink_backlog(unsafe { &mut *state_ptr }, 30_000, MultiGas::default());
653 assert!(result.is_ok(), "shrink_backlog should succeed");
654
655 let after_shrink = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
656 assert_eq!(
657 after_shrink, 120_000,
658 "gasBacklog should be 120000 after shrink"
659 );
660
661 use revm::database::states::bundle_state::BundleRetention;
663 state.merge_transitions(BundleRetention::Reverts);
664 let bundle = state.take_bundle();
665
666 let acct = bundle
667 .state
668 .get(&ARBOS_STATE_ADDRESS)
669 .expect("ArbOS account should be in bundle");
670
671 let l2_base = keccak256([1u8]); let gas_backlog_offset: u64 = 4;
675 let slot = arb_storage::storage_key_map(l2_base.as_slice(), gas_backlog_offset);
676
677 let bundle_slot = acct
678 .storage
679 .get(&slot)
680 .expect("gasBacklog slot should be in bundle");
681 assert_eq!(
682 bundle_slot.present_value,
683 U256::from(120_000u64),
684 "Bundle should contain final gasBacklog value"
685 );
686 }
687
688 #[test]
693 fn grow_backlog_survives_submit_retryable_then_retry_tx_flow() {
694 use alloy_primitives::map::HashMap;
695 use revm::{DatabaseCommit, database::states::bundle_state::BundleRetention};
696
697 let l2_base = keccak256([1u8]); let gas_backlog_slot = arb_storage::storage_key_map(l2_base.as_slice(), 4);
700 let speed_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 0);
701 let per_block_gas_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 1);
702 let base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 2);
703 let min_base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 3);
704 let pricing_inertia_slot = arb_storage::storage_key_map(l2_base.as_slice(), 5);
705 let backlog_tolerance_slot = arb_storage::storage_key_map(l2_base.as_slice(), 6);
706
707 let version_slot = arb_storage::storage_key_map(&[], 0);
709
710 struct PreloadedDb {
712 slots: HashMap<(Address, U256), U256>,
713 }
714
715 impl PreloadedDb {
716 fn new() -> Self {
717 Self {
718 slots: HashMap::default(),
719 }
720 }
721 fn set(&mut self, addr: Address, slot: U256, val: U256) {
722 self.slots.insert((addr, slot), val);
723 }
724 }
725
726 impl Database for PreloadedDb {
727 type Error = std::convert::Infallible;
728 fn basic(
729 &mut self,
730 addr: Address,
731 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
732 if addr == ARBOS_STATE_ADDRESS {
733 Ok(Some(revm::state::AccountInfo {
734 nonce: 1,
735 balance: U256::ZERO,
736 code_hash: keccak256([]),
737 code: None,
738 account_id: None,
739 }))
740 } else {
741 Ok(None)
742 }
743 }
744 fn code_by_hash(&mut self, _: B256) -> Result<revm::state::Bytecode, Self::Error> {
745 Ok(revm::state::Bytecode::default())
746 }
747 fn storage(&mut self, addr: Address, index: U256) -> Result<U256, Self::Error> {
748 Ok(self
749 .slots
750 .get(&(addr, index))
751 .copied()
752 .unwrap_or(U256::ZERO))
753 }
754 fn block_hash(&mut self, _: u64) -> Result<B256, Self::Error> {
755 Ok(B256::ZERO)
756 }
757 }
758
759 let arbos = ARBOS_STATE_ADDRESS;
760
761 let mut db = PreloadedDb::new();
763 db.set(arbos, gas_backlog_slot, U256::from(552_756u64));
764 db.set(arbos, speed_limit_slot, U256::from(7_000_000u64));
765 db.set(arbos, per_block_gas_limit_slot, U256::from(32_000_000u64));
766 db.set(arbos, base_fee_slot, U256::from(100_000_000u64));
767 db.set(arbos, min_base_fee_slot, U256::from(100_000_000u64));
768 db.set(arbos, pricing_inertia_slot, U256::from(102u64));
769 db.set(arbos, backlog_tolerance_slot, U256::from(10u64));
770 db.set(arbos, version_slot, U256::from(20u64)); let mut state = StateBuilder::new()
773 .with_database(db)
774 .with_bundle_update()
775 .build();
776
777 let state_ptr: *mut revm::database::State<PreloadedDb> = &mut state;
778
779 {
784 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
785 let l2_sto = backing.open_sub_storage(&[1]);
786 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
787
788 let result = l2_pricing.update_pricing_model(unsafe { &mut *state_ptr }, 0, 20);
790 assert!(result.is_ok(), "update_pricing_model should succeed");
791
792 let backlog = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
794 assert_eq!(
795 backlog, 552_756,
796 "gasBacklog should be 552756 after no-op drain"
797 );
798 }
799
800 let empty_changes: revm::state::EvmState = Default::default();
802 state.commit(empty_changes);
803
804 {
809 let retryable_base = keccak256([2u8]); for i in 0u64..10 {
812 let slot = arb_storage::storage_key_map(retryable_base.as_slice(), i);
813 let _ = arb_storage::write_storage_at(
814 unsafe { &mut *state_ptr },
815 arbos,
816 slot,
817 U256::from(1000 + i),
818 );
819 }
820
821 let scratch_slot_1 = arb_storage::storage_key_map(&[], 5); let scratch_slot_2 = arb_storage::storage_key_map(&[], 6);
824 let scratch_slot_3 = arb_storage::storage_key_map(&[], 7);
825 let _ = arb_storage::write_storage_at(
826 unsafe { &mut *state_ptr },
827 arbos,
828 scratch_slot_1,
829 U256::from(42),
830 );
831 let _ = arb_storage::write_storage_at(
832 unsafe { &mut *state_ptr },
833 arbos,
834 scratch_slot_2,
835 U256::from(43),
836 );
837 let _ = arb_storage::write_storage_at(
838 unsafe { &mut *state_ptr },
839 arbos,
840 scratch_slot_3,
841 U256::from(44),
842 );
843 }
844
845 let empty_changes2: revm::state::EvmState = Default::default();
847 state.commit(empty_changes2);
848
849 {
851 let scratch_slot_1 = arb_storage::storage_key_map(&[], 5);
852 let scratch_slot_2 = arb_storage::storage_key_map(&[], 6);
853 let scratch_slot_3 = arb_storage::storage_key_map(&[], 7);
854 let _ = arb_storage::write_arbos_storage(
855 unsafe { &mut *state_ptr },
856 scratch_slot_1,
857 U256::ZERO,
858 );
859 let _ = arb_storage::write_arbos_storage(
860 unsafe { &mut *state_ptr },
861 scratch_slot_2,
862 U256::ZERO,
863 );
864 let _ = arb_storage::write_arbos_storage(
865 unsafe { &mut *state_ptr },
866 scratch_slot_3,
867 U256::ZERO,
868 );
869 }
870
871 {
877 let scratch_slot_1 = arb_storage::storage_key_map(&[], 5);
878 let scratch_slot_2 = arb_storage::storage_key_map(&[], 6);
879 let scratch_slot_3 = arb_storage::storage_key_map(&[], 7);
880 let _ = arb_storage::write_storage_at(
881 unsafe { &mut *state_ptr },
882 arbos,
883 scratch_slot_1,
884 U256::from(99),
885 );
886 let _ = arb_storage::write_storage_at(
887 unsafe { &mut *state_ptr },
888 arbos,
889 scratch_slot_2,
890 U256::from(100),
891 );
892 let _ = arb_storage::write_storage_at(
893 unsafe { &mut *state_ptr },
894 arbos,
895 scratch_slot_3,
896 U256::from(101),
897 );
898 }
899
900 {
904 let mut evm_changes: revm::state::EvmState = Default::default();
905
906 let sender = address!("fd86e9a33fd52e4085fb94d24b759448a621cd36");
908 let _ = state.load_cache_account(sender);
909 let mut sender_acct = revm::state::Account::default();
910 sender_acct.info.balance = U256::from(1_000_000_000u64);
911 sender_acct.info.nonce = 1;
912 sender_acct.mark_touch();
913 evm_changes.insert(sender, sender_acct);
914
915 let contracts = [
917 address!("4453d0eaf066a61c9b81ddc18bb5a2bf2fc52224"),
918 address!("7c7db13e5d385bcc797422d3c767856d15d24c5c"),
919 address!("0057892cb8bb5f1ce1b3c6f5ade899732249713f"),
920 address!("35aa95ac4747d928e2cd42fe4461f6d9d1826346"),
921 address!("e1e3b1cbacc870cb6e5f4bdf246feb6eb5cd351b"),
922 address!("7348fdf6f3e090c635b23d970945093455214f3b"),
923 address!("d50e4a971bc8ed55af6aebc0a2178456069e87b5"),
924 ];
925
926 for (i, &contract) in contracts.iter().enumerate() {
927 let _ = state.load_cache_account(contract);
928 let mut acct = revm::state::Account::default();
929 acct.info.nonce = 1;
930 acct.info.code_hash = keccak256(format!("code_{}", i).as_bytes());
931 acct.mark_touch();
932 for j in 0u64..3 {
934 let slot = U256::from(j);
935 let mut evm_slot =
936 revm::state::EvmStorageSlot::new(U256::from(i as u64 * 100 + j), 0);
937 evm_slot.present_value = U256::from(i as u64 * 100 + j + 1);
938 acct.storage.insert(slot, evm_slot);
939 }
940 evm_changes.insert(contract, acct);
941 }
942
943 state.commit(evm_changes);
944 }
945
946 {
948 let scratch_slot_1 = arb_storage::storage_key_map(&[], 5);
949 let scratch_slot_2 = arb_storage::storage_key_map(&[], 6);
950 let scratch_slot_3 = arb_storage::storage_key_map(&[], 7);
951 let _ = arb_storage::write_arbos_storage(
952 unsafe { &mut *state_ptr },
953 scratch_slot_1,
954 U256::ZERO,
955 );
956 let _ = arb_storage::write_arbos_storage(
957 unsafe { &mut *state_ptr },
958 scratch_slot_2,
959 U256::ZERO,
960 );
961 let _ = arb_storage::write_arbos_storage(
962 unsafe { &mut *state_ptr },
963 scratch_slot_3,
964 U256::ZERO,
965 );
966 }
967
968 {
970 let retryable_base = keccak256([2u8]);
971 for i in 0u64..10 {
972 let slot = arb_storage::storage_key_map(retryable_base.as_slice(), i);
973 let _ = arb_storage::write_storage_at(
974 unsafe { &mut *state_ptr },
975 arbos,
976 slot,
977 U256::ZERO,
978 );
979 }
980 }
981
982 {
984 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
985 let l2_sto = backing.open_sub_storage(&[1]);
986 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
987
988 let backlog_before = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
989 assert_eq!(
990 backlog_before, 552_756,
991 "gasBacklog should still be 552756 before grow"
992 );
993
994 let result =
995 l2_pricing.grow_backlog(unsafe { &mut *state_ptr }, 357_751, MultiGas::default());
996 assert!(result.is_ok(), "grow_backlog should succeed");
997
998 let backlog_after = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
999 assert_eq!(
1000 backlog_after, 910_507,
1001 "gasBacklog should be 910507 after grow"
1002 );
1003 }
1004
1005 state.merge_transitions(BundleRetention::Reverts);
1009 let mut bundle = state.take_bundle();
1010
1011 let pre_filter_backlog = bundle
1013 .state
1014 .get(&arbos)
1015 .and_then(|a| a.storage.get(&gas_backlog_slot))
1016 .map(|s| s.present_value);
1017 assert_eq!(
1018 pre_filter_backlog,
1019 Some(U256::from(910_507u64)),
1020 "gasBacklog should be in bundle before filter with value 910507"
1021 );
1022
1023 for (_addr, account) in bundle.state.iter_mut() {
1025 account
1026 .storage
1027 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1028 }
1029
1030 let post_filter_backlog = bundle
1032 .state
1033 .get(&arbos)
1034 .and_then(|a| a.storage.get(&gas_backlog_slot))
1035 .map(|s| s.present_value);
1036 assert_eq!(
1037 post_filter_backlog,
1038 Some(U256::from(910_507u64)),
1039 "gasBacklog should survive filter_unchanged_storage with value 910507"
1040 );
1041
1042 let original = bundle
1044 .state
1045 .get(&arbos)
1046 .and_then(|a| a.storage.get(&gas_backlog_slot))
1047 .map(|s| s.previous_or_original_value);
1048 assert_eq!(
1049 original,
1050 Some(U256::from(552_756u64)),
1051 "gasBacklog original should be the pre-block DB value 552756"
1052 );
1053
1054 let cache_backlog = state
1058 .cache
1059 .accounts
1060 .get(&arbos)
1061 .and_then(|ca| ca.account.as_ref())
1062 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
1063 assert_eq!(
1064 cache_backlog,
1065 Some(U256::from(910_507u64)),
1066 "gasBacklog should be in cache with value 910507"
1067 );
1068 }
1069
1070 #[test]
1073 fn grow_backlog_with_arbos_in_evm_commit() {
1074 use alloy_primitives::map::HashMap;
1075 use revm::{DatabaseCommit, database::states::bundle_state::BundleRetention};
1076
1077 let l2_base = keccak256([1u8]);
1078 let gas_backlog_slot = arb_storage::storage_key_map(l2_base.as_slice(), 4);
1079 let speed_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 0);
1080 let base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 2);
1081 let min_base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 3);
1082 let pricing_inertia_slot = arb_storage::storage_key_map(l2_base.as_slice(), 5);
1083 let backlog_tolerance_slot = arb_storage::storage_key_map(l2_base.as_slice(), 6);
1084 let version_slot = arb_storage::storage_key_map(&[], 0);
1085 let scratch_1 = arb_storage::storage_key_map(&[], 5);
1087 let scratch_2 = arb_storage::storage_key_map(&[], 6);
1088
1089 struct PreloadedDb {
1090 slots: HashMap<(Address, U256), U256>,
1091 }
1092 impl PreloadedDb {
1093 fn new() -> Self {
1094 Self {
1095 slots: HashMap::default(),
1096 }
1097 }
1098 fn set(&mut self, addr: Address, slot: U256, val: U256) {
1099 self.slots.insert((addr, slot), val);
1100 }
1101 }
1102 impl Database for PreloadedDb {
1103 type Error = std::convert::Infallible;
1104 fn basic(
1105 &mut self,
1106 addr: Address,
1107 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
1108 if addr == ARBOS_STATE_ADDRESS {
1109 Ok(Some(revm::state::AccountInfo {
1110 nonce: 1,
1111 balance: U256::ZERO,
1112 code_hash: keccak256([]),
1113 code: None,
1114 account_id: None,
1115 }))
1116 } else {
1117 Ok(None)
1118 }
1119 }
1120 fn code_by_hash(&mut self, _: B256) -> Result<revm::state::Bytecode, Self::Error> {
1121 Ok(revm::state::Bytecode::default())
1122 }
1123 fn storage(&mut self, addr: Address, index: U256) -> Result<U256, Self::Error> {
1124 Ok(self
1125 .slots
1126 .get(&(addr, index))
1127 .copied()
1128 .unwrap_or(U256::ZERO))
1129 }
1130 fn block_hash(&mut self, _: u64) -> Result<B256, Self::Error> {
1131 Ok(B256::ZERO)
1132 }
1133 }
1134
1135 let arbos = ARBOS_STATE_ADDRESS;
1136 let mut db = PreloadedDb::new();
1137 db.set(arbos, gas_backlog_slot, U256::from(552_756u64));
1138 db.set(arbos, speed_limit_slot, U256::from(7_000_000u64));
1139 db.set(arbos, base_fee_slot, U256::from(100_000_000u64));
1140 db.set(arbos, min_base_fee_slot, U256::from(100_000_000u64));
1141 db.set(arbos, pricing_inertia_slot, U256::from(102u64));
1142 db.set(arbos, backlog_tolerance_slot, U256::from(10u64));
1143 db.set(arbos, version_slot, U256::from(20u64));
1144
1145 let mut state = StateBuilder::new()
1146 .with_database(db)
1147 .with_bundle_update()
1148 .build();
1149 let state_ptr: *mut revm::database::State<PreloadedDb> = &mut state;
1150
1151 {
1153 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1154 let l2_sto = backing.open_sub_storage(&[1]);
1155 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
1156 let _ = l2_pricing.update_pricing_model(unsafe { &mut *state_ptr }, 0, 20);
1157 }
1158 state.commit(HashMap::default());
1159
1160 {
1162 let _ = arb_storage::write_storage_at(
1163 unsafe { &mut *state_ptr },
1164 arbos,
1165 scratch_1,
1166 U256::from(42),
1167 );
1168 let _ = arb_storage::write_storage_at(
1169 unsafe { &mut *state_ptr },
1170 arbos,
1171 scratch_2,
1172 U256::from(43),
1173 );
1174 let retryable_base = keccak256([2u8]);
1175 for i in 0u64..5 {
1176 let slot = arb_storage::storage_key_map(retryable_base.as_slice(), i);
1177 let _ = arb_storage::write_storage_at(
1178 unsafe { &mut *state_ptr },
1179 arbos,
1180 slot,
1181 U256::from(1000 + i),
1182 );
1183 }
1184 }
1185 state.commit(HashMap::default());
1186 let _ = arb_storage::write_arbos_storage(unsafe { &mut *state_ptr }, scratch_1, U256::ZERO);
1188 let _ = arb_storage::write_arbos_storage(unsafe { &mut *state_ptr }, scratch_2, U256::ZERO);
1189
1190 let _ = arb_storage::write_storage_at(
1192 unsafe { &mut *state_ptr },
1193 arbos,
1194 scratch_1,
1195 U256::from(99),
1196 );
1197 let _ = arb_storage::write_storage_at(
1198 unsafe { &mut *state_ptr },
1199 arbos,
1200 scratch_2,
1201 U256::from(100),
1202 );
1203
1204 {
1206 let mut evm_changes: revm::state::EvmState = Default::default();
1207
1208 let sender = address!("fd86e9a33fd52e4085fb94d24b759448a621cd36");
1210 let _ = state.load_cache_account(sender);
1211 let mut sender_acct = revm::state::Account::default();
1212 sender_acct.info.balance = U256::from(1_000_000_000u64);
1213 sender_acct.info.nonce = 1;
1214 sender_acct.mark_touch();
1215 evm_changes.insert(sender, sender_acct);
1216
1217 let _ = state.load_cache_account(arbos);
1220 let mut arbos_acct = revm::state::Account {
1221 info: revm::state::AccountInfo {
1222 nonce: 1,
1223 balance: U256::ZERO,
1224 code_hash: keccak256([]),
1225 code: None,
1226 account_id: None,
1227 },
1228 ..Default::default()
1229 };
1230 arbos_acct.storage.insert(
1233 scratch_1,
1234 revm::state::EvmStorageSlot::new(U256::from(99), 0),
1235 );
1236 arbos_acct.mark_touch();
1237 evm_changes.insert(arbos, arbos_acct);
1238
1239 state.commit(evm_changes);
1240 }
1241
1242 let _backlog_check =
1244 arb_storage::read_storage_at(unsafe { &mut *state_ptr }, arbos, gas_backlog_slot);
1245
1246 let _ = arb_storage::write_arbos_storage(unsafe { &mut *state_ptr }, scratch_1, U256::ZERO);
1248 let _ = arb_storage::write_arbos_storage(unsafe { &mut *state_ptr }, scratch_2, U256::ZERO);
1249
1250 {
1252 let retryable_base = keccak256([2u8]);
1253 for i in 0u64..5 {
1254 let slot = arb_storage::storage_key_map(retryable_base.as_slice(), i);
1255 let _ = arb_storage::write_storage_at(
1256 unsafe { &mut *state_ptr },
1257 arbos,
1258 slot,
1259 U256::ZERO,
1260 );
1261 }
1262 }
1263
1264 {
1266 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1267 let l2_sto = backing.open_sub_storage(&[1]);
1268 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
1269
1270 let backlog_before = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1271 assert_eq!(
1272 backlog_before, 552_756,
1273 "gasBacklog should be 552756 before grow"
1274 );
1275
1276 let _ =
1277 l2_pricing.grow_backlog(unsafe { &mut *state_ptr }, 357_751, MultiGas::default());
1278
1279 let backlog_after = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1280 assert_eq!(backlog_after, 910_507, "gasBacklog should be 910507");
1281 }
1282
1283 state.merge_transitions(BundleRetention::Reverts);
1285 let mut bundle = state.take_bundle();
1286
1287 let pre_filter = bundle
1288 .state
1289 .get(&arbos)
1290 .and_then(|a| a.storage.get(&gas_backlog_slot))
1291 .map(|s| (s.present_value, s.previous_or_original_value));
1292 assert_eq!(
1293 pre_filter.map(|p| p.0),
1294 Some(U256::from(910_507u64)),
1295 "gasBacklog should be 910507 in bundle before filter"
1296 );
1297
1298 for (_addr, account) in bundle.state.iter_mut() {
1300 account
1301 .storage
1302 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1303 }
1304
1305 let post_filter = bundle
1306 .state
1307 .get(&arbos)
1308 .and_then(|a| a.storage.get(&gas_backlog_slot))
1309 .map(|s| s.present_value);
1310 assert_eq!(
1311 post_filter,
1312 Some(U256::from(910_507u64)),
1313 "gasBacklog should survive filter when ArbOS is in EVM commit"
1314 );
1315 }
1316
1317 #[test]
1321 fn grow_backlog_with_transition_state_consumed() {
1322 use alloy_primitives::map::HashMap;
1323 use revm::{
1324 DatabaseCommit,
1325 database::states::{bundle_state::BundleRetention, plain_account::StorageSlot},
1326 };
1327
1328 let l2_base = keccak256([1u8]);
1329 let gas_backlog_slot = arb_storage::storage_key_map(l2_base.as_slice(), 4);
1330 let speed_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 0);
1331 let base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 2);
1332 let min_base_fee_slot = arb_storage::storage_key_map(l2_base.as_slice(), 3);
1333 let pricing_inertia_slot = arb_storage::storage_key_map(l2_base.as_slice(), 5);
1334 let backlog_tolerance_slot = arb_storage::storage_key_map(l2_base.as_slice(), 6);
1335 let version_slot = arb_storage::storage_key_map(&[], 0);
1336
1337 struct PreloadedDb(HashMap<(Address, U256), U256>);
1338 impl PreloadedDb {
1339 fn new() -> Self {
1340 Self(HashMap::default())
1341 }
1342 fn set(&mut self, a: Address, s: U256, v: U256) {
1343 self.0.insert((a, s), v);
1344 }
1345 }
1346 impl Database for PreloadedDb {
1347 type Error = std::convert::Infallible;
1348 fn basic(
1349 &mut self,
1350 addr: Address,
1351 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
1352 if addr == ARBOS_STATE_ADDRESS {
1353 Ok(Some(revm::state::AccountInfo {
1354 nonce: 1,
1355 balance: U256::ZERO,
1356 code_hash: keccak256([]),
1357 code: None,
1358 account_id: None,
1359 }))
1360 } else {
1361 Ok(None)
1362 }
1363 }
1364 fn code_by_hash(&mut self, _: B256) -> Result<revm::state::Bytecode, Self::Error> {
1365 Ok(revm::state::Bytecode::default())
1366 }
1367 fn storage(&mut self, a: Address, i: U256) -> Result<U256, Self::Error> {
1368 Ok(self.0.get(&(a, i)).copied().unwrap_or(U256::ZERO))
1369 }
1370 fn block_hash(&mut self, _: u64) -> Result<B256, Self::Error> {
1371 Ok(B256::ZERO)
1372 }
1373 }
1374
1375 let arbos = ARBOS_STATE_ADDRESS;
1376 let mut db = PreloadedDb::new();
1377 db.set(arbos, gas_backlog_slot, U256::from(552_756u64));
1378 db.set(arbos, speed_limit_slot, U256::from(7_000_000u64));
1379 db.set(arbos, base_fee_slot, U256::from(100_000_000u64));
1380 db.set(arbos, min_base_fee_slot, U256::from(100_000_000u64));
1381 db.set(arbos, pricing_inertia_slot, U256::from(102u64));
1382 db.set(arbos, backlog_tolerance_slot, U256::from(10u64));
1383 db.set(arbos, version_slot, U256::from(20u64));
1384
1385 let mut state = StateBuilder::new()
1386 .with_database(db)
1387 .with_bundle_update()
1388 .build();
1389 let state_ptr: *mut revm::database::State<PreloadedDb> = &mut state;
1390
1391 {
1393 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1394 let l2_sto = backing.open_sub_storage(&[1]);
1395 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
1396 let _ = l2_pricing.update_pricing_model(unsafe { &mut *state_ptr }, 0, 20);
1397 }
1398 state.commit(HashMap::default());
1399
1400 state.merge_transitions(BundleRetention::Reverts);
1405 let _mid_bundle = state.take_bundle();
1406
1407 let _ts_is_none = state.transition_state.is_none();
1409
1410 {
1412 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1413 let l2_sto = backing.open_sub_storage(&[1]);
1414 let l2_pricing = super::super::open_l2_pricing_state(l2_sto, 20);
1415
1416 let _backlog_before = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1417
1418 let _ =
1419 l2_pricing.grow_backlog(unsafe { &mut *state_ptr }, 357_751, MultiGas::default());
1420
1421 let _backlog_after = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1422 }
1423
1424 state.merge_transitions(BundleRetention::Reverts);
1426 let mut bundle = state.take_bundle();
1427
1428 let _in_bundle = bundle
1430 .state
1431 .get(&arbos)
1432 .and_then(|a| a.storage.get(&gas_backlog_slot))
1433 .map(|s| s.present_value);
1434
1435 {
1439 let _cache_val = state
1440 .cache
1441 .accounts
1442 .get(&arbos)
1443 .and_then(|ca| ca.account.as_ref())
1444 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
1445
1446 if let Some(bundle_acct) = bundle.state.get_mut(&arbos) {
1448 if let Some(cached_acc) = state.cache.accounts.get(&arbos)
1449 && let Some(ref plain) = cached_acc.account
1450 {
1451 for (key, value) in &plain.storage {
1452 if let Some(slot) = bundle_acct.storage.get_mut(key) {
1453 slot.present_value = *value;
1454 } else {
1455 let original =
1456 state.database.storage(arbos, *key).unwrap_or(U256::ZERO);
1457 if *value != original {
1458 bundle_acct.storage.insert(
1459 *key,
1460 StorageSlot {
1461 previous_or_original_value: original,
1462 present_value: *value,
1463 },
1464 );
1465 }
1466 }
1467 }
1468 }
1469 } else {
1470 if let Some(cached_acc) = state.cache.accounts.get(&arbos)
1472 && let Some(ref plain) = cached_acc.account
1473 {
1474 let mut storage_changes: revm::database::StorageWithOriginalValues =
1475 Default::default();
1476 for (key, value) in &plain.storage {
1477 let original = state.database.storage(arbos, *key).unwrap_or(U256::ZERO);
1478 if *value != original {
1479 storage_changes.insert(
1480 *key,
1481 StorageSlot {
1482 previous_or_original_value: original,
1483 present_value: *value,
1484 },
1485 );
1486 }
1487 }
1488 if !storage_changes.is_empty() {
1489 bundle.state.insert(
1490 arbos,
1491 revm::database::BundleAccount {
1492 info: Some(plain.info.clone()),
1493 original_info: None,
1494 storage: storage_changes,
1495 status: revm::database::AccountStatus::Changed,
1496 },
1497 );
1498 }
1499 }
1500 }
1501 }
1502
1503 let _after_augment = bundle
1504 .state
1505 .get(&arbos)
1506 .and_then(|a| a.storage.get(&gas_backlog_slot))
1507 .map(|s| s.present_value);
1508
1509 for (_addr, account) in bundle.state.iter_mut() {
1511 account
1512 .storage
1513 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1514 }
1515
1516 let after_filter = bundle
1517 .state
1518 .get(&arbos)
1519 .and_then(|a| a.storage.get(&gas_backlog_slot))
1520 .map(|s| s.present_value);
1521
1522 assert_eq!(
1523 after_filter,
1524 Some(U256::from(910_507u64)),
1525 "gasBacklog MUST survive even when transition_state was consumed mid-block"
1526 );
1527 }
1528
1529 #[test]
1537 fn test_grow_backlog_survives_evm_commit_and_augment() {
1538 use revm::{
1539 DatabaseCommit,
1540 database::states::{bundle_state::BundleRetention, plain_account::StorageSlot},
1541 };
1542
1543 let l2_base = keccak256([1u8]); let gas_backlog_offset: u64 = 4;
1546 let gas_backlog_slot = arb_storage::storage_key_map(l2_base.as_slice(), gas_backlog_offset);
1547
1548 {
1550 let mut state = StateBuilder::new()
1551 .with_database(EmptyDb)
1552 .with_bundle_update()
1553 .build();
1554
1555 ensure_cache_account(&mut state, ARBOS_STATE_ADDRESS);
1557 arb_storage::set_account_nonce(&mut state, ARBOS_STATE_ADDRESS, 1);
1558
1559 let state_ptr: *mut revm::database::State<EmptyDb> = &mut state;
1560
1561 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1563 let l2_sto = backing.open_sub_storage(&[1]);
1564 super::super::initialize_l2_pricing_state(&l2_sto, unsafe { &mut *state_ptr }).unwrap();
1565
1566 let l2_pricing =
1568 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1569 l2_pricing
1570 .set_gas_backlog(unsafe { &mut *state_ptr }, 552756)
1571 .unwrap();
1572 let pre_start = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1573 assert_eq!(pre_start, 552756, "Pre-existing backlog should be 552756");
1574
1575 l2_pricing
1577 .update_pricing_model(unsafe { &mut *state_ptr }, 0, 10)
1578 .unwrap();
1579 let after_start = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1580 assert_eq!(
1581 after_start, 552756,
1582 "time_passed=0 should not change backlog"
1583 );
1584
1585 let empty_state: revm::state::EvmState = Default::default();
1587 state.commit(empty_state);
1588
1589 let _cache_val = state
1591 .cache
1592 .accounts
1593 .get(&ARBOS_STATE_ADDRESS)
1594 .and_then(|ca| ca.account.as_ref())
1595 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
1596
1597 let l2_pricing2 =
1599 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1600 l2_pricing2
1601 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
1602 .unwrap();
1603 let after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1604 assert_eq!(after_grow, 552756 + 357751, "backlog should be sum");
1605
1606 let _cache_val2 = state
1608 .cache
1609 .accounts
1610 .get(&ARBOS_STATE_ADDRESS)
1611 .and_then(|ca| ca.account.as_ref())
1612 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
1613
1614 state.merge_transitions(BundleRetention::Reverts);
1616 let mut bundle = state.take_bundle();
1617
1618 let _bundle_has_slot = bundle
1620 .state
1621 .get(&ARBOS_STATE_ADDRESS)
1622 .and_then(|a| a.storage.get(&gas_backlog_slot))
1623 .map(|s| s.present_value);
1624
1625 for (addr, cache_acct) in &state.cache.accounts {
1628 let current_info = cache_acct.account.as_ref().map(|a| a.info.clone());
1629 let current_storage = cache_acct
1630 .account
1631 .as_ref()
1632 .map(|a| &a.storage)
1633 .cloned()
1634 .unwrap_or_default();
1635
1636 if let Some(bundle_acct) = bundle.state.get_mut(addr) {
1637 bundle_acct.info = current_info;
1638 for (key, value) in ¤t_storage {
1639 if let Some(slot) = bundle_acct.storage.get_mut(key) {
1640 slot.present_value = *value;
1641 } else {
1642 let original_value = U256::ZERO;
1645 if *value != original_value {
1646 bundle_acct.storage.insert(
1647 *key,
1648 StorageSlot {
1649 previous_or_original_value: original_value,
1650 present_value: *value,
1651 },
1652 );
1653 }
1654 }
1655 }
1656 } else {
1657 let storage_changes: revm::database::StorageWithOriginalValues =
1659 current_storage
1660 .iter()
1661 .filter_map(|(key, value)| {
1662 let original_value = U256::ZERO;
1663 if original_value != *value {
1664 Some((
1665 *key,
1666 StorageSlot {
1667 previous_or_original_value: original_value,
1668 present_value: *value,
1669 },
1670 ))
1671 } else {
1672 None
1673 }
1674 })
1675 .collect();
1676
1677 let info_changed = current_info.is_some(); if info_changed || !storage_changes.is_empty() {
1679 bundle.state.insert(
1680 *addr,
1681 revm::database::BundleAccount {
1682 info: current_info,
1683 original_info: None,
1684 storage: storage_changes,
1685 status: revm::database::AccountStatus::InMemoryChange,
1686 },
1687 );
1688 }
1689 }
1690 }
1691
1692 let _bundle_after_augment = bundle
1693 .state
1694 .get(&ARBOS_STATE_ADDRESS)
1695 .and_then(|a| a.storage.get(&gas_backlog_slot))
1696 .map(|s| (s.present_value, s.previous_or_original_value));
1697
1698 for (_addr, account) in bundle.state.iter_mut() {
1700 account
1701 .storage
1702 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1703 }
1704
1705 let final_slot = bundle
1706 .state
1707 .get(&ARBOS_STATE_ADDRESS)
1708 .and_then(|a| a.storage.get(&gas_backlog_slot))
1709 .map(|s| s.present_value);
1710 assert!(
1711 final_slot.is_some(),
1712 "VARIANT A FAILED: gas_backlog slot MISSING from bundle after empty EVM commit"
1713 );
1714 assert_eq!(
1715 final_slot.unwrap(),
1716 U256::from(552756u64 + 357751u64),
1717 "VARIANT A: gas_backlog should be 910507"
1718 );
1719 }
1720
1721 {
1723 let mut state = StateBuilder::new()
1724 .with_database(EmptyDb)
1725 .with_bundle_update()
1726 .build();
1727
1728 ensure_cache_account(&mut state, ARBOS_STATE_ADDRESS);
1729 arb_storage::set_account_nonce(&mut state, ARBOS_STATE_ADDRESS, 1);
1730
1731 let state_ptr: *mut revm::database::State<EmptyDb> = &mut state;
1732
1733 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1734 let l2_sto = backing.open_sub_storage(&[1]);
1735 super::super::initialize_l2_pricing_state(&l2_sto, unsafe { &mut *state_ptr }).unwrap();
1736
1737 let l2_pricing =
1738 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1739 l2_pricing
1740 .set_gas_backlog(unsafe { &mut *state_ptr }, 552756)
1741 .unwrap();
1742 l2_pricing
1743 .update_pricing_model(unsafe { &mut *state_ptr }, 0, 10)
1744 .unwrap();
1745 let _before_commit = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1746
1747 let _ = state.load_cache_account(ARBOS_STATE_ADDRESS);
1752 let mut arbos_evm_account = revm::state::Account {
1753 info: revm::state::AccountInfo {
1754 balance: U256::ZERO,
1755 nonce: 1,
1756 code_hash: keccak256([]),
1757 code: None,
1758 account_id: None,
1759 },
1760 ..Default::default()
1761 };
1762 arbos_evm_account.mark_touch();
1763 let mut evm_changes: revm::state::EvmState = Default::default();
1765 evm_changes.insert(ARBOS_STATE_ADDRESS, arbos_evm_account);
1766 state.commit(evm_changes);
1767
1768 let l2_pricing2 =
1770 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1771 let _read_before_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1772
1773 l2_pricing2
1774 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
1775 .unwrap();
1776 let _after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1777
1778 let _cache_val2 = state
1780 .cache
1781 .accounts
1782 .get(&ARBOS_STATE_ADDRESS)
1783 .and_then(|ca| ca.account.as_ref())
1784 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
1785
1786 state.merge_transitions(BundleRetention::Reverts);
1788 let mut bundle = state.take_bundle();
1789
1790 let _bundle_pre = bundle
1791 .state
1792 .get(&ARBOS_STATE_ADDRESS)
1793 .and_then(|a| a.storage.get(&gas_backlog_slot))
1794 .map(|s| (s.present_value, s.previous_or_original_value));
1795
1796 for (addr, cache_acct) in &state.cache.accounts {
1798 let current_info = cache_acct.account.as_ref().map(|a| a.info.clone());
1799 let current_storage = cache_acct
1800 .account
1801 .as_ref()
1802 .map(|a| &a.storage)
1803 .cloned()
1804 .unwrap_or_default();
1805
1806 if let Some(bundle_acct) = bundle.state.get_mut(addr) {
1807 bundle_acct.info = current_info;
1808 for (key, value) in ¤t_storage {
1809 if let Some(slot) = bundle_acct.storage.get_mut(key) {
1810 slot.present_value = *value;
1811 } else {
1812 let original_value = U256::ZERO;
1813 if *value != original_value {
1814 bundle_acct.storage.insert(
1815 *key,
1816 StorageSlot {
1817 previous_or_original_value: original_value,
1818 present_value: *value,
1819 },
1820 );
1821 }
1822 }
1823 }
1824 }
1825 }
1826
1827 let _bundle_post = bundle
1828 .state
1829 .get(&ARBOS_STATE_ADDRESS)
1830 .and_then(|a| a.storage.get(&gas_backlog_slot))
1831 .map(|s| (s.present_value, s.previous_or_original_value));
1832
1833 for (_addr, account) in bundle.state.iter_mut() {
1835 account
1836 .storage
1837 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1838 }
1839
1840 let final_slot = bundle
1841 .state
1842 .get(&ARBOS_STATE_ADDRESS)
1843 .and_then(|a| a.storage.get(&gas_backlog_slot))
1844 .map(|s| s.present_value);
1845 assert!(
1846 final_slot.is_some(),
1847 "VARIANT B FAILED: gas_backlog slot MISSING from bundle after EVM commit with ArbOS touched"
1848 );
1849 assert_eq!(
1850 final_slot.unwrap(),
1851 U256::from(552756u64 + 357751u64),
1852 "VARIANT B: gas_backlog should be 910507"
1853 );
1854 }
1855
1856 {
1861 let mut state = StateBuilder::new()
1862 .with_database(EmptyDb)
1863 .with_bundle_update()
1864 .build();
1865
1866 ensure_cache_account(&mut state, ARBOS_STATE_ADDRESS);
1867 arb_storage::set_account_nonce(&mut state, ARBOS_STATE_ADDRESS, 1);
1868
1869 let state_ptr: *mut revm::database::State<EmptyDb> = &mut state;
1870
1871 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
1872 let l2_sto = backing.open_sub_storage(&[1]);
1873 super::super::initialize_l2_pricing_state(&l2_sto, unsafe { &mut *state_ptr }).unwrap();
1874
1875 let l2_pricing =
1876 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1877 l2_pricing
1878 .set_gas_backlog(unsafe { &mut *state_ptr }, 552756)
1879 .unwrap();
1880 l2_pricing
1881 .update_pricing_model(unsafe { &mut *state_ptr }, 0, 10)
1882 .unwrap();
1883
1884 let _ = state.load_cache_account(ARBOS_STATE_ADDRESS);
1887 let mut arbos_evm_account = revm::state::Account {
1888 info: revm::state::AccountInfo {
1889 balance: U256::ZERO,
1890 nonce: 1,
1891 code_hash: keccak256([]),
1892 code: None,
1893 account_id: None,
1894 },
1895 ..Default::default()
1896 };
1897 arbos_evm_account.mark_touch();
1898
1899 arbos_evm_account.storage.insert(
1902 gas_backlog_slot,
1903 revm::state::EvmStorageSlot::new(U256::from(552756u64), 0),
1904 );
1906
1907 let mut evm_changes: revm::state::EvmState = Default::default();
1908 evm_changes.insert(ARBOS_STATE_ADDRESS, arbos_evm_account);
1909 state.commit(evm_changes);
1910
1911 let l2_pricing2 =
1913 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
1914 let _read_before_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1915
1916 l2_pricing2
1917 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
1918 .unwrap();
1919 let _after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
1920
1921 state.merge_transitions(BundleRetention::Reverts);
1923 let mut bundle = state.take_bundle();
1924
1925 let _bundle_pre = bundle
1926 .state
1927 .get(&ARBOS_STATE_ADDRESS)
1928 .and_then(|a| a.storage.get(&gas_backlog_slot))
1929 .map(|s| (s.present_value, s.previous_or_original_value));
1930
1931 for (addr, cache_acct) in &state.cache.accounts {
1933 let current_info = cache_acct.account.as_ref().map(|a| a.info.clone());
1934 let current_storage = cache_acct
1935 .account
1936 .as_ref()
1937 .map(|a| &a.storage)
1938 .cloned()
1939 .unwrap_or_default();
1940
1941 if let Some(bundle_acct) = bundle.state.get_mut(addr) {
1942 bundle_acct.info = current_info;
1943 for (key, value) in ¤t_storage {
1944 if let Some(slot) = bundle_acct.storage.get_mut(key) {
1945 slot.present_value = *value;
1946 } else {
1947 let original_value = U256::ZERO;
1948 if *value != original_value {
1949 bundle_acct.storage.insert(
1950 *key,
1951 StorageSlot {
1952 previous_or_original_value: original_value,
1953 present_value: *value,
1954 },
1955 );
1956 }
1957 }
1958 }
1959 }
1960 }
1961
1962 let _bundle_post = bundle
1963 .state
1964 .get(&ARBOS_STATE_ADDRESS)
1965 .and_then(|a| a.storage.get(&gas_backlog_slot))
1966 .map(|s| (s.present_value, s.previous_or_original_value));
1967
1968 for (_addr, account) in bundle.state.iter_mut() {
1970 account
1971 .storage
1972 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
1973 }
1974
1975 let final_slot = bundle
1976 .state
1977 .get(&ARBOS_STATE_ADDRESS)
1978 .and_then(|a| a.storage.get(&gas_backlog_slot))
1979 .map(|s| s.present_value);
1980 assert!(
1981 final_slot.is_some(),
1982 "VARIANT C FAILED: gas_backlog slot MISSING from bundle after EVM commit with ArbOS storage read"
1983 );
1984 assert_eq!(
1985 final_slot.unwrap(),
1986 U256::from(552756u64 + 357751u64),
1987 "VARIANT C: gas_backlog should be 910507"
1988 );
1989 }
1990
1991 {
1994 let mut state = StateBuilder::new()
1995 .with_database(EmptyDb)
1996 .with_bundle_update()
1997 .build();
1998
1999 ensure_cache_account(&mut state, ARBOS_STATE_ADDRESS);
2000 arb_storage::set_account_nonce(&mut state, ARBOS_STATE_ADDRESS, 1);
2001
2002 let state_ptr: *mut revm::database::State<EmptyDb> = &mut state;
2003
2004 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
2005 let l2_sto = backing.open_sub_storage(&[1]);
2006 super::super::initialize_l2_pricing_state(&l2_sto, unsafe { &mut *state_ptr }).unwrap();
2007
2008 let l2_pricing =
2010 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2011 l2_pricing
2012 .set_gas_backlog(unsafe { &mut *state_ptr }, 552756)
2013 .unwrap();
2014
2015 l2_pricing
2017 .update_pricing_model(unsafe { &mut *state_ptr }, 0, 10)
2018 .unwrap();
2019
2020 state.commit(Default::default());
2022
2023 let sender = address!("1111111111111111111111111111111111111111");
2025 let receiver = address!("2222222222222222222222222222222222222222");
2026 let _ = state.load_cache_account(sender);
2027 let _ = state.load_cache_account(receiver);
2028
2029 let mut user_changes: revm::state::EvmState = Default::default();
2030 let mut sender_acct = revm::state::Account::default();
2031 sender_acct.info.balance = U256::from(999_000u64);
2032 sender_acct.info.nonce = 1;
2033 sender_acct.mark_touch();
2034 user_changes.insert(sender, sender_acct);
2035
2036 let mut receiver_acct = revm::state::Account::default();
2037 receiver_acct.info.balance = U256::from(1_000u64);
2038 receiver_acct.mark_touch();
2039 user_changes.insert(receiver, receiver_acct);
2040
2041 state.commit(user_changes);
2042
2043 let _cache_val_after_user = state
2045 .cache
2046 .accounts
2047 .get(&ARBOS_STATE_ADDRESS)
2048 .and_then(|ca| ca.account.as_ref())
2049 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
2050
2051 let l2_pricing2 =
2054 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2055 let _read_val = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2056 l2_pricing2
2057 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
2058 .unwrap();
2059 let after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2060 assert_eq!(after_grow, 552756 + 357751, "backlog should be sum");
2061
2062 state.merge_transitions(BundleRetention::Reverts);
2064 let mut bundle = state.take_bundle();
2065
2066 let _bundle_pre = bundle
2067 .state
2068 .get(&ARBOS_STATE_ADDRESS)
2069 .and_then(|a| a.storage.get(&gas_backlog_slot))
2070 .map(|s| (s.present_value, s.previous_or_original_value));
2071
2072 for (addr, cache_acct) in &state.cache.accounts {
2074 let current_info = cache_acct.account.as_ref().map(|a| a.info.clone());
2075 let current_storage = cache_acct
2076 .account
2077 .as_ref()
2078 .map(|a| &a.storage)
2079 .cloned()
2080 .unwrap_or_default();
2081
2082 if let Some(bundle_acct) = bundle.state.get_mut(addr) {
2083 bundle_acct.info = current_info;
2084 for (key, value) in ¤t_storage {
2085 if let Some(slot) = bundle_acct.storage.get_mut(key) {
2086 slot.present_value = *value;
2087 } else {
2088 let original_value = U256::ZERO;
2089 if *value != original_value {
2090 bundle_acct.storage.insert(
2091 *key,
2092 StorageSlot {
2093 previous_or_original_value: original_value,
2094 present_value: *value,
2095 },
2096 );
2097 }
2098 }
2099 }
2100 } else {
2101 let storage_changes: revm::database::StorageWithOriginalValues =
2102 current_storage
2103 .iter()
2104 .filter_map(|(key, value)| {
2105 let original_value = U256::ZERO;
2106 if original_value != *value {
2107 Some((
2108 *key,
2109 StorageSlot {
2110 previous_or_original_value: original_value,
2111 present_value: *value,
2112 },
2113 ))
2114 } else {
2115 None
2116 }
2117 })
2118 .collect();
2119 let info_changed = current_info.is_some();
2120 if info_changed || !storage_changes.is_empty() {
2121 bundle.state.insert(
2122 *addr,
2123 revm::database::BundleAccount {
2124 info: current_info,
2125 original_info: None,
2126 storage: storage_changes,
2127 status: revm::database::AccountStatus::InMemoryChange,
2128 },
2129 );
2130 }
2131 }
2132 }
2133
2134 let _bundle_post = bundle
2135 .state
2136 .get(&ARBOS_STATE_ADDRESS)
2137 .and_then(|a| a.storage.get(&gas_backlog_slot))
2138 .map(|s| (s.present_value, s.previous_or_original_value));
2139
2140 for (_addr, account) in bundle.state.iter_mut() {
2142 account
2143 .storage
2144 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
2145 }
2146
2147 let final_slot = bundle
2148 .state
2149 .get(&ARBOS_STATE_ADDRESS)
2150 .and_then(|a| a.storage.get(&gas_backlog_slot))
2151 .map(|s| s.present_value);
2152 assert!(
2153 final_slot.is_some(),
2154 "VARIANT D FAILED: gas_backlog slot MISSING from bundle"
2155 );
2156 assert_eq!(
2157 final_slot.unwrap(),
2158 U256::from(552756u64 + 357751u64),
2159 "VARIANT D: gas_backlog should be 910507"
2160 );
2161 }
2162
2163 {
2169 struct PrePopulatedDb {
2171 gas_backlog_slot: U256,
2172 pre_existing_backlog: U256,
2173 }
2174
2175 impl Database for PrePopulatedDb {
2176 type Error = std::convert::Infallible;
2177 fn basic(
2178 &mut self,
2179 _address: Address,
2180 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
2181 Ok(Some(revm::state::AccountInfo {
2183 balance: U256::ZERO,
2184 nonce: 1,
2185 code_hash: keccak256([]),
2186 code: None,
2187 account_id: None,
2188 }))
2189 }
2190 fn code_by_hash(
2191 &mut self,
2192 _code_hash: B256,
2193 ) -> Result<revm::state::Bytecode, Self::Error> {
2194 Ok(revm::state::Bytecode::default())
2195 }
2196 fn storage(&mut self, _address: Address, index: U256) -> Result<U256, Self::Error> {
2197 if index == self.gas_backlog_slot {
2199 Ok(self.pre_existing_backlog)
2200 } else {
2201 Ok(U256::ZERO)
2202 }
2203 }
2204 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
2205 Ok(B256::ZERO)
2206 }
2207 }
2208
2209 let pre_existing_backlog = U256::from(552756u64);
2210 let mut state = StateBuilder::new()
2211 .with_database(PrePopulatedDb {
2212 gas_backlog_slot,
2213 pre_existing_backlog,
2214 })
2215 .with_bundle_update()
2216 .build();
2217
2218 let _ = state.load_cache_account(ARBOS_STATE_ADDRESS);
2220
2221 let state_ptr: *mut revm::database::State<PrePopulatedDb> = &mut state;
2222
2223 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
2225 let l2_pricing =
2226 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2227
2228 let current = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2230 assert_eq!(current, 552756, "Should read from DB");
2231
2232 l2_pricing
2234 .update_pricing_model(unsafe { &mut *state_ptr }, 0, 10)
2235 .unwrap();
2236 let _after_start = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2237
2238 {
2240 use revm::DatabaseCommit;
2241 let empty: revm::state::EvmState = Default::default();
2242 state.commit(empty);
2243 }
2244
2245 {
2247 use revm::DatabaseCommit;
2248 let sender = address!("1111111111111111111111111111111111111111");
2249 let _ = state.load_cache_account(sender);
2250 let mut user_changes: revm::state::EvmState = Default::default();
2251 let mut sender_acct = revm::state::Account::default();
2252 sender_acct.info.balance = U256::from(999_000u64);
2253 sender_acct.info.nonce = 1;
2254 sender_acct.mark_touch();
2255 user_changes.insert(sender, sender_acct);
2256 state.commit(user_changes);
2257 }
2258
2259 let l2_pricing2 =
2261 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2262 let _read_before = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2263
2264 l2_pricing2
2265 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
2266 .unwrap();
2267 let _after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2268
2269 let _cache_val = state
2271 .cache
2272 .accounts
2273 .get(&ARBOS_STATE_ADDRESS)
2274 .and_then(|ca| ca.account.as_ref())
2275 .and_then(|a| a.storage.get(&gas_backlog_slot).copied());
2276
2277 state.merge_transitions(BundleRetention::Reverts);
2279 let mut bundle = state.take_bundle();
2280
2281 let _bundle_pre = bundle
2282 .state
2283 .get(&ARBOS_STATE_ADDRESS)
2284 .and_then(|a| a.storage.get(&gas_backlog_slot))
2285 .map(|s| (s.present_value, s.previous_or_original_value));
2286
2287 for (addr, cache_acct) in &state.cache.accounts {
2289 let current_info = cache_acct.account.as_ref().map(|a| a.info.clone());
2290 let current_storage = cache_acct
2291 .account
2292 .as_ref()
2293 .map(|a| &a.storage)
2294 .cloned()
2295 .unwrap_or_default();
2296
2297 if let Some(bundle_acct) = bundle.state.get_mut(addr) {
2298 bundle_acct.info = current_info;
2299 for (key, value) in ¤t_storage {
2300 if let Some(slot) = bundle_acct.storage.get_mut(key) {
2301 slot.present_value = *value;
2302 } else {
2303 let original_value =
2305 if *addr == ARBOS_STATE_ADDRESS && *key == gas_backlog_slot {
2306 pre_existing_backlog
2307 } else {
2308 U256::ZERO
2309 };
2310 if *value != original_value {
2311 bundle_acct.storage.insert(
2312 *key,
2313 StorageSlot {
2314 previous_or_original_value: original_value,
2315 present_value: *value,
2316 },
2317 );
2318 }
2319 }
2320 }
2321 } else {
2322 let storage_changes: revm::database::StorageWithOriginalValues =
2324 current_storage
2325 .iter()
2326 .filter_map(|(key, value)| {
2327 let original_value =
2328 if *addr == ARBOS_STATE_ADDRESS && *key == gas_backlog_slot {
2329 pre_existing_backlog
2330 } else {
2331 U256::ZERO
2332 };
2333 if original_value != *value {
2334 Some((
2335 *key,
2336 StorageSlot {
2337 previous_or_original_value: original_value,
2338 present_value: *value,
2339 },
2340 ))
2341 } else {
2342 None
2343 }
2344 })
2345 .collect();
2346 let info_changed = false; if info_changed || !storage_changes.is_empty() {
2348 bundle.state.insert(
2349 *addr,
2350 revm::database::BundleAccount {
2351 info: current_info,
2352 original_info: None,
2353 storage: storage_changes,
2354 status: revm::database::AccountStatus::Changed,
2355 },
2356 );
2357 }
2358 }
2359 }
2360
2361 let _bundle_post = bundle
2362 .state
2363 .get(&ARBOS_STATE_ADDRESS)
2364 .and_then(|a| a.storage.get(&gas_backlog_slot))
2365 .map(|s| (s.present_value, s.previous_or_original_value));
2366
2367 for (_addr, account) in bundle.state.iter_mut() {
2369 account
2370 .storage
2371 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
2372 }
2373
2374 let final_slot = bundle
2375 .state
2376 .get(&ARBOS_STATE_ADDRESS)
2377 .and_then(|a| a.storage.get(&gas_backlog_slot))
2378 .map(|s| s.present_value);
2379 assert!(
2380 final_slot.is_some(),
2381 "VARIANT E FAILED: gas_backlog slot MISSING from bundle (pre-populated DB)"
2382 );
2383 assert_eq!(
2384 final_slot.unwrap(),
2385 U256::from(552756u64 + 357751u64),
2386 "VARIANT E: gas_backlog should be 910507"
2387 );
2388 }
2389
2390 {
2400 struct PrePopulatedDb2 {
2401 gas_backlog_slot: U256,
2402 pre_existing_backlog: U256,
2403 speed_limit_slot: U256,
2404 speed_limit_value: U256,
2405 }
2406
2407 impl Database for PrePopulatedDb2 {
2408 type Error = std::convert::Infallible;
2409 fn basic(
2410 &mut self,
2411 _address: Address,
2412 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
2413 Ok(Some(revm::state::AccountInfo {
2414 balance: U256::ZERO,
2415 nonce: 1,
2416 code_hash: keccak256([]),
2417 code: None,
2418 account_id: None,
2419 }))
2420 }
2421 fn code_by_hash(
2422 &mut self,
2423 _code_hash: B256,
2424 ) -> Result<revm::state::Bytecode, Self::Error> {
2425 Ok(revm::state::Bytecode::default())
2426 }
2427 fn storage(&mut self, _address: Address, index: U256) -> Result<U256, Self::Error> {
2428 if index == self.gas_backlog_slot {
2429 Ok(self.pre_existing_backlog)
2430 } else if index == self.speed_limit_slot {
2431 Ok(self.speed_limit_value)
2432 } else {
2433 Ok(U256::ZERO)
2434 }
2435 }
2436 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
2437 Ok(B256::ZERO)
2438 }
2439 }
2440
2441 let speed_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 0); let pre_existing_backlog = U256::from(552756u64);
2444
2445 let mut state = StateBuilder::new()
2446 .with_database(PrePopulatedDb2 {
2447 gas_backlog_slot,
2448 pre_existing_backlog,
2449 speed_limit_slot,
2450 speed_limit_value: U256::from(7_000_000u64), })
2452 .with_bundle_update()
2453 .build();
2454
2455 let _ = state.load_cache_account(ARBOS_STATE_ADDRESS);
2456 let state_ptr: *mut revm::database::State<PrePopulatedDb2> = &mut state;
2457
2458 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
2459 let l2_pricing =
2460 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2461
2462 let _initial = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2463
2464 l2_pricing
2467 .update_pricing_model(unsafe { &mut *state_ptr }, 1, 10)
2468 .unwrap();
2469 let _after_drain = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2470
2471 {
2473 use revm::DatabaseCommit;
2474 state.commit(Default::default());
2475 }
2476
2477 let l2_pricing2 =
2479 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2480 l2_pricing2
2481 .grow_backlog(unsafe { &mut *state_ptr }, 357751, MultiGas::default())
2482 .unwrap();
2483 let _after_grow = l2_pricing2.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2484
2485 state.merge_transitions(BundleRetention::Reverts);
2487 let mut bundle = state.take_bundle();
2488
2489 let _bundle_pre = bundle
2490 .state
2491 .get(&ARBOS_STATE_ADDRESS)
2492 .and_then(|a| a.storage.get(&gas_backlog_slot))
2493 .map(|s| (s.present_value, s.previous_or_original_value));
2494
2495 for (_addr, account) in bundle.state.iter_mut() {
2501 account
2502 .storage
2503 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
2504 }
2505
2506 let final_slot = bundle
2507 .state
2508 .get(&ARBOS_STATE_ADDRESS)
2509 .and_then(|a| a.storage.get(&gas_backlog_slot))
2510 .map(|s| (s.present_value, s.previous_or_original_value));
2511
2512 assert!(
2516 final_slot.is_some(),
2517 "VARIANT F FAILED: gas_backlog slot MISSING from bundle (drain+grow)"
2518 );
2519 assert_eq!(
2520 final_slot.unwrap().0,
2521 U256::from(357751u64),
2522 "VARIANT F: gas_backlog should be 357751"
2523 );
2524 }
2525
2526 {
2535 struct PrePopDb3 {
2536 gas_backlog_slot: U256,
2537 speed_limit_slot: U256,
2538 }
2539
2540 impl Database for PrePopDb3 {
2541 type Error = std::convert::Infallible;
2542 fn basic(
2543 &mut self,
2544 _address: Address,
2545 ) -> Result<Option<revm::state::AccountInfo>, Self::Error> {
2546 Ok(Some(revm::state::AccountInfo {
2547 balance: U256::ZERO,
2548 nonce: 1,
2549 code_hash: keccak256([]),
2550 code: None,
2551 account_id: None,
2552 }))
2553 }
2554 fn code_by_hash(
2555 &mut self,
2556 _code_hash: B256,
2557 ) -> Result<revm::state::Bytecode, Self::Error> {
2558 Ok(revm::state::Bytecode::default())
2559 }
2560 fn storage(&mut self, _address: Address, index: U256) -> Result<U256, Self::Error> {
2561 if index == self.gas_backlog_slot {
2562 Ok(U256::from(552756u64))
2563 } else if index == self.speed_limit_slot {
2564 Ok(U256::from(7_000_000u64))
2565 } else {
2566 Ok(U256::ZERO)
2567 }
2568 }
2569 fn block_hash(&mut self, _number: u64) -> Result<B256, Self::Error> {
2570 Ok(B256::ZERO)
2571 }
2572 }
2573
2574 let speed_limit_slot = arb_storage::storage_key_map(l2_base.as_slice(), 0);
2575
2576 let mut state = StateBuilder::new()
2577 .with_database(PrePopDb3 {
2578 gas_backlog_slot,
2579 speed_limit_slot,
2580 })
2581 .with_bundle_update()
2582 .build();
2583
2584 let _ = state.load_cache_account(ARBOS_STATE_ADDRESS);
2585 let state_ptr: *mut revm::database::State<PrePopDb3> = &mut state;
2586
2587 let backing = Storage::new(unsafe { &mut *state_ptr }, B256::ZERO);
2588 let l2_pricing =
2589 super::super::open_l2_pricing_state(backing.open_sub_storage(&[1]), 10);
2590
2591 let _initial = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2592
2593 l2_pricing
2595 .update_pricing_model(unsafe { &mut *state_ptr }, 1, 10)
2596 .unwrap();
2597 let after_drain = l2_pricing.gas_backlog(unsafe { &mut *state_ptr }).unwrap();
2598 assert_eq!(after_drain, 0);
2599
2600 state.merge_transitions(BundleRetention::Reverts);
2602 let mut bundle = state.take_bundle();
2603
2604 let _pre_filter = bundle
2605 .state
2606 .get(&ARBOS_STATE_ADDRESS)
2607 .and_then(|a| a.storage.get(&gas_backlog_slot))
2608 .map(|s| (s.present_value, s.previous_or_original_value));
2609
2610 for (_addr, account) in bundle.state.iter_mut() {
2612 account
2613 .storage
2614 .retain(|_key, slot| slot.present_value != slot.previous_or_original_value);
2615 }
2616
2617 let final_slot = bundle
2618 .state
2619 .get(&ARBOS_STATE_ADDRESS)
2620 .and_then(|a| a.storage.get(&gas_backlog_slot))
2621 .map(|s| (s.present_value, s.previous_or_original_value));
2622
2623 assert!(
2625 final_slot.is_some(),
2626 "VARIANT G FAILED: drain-to-0 write was lost!"
2627 );
2628 }
2629 }
2630}