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