revm_context_interface/
host.rs

1//! Host interface for external blockchain state access.
2
3use crate::{
4    cfg::GasParams,
5    context::{SStoreResult, SelfDestructResult, StateLoad},
6    journaled_state::{AccountInfoLoad, AccountLoad},
7};
8use auto_impl::auto_impl;
9use primitives::{hardfork::SpecId, Address, Bytes, Log, StorageKey, StorageValue, B256, U256};
10use state::Bytecode;
11
12/// Error that can happen when loading account info.
13#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15pub enum LoadError {
16    /// Database error.
17    DBError,
18    /// Cold load skipped.
19    ColdLoadSkipped,
20}
21
22/// Host trait with all methods that are needed by the Interpreter.
23///
24/// This trait is implemented for all types that have `ContextTr` trait.
25///
26/// There are few groups of functions which are Block, Transaction, Config, Database and Journal functions.
27#[auto_impl(&mut, Box)]
28pub trait Host {
29    /* Block */
30
31    /// Block basefee, calls ContextTr::block().basefee()
32    fn basefee(&self) -> U256;
33    /// Block blob gasprice, calls `ContextTr::block().blob_gasprice()`
34    fn blob_gasprice(&self) -> U256;
35    /// Block gas limit, calls ContextTr::block().gas_limit()
36    fn gas_limit(&self) -> U256;
37    /// Block difficulty, calls ContextTr::block().difficulty()
38    fn difficulty(&self) -> U256;
39    /// Block prevrandao, calls ContextTr::block().prevrandao()
40    fn prevrandao(&self) -> Option<U256>;
41    /// Block number, calls ContextTr::block().number()
42    fn block_number(&self) -> U256;
43    /// Block timestamp, calls ContextTr::block().timestamp()
44    fn timestamp(&self) -> U256;
45    /// Block beneficiary, calls ContextTr::block().beneficiary()
46    fn beneficiary(&self) -> Address;
47    /// Chain id, calls ContextTr::cfg().chain_id()
48    fn chain_id(&self) -> U256;
49
50    /* Transaction */
51
52    /// Transaction effective gas price, calls `ContextTr::tx().effective_gas_price(basefee as u128)`
53    fn effective_gas_price(&self) -> U256;
54    /// Transaction caller, calls `ContextTr::tx().caller()`
55    fn caller(&self) -> Address;
56    /// Transaction blob hash, calls `ContextTr::tx().blob_hash(number)`
57    fn blob_hash(&self, number: usize) -> Option<U256>;
58
59    /* Config */
60
61    /// Max initcode size, calls `ContextTr::cfg().max_code_size().saturating_mul(2)`
62    fn max_initcode_size(&self) -> usize;
63
64    /// Gas params contains the dynamic gas constants for the EVM.
65    fn gas_params(&self) -> &GasParams;
66
67    /* Database */
68
69    /// Block hash, calls `ContextTr::journal_mut().db().block_hash(number)`
70    fn block_hash(&mut self, number: u64) -> Option<B256>;
71
72    /* Journal */
73
74    /// Selfdestruct account, calls `ContextTr::journal_mut().selfdestruct(address, target)`
75    fn selfdestruct(
76        &mut self,
77        address: Address,
78        target: Address,
79        skip_cold_load: bool,
80    ) -> Result<StateLoad<SelfDestructResult>, LoadError>;
81
82    /// Log, calls `ContextTr::journal_mut().log(log)`
83    fn log(&mut self, log: Log);
84
85    /// Sstore with optional fetch from database. Return none if the value is cold or if there is db error.
86    fn sstore_skip_cold_load(
87        &mut self,
88        address: Address,
89        key: StorageKey,
90        value: StorageValue,
91        skip_cold_load: bool,
92    ) -> Result<StateLoad<SStoreResult>, LoadError>;
93
94    /// Sstore, calls `ContextTr::journal_mut().sstore(address, key, value)`
95    fn sstore(
96        &mut self,
97        address: Address,
98        key: StorageKey,
99        value: StorageValue,
100    ) -> Option<StateLoad<SStoreResult>> {
101        self.sstore_skip_cold_load(address, key, value, false).ok()
102    }
103
104    /// Sload with optional fetch from database. Return none if the value is cold or if there is db error.
105    fn sload_skip_cold_load(
106        &mut self,
107        address: Address,
108        key: StorageKey,
109        skip_cold_load: bool,
110    ) -> Result<StateLoad<StorageValue>, LoadError>;
111
112    /// Sload, calls `ContextTr::journal_mut().sload(address, key)`
113    fn sload(&mut self, address: Address, key: StorageKey) -> Option<StateLoad<StorageValue>> {
114        self.sload_skip_cold_load(address, key, false).ok()
115    }
116
117    /// Tstore, calls `ContextTr::journal_mut().tstore(address, key, value)`
118    fn tstore(&mut self, address: Address, key: StorageKey, value: StorageValue);
119
120    /// Tload, calls `ContextTr::journal_mut().tload(address, key)`
121    fn tload(&mut self, address: Address, key: StorageKey) -> StorageValue;
122
123    /// Main function to load account info.
124    ///
125    /// If load_code is true, it will load the code fetching it from the database if not done before.
126    ///
127    /// If skip_cold_load is true, it will not load the account if it is cold. This is needed to short circuit
128    /// the load if there is not enough gas.
129    ///
130    /// Returns AccountInfo, is_cold and is_empty.
131    fn load_account_info_skip_cold_load(
132        &mut self,
133        address: Address,
134        load_code: bool,
135        skip_cold_load: bool,
136    ) -> Result<AccountInfoLoad<'_>, LoadError>;
137
138    /// Balance, calls `ContextTr::journal_mut().load_account(address)`
139    #[inline]
140    fn balance(&mut self, address: Address) -> Option<StateLoad<U256>> {
141        self.load_account_info_skip_cold_load(address, false, false)
142            .ok()
143            .map(|load| load.into_state_load(|i| i.balance))
144    }
145
146    /// Load account delegated, calls `ContextTr::journal_mut().load_account_delegated(address)`
147    #[inline]
148    fn load_account_delegated(&mut self, address: Address) -> Option<StateLoad<AccountLoad>> {
149        let account = self
150            .load_account_info_skip_cold_load(address, true, false)
151            .ok()?;
152
153        let mut account_load = StateLoad::new(
154            AccountLoad {
155                is_delegate_account_cold: None,
156                is_empty: account.is_empty,
157            },
158            account.is_cold,
159        );
160
161        // load delegate code if account is EIP-7702
162        if let Some(Bytecode::Eip7702(code)) = &account.code {
163            let address = code.address();
164            let delegate_account = self
165                .load_account_info_skip_cold_load(address, true, false)
166                .ok()?;
167            account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
168            account_load.data.is_empty = delegate_account.is_empty;
169        }
170
171        Some(account_load)
172    }
173
174    /// Load account code, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false.
175    #[inline]
176    fn load_account_code(&mut self, address: Address) -> Option<StateLoad<Bytes>> {
177        self.load_account_info_skip_cold_load(address, true, false)
178            .ok()
179            .map(|load| {
180                load.into_state_load(|i| {
181                    i.code
182                        .as_ref()
183                        .map(|b| b.original_bytes())
184                        .unwrap_or_default()
185                })
186            })
187    }
188
189    /// Load account code hash, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false.
190    #[inline]
191    fn load_account_code_hash(&mut self, address: Address) -> Option<StateLoad<B256>> {
192        self.load_account_info_skip_cold_load(address, false, false)
193            .ok()
194            .map(|load| {
195                load.into_state_load(|i| {
196                    if i.is_empty() {
197                        B256::ZERO
198                    } else {
199                        i.code_hash
200                    }
201                })
202            })
203    }
204}
205
206/// Dummy host that implements [`Host`] trait and  returns all default values.
207#[derive(Default, Debug)]
208pub struct DummyHost {
209    gas_params: GasParams,
210}
211
212impl DummyHost {
213    /// Create a new dummy host with the given spec.
214    pub fn new(spec: SpecId) -> Self {
215        Self {
216            gas_params: GasParams::new_spec(spec).clone(),
217        }
218    }
219}
220
221impl Host for DummyHost {
222    fn basefee(&self) -> U256 {
223        U256::ZERO
224    }
225
226    fn blob_gasprice(&self) -> U256 {
227        U256::ZERO
228    }
229
230    fn gas_limit(&self) -> U256 {
231        U256::ZERO
232    }
233
234    fn gas_params(&self) -> &GasParams {
235        &self.gas_params
236    }
237
238    fn difficulty(&self) -> U256 {
239        U256::ZERO
240    }
241
242    fn prevrandao(&self) -> Option<U256> {
243        None
244    }
245
246    fn block_number(&self) -> U256 {
247        U256::ZERO
248    }
249
250    fn timestamp(&self) -> U256 {
251        U256::ZERO
252    }
253
254    fn beneficiary(&self) -> Address {
255        Address::ZERO
256    }
257
258    fn chain_id(&self) -> U256 {
259        U256::ZERO
260    }
261
262    fn effective_gas_price(&self) -> U256 {
263        U256::ZERO
264    }
265
266    fn caller(&self) -> Address {
267        Address::ZERO
268    }
269
270    fn blob_hash(&self, _number: usize) -> Option<U256> {
271        None
272    }
273
274    fn max_initcode_size(&self) -> usize {
275        0
276    }
277
278    fn block_hash(&mut self, _number: u64) -> Option<B256> {
279        None
280    }
281
282    fn selfdestruct(
283        &mut self,
284        _address: Address,
285        _target: Address,
286        _skip_cold_load: bool,
287    ) -> Result<StateLoad<SelfDestructResult>, LoadError> {
288        Err(LoadError::ColdLoadSkipped)
289    }
290
291    fn log(&mut self, _log: Log) {}
292
293    fn tstore(&mut self, _address: Address, _key: StorageKey, _value: StorageValue) {}
294
295    fn tload(&mut self, _address: Address, _key: StorageKey) -> StorageValue {
296        StorageValue::ZERO
297    }
298
299    fn load_account_info_skip_cold_load(
300        &mut self,
301        _address: Address,
302        _load_code: bool,
303        _skip_cold_load: bool,
304    ) -> Result<AccountInfoLoad<'_>, LoadError> {
305        Err(LoadError::DBError)
306    }
307
308    fn sstore_skip_cold_load(
309        &mut self,
310        _address: Address,
311        _key: StorageKey,
312        _value: StorageValue,
313        _skip_cold_load: bool,
314    ) -> Result<StateLoad<SStoreResult>, LoadError> {
315        Err(LoadError::DBError)
316    }
317
318    fn sload_skip_cold_load(
319        &mut self,
320        _address: Address,
321        _key: StorageKey,
322        _skip_cold_load: bool,
323    ) -> Result<StateLoad<StorageValue>, LoadError> {
324        Err(LoadError::DBError)
325    }
326}