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(address) = account.code.as_ref().and_then(Bytecode::eip7702_address) {
165            let delegate_account = self
166                .load_account_info_skip_cold_load(address, true, false)
167                .ok()?;
168            account_load.data.is_delegate_account_cold = Some(delegate_account.is_cold);
169            account_load.data.is_empty = delegate_account.is_empty;
170        }
171
172        Some(account_load)
173    }
174
175    /// Load account code, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false.
176    #[inline]
177    fn load_account_code(&mut self, address: Address) -> Option<StateLoad<Bytes>> {
178        self.load_account_info_skip_cold_load(address, true, false)
179            .ok()
180            .map(|load| {
181                load.into_state_load(|i| {
182                    i.code
183                        .as_ref()
184                        .map(|b| b.original_bytes())
185                        .unwrap_or_default()
186                })
187            })
188    }
189
190    /// Load account code hash, calls [`Host::load_account_info_skip_cold_load`] with `load_code` set to false.
191    #[inline]
192    fn load_account_code_hash(&mut self, address: Address) -> Option<StateLoad<B256>> {
193        self.load_account_info_skip_cold_load(address, false, false)
194            .ok()
195            .map(|load| {
196                load.into_state_load(|i| {
197                    if i.is_empty() {
198                        B256::ZERO
199                    } else {
200                        i.code_hash
201                    }
202                })
203            })
204    }
205}
206
207/// Dummy host that implements [`Host`] trait and  returns all default values.
208#[derive(Default, Debug)]
209pub struct DummyHost {
210    gas_params: GasParams,
211}
212
213impl DummyHost {
214    /// Create a new dummy host with the given spec.
215    pub fn new(spec: SpecId) -> Self {
216        Self {
217            gas_params: GasParams::new_spec(spec),
218        }
219    }
220}
221
222impl Host for DummyHost {
223    fn basefee(&self) -> U256 {
224        U256::ZERO
225    }
226
227    fn blob_gasprice(&self) -> U256 {
228        U256::ZERO
229    }
230
231    fn gas_limit(&self) -> U256 {
232        U256::ZERO
233    }
234
235    fn gas_params(&self) -> &GasParams {
236        &self.gas_params
237    }
238
239    fn difficulty(&self) -> U256 {
240        U256::ZERO
241    }
242
243    fn prevrandao(&self) -> Option<U256> {
244        None
245    }
246
247    fn block_number(&self) -> U256 {
248        U256::ZERO
249    }
250
251    fn timestamp(&self) -> U256 {
252        U256::ZERO
253    }
254
255    fn beneficiary(&self) -> Address {
256        Address::ZERO
257    }
258
259    fn slot_num(&self) -> U256 {
260        U256::ZERO
261    }
262
263    fn chain_id(&self) -> U256 {
264        U256::ZERO
265    }
266
267    fn effective_gas_price(&self) -> U256 {
268        U256::ZERO
269    }
270
271    fn caller(&self) -> Address {
272        Address::ZERO
273    }
274
275    fn blob_hash(&self, _number: usize) -> Option<U256> {
276        None
277    }
278
279    fn max_initcode_size(&self) -> usize {
280        0
281    }
282
283    fn block_hash(&mut self, _number: u64) -> Option<B256> {
284        None
285    }
286
287    fn selfdestruct(
288        &mut self,
289        _address: Address,
290        _target: Address,
291        _skip_cold_load: bool,
292    ) -> Result<StateLoad<SelfDestructResult>, LoadError> {
293        Err(LoadError::ColdLoadSkipped)
294    }
295
296    fn log(&mut self, _log: Log) {}
297
298    fn tstore(&mut self, _address: Address, _key: StorageKey, _value: StorageValue) {}
299
300    fn tload(&mut self, _address: Address, _key: StorageKey) -> StorageValue {
301        StorageValue::ZERO
302    }
303
304    fn load_account_info_skip_cold_load(
305        &mut self,
306        _address: Address,
307        _load_code: bool,
308        _skip_cold_load: bool,
309    ) -> Result<AccountInfoLoad<'_>, LoadError> {
310        Err(LoadError::DBError)
311    }
312
313    fn sstore_skip_cold_load(
314        &mut self,
315        _address: Address,
316        _key: StorageKey,
317        _value: StorageValue,
318        _skip_cold_load: bool,
319    ) -> Result<StateLoad<SStoreResult>, LoadError> {
320        Err(LoadError::DBError)
321    }
322
323    fn sload_skip_cold_load(
324        &mut self,
325        _address: Address,
326        _key: StorageKey,
327        _skip_cold_load: bool,
328    ) -> Result<StateLoad<StorageValue>, LoadError> {
329        Err(LoadError::DBError)
330    }
331}