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