arb_storage/
backend.rs

1use alloy_primitives::{Address, U256};
2use arb_storage_errors::{DatabaseError, StorageError};
3use revm::Database;
4
5use crate::{
6    state_ops::{read_storage_at, write_storage_at},
7    storage::Storage,
8};
9
10/// Abstraction over the two backing stores `arb-storage` accessor types are
11/// driven from: the block executor's `&mut State<D>` and the precompile
12/// handler's `&mut EvmInternals<'_>`.
13///
14/// The trait sits beneath the typed accessor layer (`StorageBackedX`) so the
15/// same descriptors serve both call paths without having to fork the
16/// accessor API.
17pub trait StorageBackend: SystemStateBackend {
18    /// Reads the value at `(account, slot)`. Reads through `StorageBackend`
19    /// follow the host's normal storage path (journaled when invoked on
20    /// `EvmInternals`); for non-journaled reads see [`SystemStateBackend`].
21    fn sload(
22        &mut self,
23        account: Address,
24        slot: U256,
25    ) -> Result<U256, <Self as SystemStateBackend>::Error>;
26
27    /// Writes `value` to `(account, slot)`.
28    fn sstore(
29        &mut self,
30        account: Address,
31        slot: U256,
32        value: U256,
33    ) -> Result<(), <Self as SystemStateBackend>::Error>;
34}
35
36/// Non-journaled read access to system state.
37///
38/// Reads bypass the EVM journal: no access-list entry, no cold/warm gas
39/// tracking, no account-touch propagation. Use for ArbOS state and other
40/// system reads with no consensus relationship to user-visible EVM storage.
41///
42/// Writes are NOT in this trait. System-state mutations that happen inside
43/// a user-callable precompile must remain journaled so they revert with
44/// the outer tx on failure (matching geth's StateDB semantics).
45/// Writes stay on [`StorageBackend`].
46pub trait SystemStateBackend {
47    /// Concrete failure type produced by the backend. Convertible into
48    /// [`StorageError`] so callers can stay uniform.
49    type Error: Into<StorageError>;
50
51    /// Reads the value at `(account, slot)` without journaling.
52    fn sload_system(&mut self, account: Address, slot: U256) -> Result<U256, Self::Error>;
53}
54
55impl<D: Database> StorageBackend for revm::database::State<D> {
56    fn sload(&mut self, account: Address, slot: U256) -> Result<U256, StorageError> {
57        read_storage_at(self, account, slot)
58    }
59
60    fn sstore(&mut self, account: Address, slot: U256, value: U256) -> Result<(), StorageError> {
61        write_storage_at(self, account, slot, value)
62    }
63}
64
65impl<D: Database> SystemStateBackend for revm::database::State<D> {
66    type Error = StorageError;
67
68    fn sload_system(&mut self, account: Address, slot: U256) -> Result<U256, Self::Error> {
69        read_storage_at(self, account, slot)
70    }
71}
72
73impl StorageBackend for alloy_evm::EvmInternals<'_> {
74    fn sload(&mut self, account: Address, slot: U256) -> Result<U256, StorageError> {
75        alloy_evm::EvmInternals::sload(self, account, slot)
76            .map(|state_load| state_load.data)
77            .map_err(|e| StorageError::Database(DatabaseError::custom(e)))
78    }
79
80    fn sstore(&mut self, account: Address, slot: U256, value: U256) -> Result<(), StorageError> {
81        alloy_evm::EvmInternals::sstore(self, account, slot, value)
82            .map(|_| ())
83            .map_err(|e| StorageError::Database(DatabaseError::custom(e)))
84    }
85}
86
87impl SystemStateBackend for alloy_evm::EvmInternals<'_> {
88    type Error = StorageError;
89
90    fn sload_system(&mut self, account: Address, slot: U256) -> Result<U256, Self::Error> {
91        // Reads route through the journal so that in-flight writes within the
92        // current tx are observed, matching geth-StateDB semantics. The
93        // journal's access-list bookkeeping is unavoidable on this path; the
94        // perf win comes from the per-block ArbosState cache reusing the
95        // descriptor across calls instead of reconstructing it.
96        alloy_evm::EvmInternals::sload(self, account, slot)
97            .map(|state_load| state_load.data)
98            .map_err(|e| StorageError::Database(DatabaseError::custom(e)))
99    }
100}
101
102impl<D: Database> StorageBackend for Storage<'_, D> {
103    fn sload(&mut self, account: Address, slot: U256) -> Result<U256, StorageError> {
104        // SAFETY: see `Storage` struct-level invariant.
105        let state = unsafe { self.state_mut() };
106        read_storage_at(state, account, slot)
107    }
108
109    fn sstore(&mut self, account: Address, slot: U256, value: U256) -> Result<(), StorageError> {
110        // SAFETY: see `Storage` struct-level invariant.
111        let state = unsafe { self.state_mut() };
112        write_storage_at(state, account, slot, value)
113    }
114}
115
116impl<D: Database> SystemStateBackend for Storage<'_, D> {
117    type Error = StorageError;
118
119    fn sload_system(&mut self, account: Address, slot: U256) -> Result<U256, Self::Error> {
120        // SAFETY: see `Storage` struct-level invariant.
121        let state = unsafe { self.state_mut() };
122        read_storage_at(state, account, slot)
123    }
124}