revm_context/
cfg.rs

1//! This module contains [`CfgEnv`] and implements [`Cfg`] trait for it.
2pub use context_interface::Cfg;
3
4use primitives::{eip170, eip3860, eip7825, hardfork::SpecId};
5/// EVM configuration
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7#[derive(Clone, Debug, Eq, PartialEq)]
8#[non_exhaustive]
9pub struct CfgEnv<SPEC = SpecId> {
10    /// Chain ID of the EVM. Used in CHAINID opcode and transaction's chain ID check.
11    ///
12    /// Chain ID is introduced EIP-155.
13    pub chain_id: u64,
14
15    /// Whether to check the transaction's chain ID.
16    ///
17    /// If set to `false`, the transaction's chain ID check will be skipped.
18    pub tx_chain_id_check: bool,
19
20    /// Specification for EVM represent the hardfork
21    pub spec: SPEC,
22    /// Contract code size limit override.
23    ///
24    /// If None, the limit will be determined by the SpecId (EIP-170 or EIP-7907) at runtime.
25    /// If Some, this specific limit will be used regardless of SpecId.
26    ///
27    /// Useful to increase this because of tests.
28    pub limit_contract_code_size: Option<usize>,
29    /// Contract initcode size limit override.
30    ///
31    /// If None, the limit will check if `limit_contract_code_size` is set.
32    /// If it is set, it will double it for a limit.
33    /// If it is not set, the limit will be determined by the SpecId (EIP-170 or EIP-7907) at runtime.
34    ///
35    /// Useful to increase this because of tests.
36    pub limit_contract_initcode_size: Option<usize>,
37    /// Skips the nonce validation against the account's nonce
38    pub disable_nonce_check: bool,
39    /// Blob max count. EIP-7840 Add blob schedule to EL config files.
40    ///
41    /// If this config is not set, the check for max blobs will be skipped.
42    pub max_blobs_per_tx: Option<u64>,
43    /// Blob base fee update fraction. EIP-4844 Blob base fee update fraction.
44    ///
45    /// If this config is not set, the blob base fee update fraction will be set to the default value.
46    /// See also [CfgEnv::blob_base_fee_update_fraction].
47    ///
48    /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`]
49    /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`].
50    pub blob_base_fee_update_fraction: Option<u64>,
51    /// Configures the gas limit cap for the transaction.
52    ///
53    /// If `None`, default value defined by spec will be used.
54    ///
55    /// Introduced in Osaka in [EIP-7825: Transaction Gas Limit Cap](https://eips.ethereum.org/EIPS/eip-7825)
56    /// with initials cap of 30M.
57    pub tx_gas_limit_cap: Option<u64>,
58    /// A hard memory limit in bytes beyond which
59    /// [OutOfGasError::Memory][context_interface::result::OutOfGasError::Memory] cannot be resized.
60    ///
61    /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to
62    /// a sane value to prevent memory allocation panics.
63    ///
64    /// Defaults to `2^32 - 1` bytes per EIP-1985.
65    #[cfg(feature = "memory_limit")]
66    pub memory_limit: u64,
67    /// Skip balance checks if `true`
68    ///
69    /// Adds transaction cost to balance to ensure execution doesn't fail.
70    ///
71    /// By default, it is set to `false`.
72    #[cfg(feature = "optional_balance_check")]
73    pub disable_balance_check: bool,
74    /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit.
75    ///
76    /// To that end, you can disable the block gas limit validation.
77    ///
78    /// By default, it is set to `false`.
79    #[cfg(feature = "optional_block_gas_limit")]
80    pub disable_block_gas_limit: bool,
81    /// EIP-3541 rejects the creation of contracts that starts with 0xEF
82    ///
83    /// This is useful for chains that do not implement EIP-3541.
84    ///
85    /// By default, it is set to `false`.
86    #[cfg(feature = "optional_eip3541")]
87    pub disable_eip3541: bool,
88    /// EIP-3607 rejects transactions from senders with deployed code
89    ///
90    /// In development, it can be desirable to simulate calls from contracts, which this setting allows.
91    ///
92    /// By default, it is set to `false`.
93    #[cfg(feature = "optional_eip3607")]
94    pub disable_eip3607: bool,
95    /// EIP-7623 increases calldata cost.
96    ///
97    /// This EIP can be considered irrelevant in the context of an EVM-compatible L2 rollup,
98    /// if it does not make use of blobs.
99    ///
100    /// By default, it is set to `false`.
101    #[cfg(feature = "optional_eip7623")]
102    pub disable_eip7623: bool,
103    /// Disables base fee checks for EIP-1559 transactions
104    ///
105    /// This is useful for testing method calls with zero gas price.
106    ///
107    /// By default, it is set to `false`.
108    #[cfg(feature = "optional_no_base_fee")]
109    pub disable_base_fee: bool,
110    /// Disables "max fee must be less than or equal to max priority fee" check for EIP-1559 transactions.
111    /// This is useful because some chains (e.g. Arbitrum) do not enforce this check.
112    /// By default, it is set to `false`.
113    #[cfg(feature = "optional_priority_fee_check")]
114    pub disable_priority_fee_check: bool,
115    /// Disables fee charging for transactions.
116    /// This is useful when executing `eth_call` for example, on OP-chains where setting the base fee
117    /// to 0 isn't sufficient.
118    /// By default, it is set to `false`.
119    #[cfg(feature = "optional_fee_charge")]
120    pub disable_fee_charge: bool,
121}
122
123impl CfgEnv {
124    /// Creates new `CfgEnv` with default values.
125    pub fn new() -> Self {
126        Self::default()
127    }
128}
129
130impl<SPEC: Into<SpecId> + Copy> CfgEnv<SPEC> {
131    /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction].
132    ///
133    /// If this field is not set, return the default value for the spec.
134    ///
135    /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`]
136    /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`].
137    pub fn blob_base_fee_update_fraction(&mut self) -> u64 {
138        self.blob_base_fee_update_fraction.unwrap_or_else(|| {
139            let spec: SpecId = self.spec.into();
140            if spec.is_enabled_in(SpecId::PRAGUE) {
141                primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
142            } else {
143                primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN
144            }
145        })
146    }
147}
148
149impl<SPEC> CfgEnv<SPEC> {
150    /// Create new `CfgEnv` with default values and specified spec.
151    pub fn new_with_spec(spec: SPEC) -> Self {
152        Self {
153            chain_id: 1,
154            tx_chain_id_check: true,
155            limit_contract_code_size: None,
156            limit_contract_initcode_size: None,
157            spec,
158            disable_nonce_check: false,
159            max_blobs_per_tx: None,
160            tx_gas_limit_cap: None,
161            blob_base_fee_update_fraction: None,
162            #[cfg(feature = "memory_limit")]
163            memory_limit: (1 << 32) - 1,
164            #[cfg(feature = "optional_balance_check")]
165            disable_balance_check: false,
166            #[cfg(feature = "optional_block_gas_limit")]
167            disable_block_gas_limit: false,
168            #[cfg(feature = "optional_eip3541")]
169            disable_eip3541: false,
170            #[cfg(feature = "optional_eip3607")]
171            disable_eip3607: false,
172            #[cfg(feature = "optional_eip7623")]
173            disable_eip7623: false,
174            #[cfg(feature = "optional_no_base_fee")]
175            disable_base_fee: false,
176            #[cfg(feature = "optional_priority_fee_check")]
177            disable_priority_fee_check: false,
178            #[cfg(feature = "optional_fee_charge")]
179            disable_fee_charge: false,
180        }
181    }
182
183    /// Consumes `self` and returns a new `CfgEnv` with the specified chain ID.
184    pub fn with_chain_id(mut self, chain_id: u64) -> Self {
185        self.chain_id = chain_id;
186        self
187    }
188
189    /// Enables the transaction's chain ID check.
190    pub fn enable_tx_chain_id_check(mut self) -> Self {
191        self.tx_chain_id_check = true;
192        self
193    }
194
195    /// Disables the transaction's chain ID check.
196    pub fn disable_tx_chain_id_check(mut self) -> Self {
197        self.tx_chain_id_check = false;
198        self
199    }
200
201    /// Consumes `self` and returns a new `CfgEnv` with the specified spec.
202    pub fn with_spec<OSPEC: Into<SpecId>>(self, spec: OSPEC) -> CfgEnv<OSPEC> {
203        CfgEnv {
204            chain_id: self.chain_id,
205            tx_chain_id_check: self.tx_chain_id_check,
206            limit_contract_code_size: self.limit_contract_code_size,
207            limit_contract_initcode_size: self.limit_contract_initcode_size,
208            spec,
209            disable_nonce_check: self.disable_nonce_check,
210            tx_gas_limit_cap: self.tx_gas_limit_cap,
211            max_blobs_per_tx: self.max_blobs_per_tx,
212            blob_base_fee_update_fraction: self.blob_base_fee_update_fraction,
213            #[cfg(feature = "memory_limit")]
214            memory_limit: self.memory_limit,
215            #[cfg(feature = "optional_balance_check")]
216            disable_balance_check: self.disable_balance_check,
217            #[cfg(feature = "optional_block_gas_limit")]
218            disable_block_gas_limit: self.disable_block_gas_limit,
219            #[cfg(feature = "optional_eip3541")]
220            disable_eip3541: self.disable_eip3541,
221            #[cfg(feature = "optional_eip3607")]
222            disable_eip3607: self.disable_eip3607,
223            #[cfg(feature = "optional_eip7623")]
224            disable_eip7623: self.disable_eip7623,
225            #[cfg(feature = "optional_no_base_fee")]
226            disable_base_fee: self.disable_base_fee,
227            #[cfg(feature = "optional_priority_fee_check")]
228            disable_priority_fee_check: self.disable_priority_fee_check,
229            #[cfg(feature = "optional_fee_charge")]
230            disable_fee_charge: self.disable_fee_charge,
231        }
232    }
233
234    /// Sets the blob target
235    pub fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
236        self.set_max_blobs_per_tx(max_blobs_per_tx);
237        self
238    }
239
240    /// Sets the blob target
241    pub fn set_max_blobs_per_tx(&mut self, max_blobs_per_tx: u64) {
242        self.max_blobs_per_tx = Some(max_blobs_per_tx);
243    }
244
245    /// Clears the blob target and max count over hardforks.
246    pub fn clear_max_blobs_per_tx(&mut self) {
247        self.max_blobs_per_tx = None;
248    }
249
250    /// Sets the disable priority fee check flag.
251    #[cfg(feature = "optional_priority_fee_check")]
252    pub fn with_disable_priority_fee_check(mut self, disable: bool) -> Self {
253        self.disable_priority_fee_check = disable;
254        self
255    }
256
257    /// Sets the disable fee charge flag.
258    #[cfg(feature = "optional_fee_charge")]
259    pub fn with_disable_fee_charge(mut self, disable: bool) -> Self {
260        self.disable_fee_charge = disable;
261        self
262    }
263
264    /// Sets the disable eip7623 flag.
265    #[cfg(feature = "optional_eip7623")]
266    pub fn with_disable_eip7623(mut self, disable: bool) -> Self {
267        self.disable_eip7623 = disable;
268        self
269    }
270}
271
272impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
273    type Spec = SPEC;
274
275    #[inline]
276    fn chain_id(&self) -> u64 {
277        self.chain_id
278    }
279
280    #[inline]
281    fn spec(&self) -> Self::Spec {
282        self.spec
283    }
284
285    #[inline]
286    fn tx_chain_id_check(&self) -> bool {
287        self.tx_chain_id_check
288    }
289
290    #[inline]
291    fn tx_gas_limit_cap(&self) -> u64 {
292        self.tx_gas_limit_cap
293            .unwrap_or(if self.spec.into().is_enabled_in(SpecId::OSAKA) {
294                eip7825::TX_GAS_LIMIT_CAP
295            } else {
296                u64::MAX
297            })
298    }
299
300    #[inline]
301    fn max_blobs_per_tx(&self) -> Option<u64> {
302        self.max_blobs_per_tx
303    }
304
305    fn max_code_size(&self) -> usize {
306        self.limit_contract_code_size
307            .unwrap_or(eip170::MAX_CODE_SIZE)
308    }
309
310    fn max_initcode_size(&self) -> usize {
311        self.limit_contract_initcode_size
312            .or_else(|| {
313                self.limit_contract_code_size
314                    .map(|size| size.saturating_mul(2))
315            })
316            .unwrap_or(eip3860::MAX_INITCODE_SIZE)
317    }
318
319    fn is_eip3541_disabled(&self) -> bool {
320        cfg_if::cfg_if! {
321            if #[cfg(feature = "optional_eip3541")] {
322                self.disable_eip3541
323            } else {
324                false
325            }
326        }
327    }
328
329    fn is_eip3607_disabled(&self) -> bool {
330        cfg_if::cfg_if! {
331            if #[cfg(feature = "optional_eip3607")] {
332                self.disable_eip3607
333            } else {
334                false
335            }
336        }
337    }
338
339    fn is_eip7623_disabled(&self) -> bool {
340        cfg_if::cfg_if! {
341            if #[cfg(feature = "optional_eip7623")] {
342                self.disable_eip7623
343            } else {
344                false
345            }
346        }
347    }
348
349    fn is_balance_check_disabled(&self) -> bool {
350        cfg_if::cfg_if! {
351            if #[cfg(feature = "optional_balance_check")] {
352                self.disable_balance_check
353            } else {
354                false
355            }
356        }
357    }
358
359    /// Returns `true` if the block gas limit is disabled.
360    fn is_block_gas_limit_disabled(&self) -> bool {
361        cfg_if::cfg_if! {
362            if #[cfg(feature = "optional_block_gas_limit")] {
363                self.disable_block_gas_limit
364            } else {
365                false
366            }
367        }
368    }
369
370    fn is_nonce_check_disabled(&self) -> bool {
371        self.disable_nonce_check
372    }
373
374    fn is_base_fee_check_disabled(&self) -> bool {
375        cfg_if::cfg_if! {
376            if #[cfg(feature = "optional_no_base_fee")] {
377                self.disable_base_fee
378            } else {
379                false
380            }
381        }
382    }
383
384    fn is_priority_fee_check_disabled(&self) -> bool {
385        cfg_if::cfg_if! {
386            if #[cfg(feature = "optional_priority_fee_check")] {
387                self.disable_priority_fee_check
388            } else {
389                false
390            }
391        }
392    }
393
394    fn is_fee_charge_disabled(&self) -> bool {
395        cfg_if::cfg_if! {
396            if #[cfg(feature = "optional_fee_charge")] {
397                self.disable_fee_charge
398            } else {
399                false
400            }
401        }
402    }
403}
404
405impl<SPEC: Default> Default for CfgEnv<SPEC> {
406    fn default() -> Self {
407        Self::new_with_spec(SPEC::default())
408    }
409}
410
411#[cfg(test)]
412mod test {
413    use super::*;
414
415    #[test]
416    fn blob_max_and_target_count() {
417        let cfg: CfgEnv = Default::default();
418        assert_eq!(cfg.max_blobs_per_tx(), None);
419    }
420}