Skip to main content

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