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    /// Disables base fee checks for EIP-1559 transactions
96    ///
97    /// This is useful for testing method calls with zero gas price.
98    ///
99    /// By default, it is set to `false`.
100    #[cfg(feature = "optional_no_base_fee")]
101    pub disable_base_fee: bool,
102    /// Disables "max fee must be less than or equal to max priority fee" check for EIP-1559 transactions.
103    /// This is useful because some chains (e.g. Arbitrum) do not enforce this check.
104    /// By default, it is set to `false`.
105    #[cfg(feature = "optional_priority_fee_check")]
106    pub disable_priority_fee_check: bool,
107    /// Disables fee charging for transactions.
108    /// This is useful when executing `eth_call` for example, on OP-chains where setting the base fee
109    /// to 0 isn't sufficient.
110    /// By default, it is set to `false`.
111    #[cfg(feature = "optional_fee_charge")]
112    pub disable_fee_charge: bool,
113}
114
115impl CfgEnv {
116    /// Creates new `CfgEnv` with default values.
117    pub fn new() -> Self {
118        Self::default()
119    }
120}
121
122impl<SPEC: Into<SpecId> + Copy> CfgEnv<SPEC> {
123    /// Returns the blob base fee update fraction from [CfgEnv::blob_base_fee_update_fraction].
124    ///
125    /// If this field is not set, return the default value for the spec.
126    ///
127    /// Default values for Cancun is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN`]
128    /// and for Prague is [`primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE`].
129    pub fn blob_base_fee_update_fraction(&mut self) -> u64 {
130        self.blob_base_fee_update_fraction.unwrap_or_else(|| {
131            let spec: SpecId = self.spec.into();
132            if spec.is_enabled_in(SpecId::PRAGUE) {
133                primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
134            } else {
135                primitives::eip4844::BLOB_BASE_FEE_UPDATE_FRACTION_CANCUN
136            }
137        })
138    }
139}
140
141impl<SPEC> CfgEnv<SPEC> {
142    /// Create new `CfgEnv` with default values and specified spec.
143    pub fn new_with_spec(spec: SPEC) -> Self {
144        Self {
145            chain_id: 1,
146            tx_chain_id_check: false,
147            limit_contract_code_size: None,
148            limit_contract_initcode_size: None,
149            spec,
150            disable_nonce_check: false,
151            max_blobs_per_tx: None,
152            tx_gas_limit_cap: None,
153            blob_base_fee_update_fraction: None,
154            #[cfg(feature = "memory_limit")]
155            memory_limit: (1 << 32) - 1,
156            #[cfg(feature = "optional_balance_check")]
157            disable_balance_check: false,
158            #[cfg(feature = "optional_block_gas_limit")]
159            disable_block_gas_limit: false,
160            #[cfg(feature = "optional_eip3541")]
161            disable_eip3541: false,
162            #[cfg(feature = "optional_eip3607")]
163            disable_eip3607: false,
164            #[cfg(feature = "optional_no_base_fee")]
165            disable_base_fee: false,
166            #[cfg(feature = "optional_priority_fee_check")]
167            disable_priority_fee_check: false,
168            #[cfg(feature = "optional_fee_charge")]
169            disable_fee_charge: false,
170        }
171    }
172
173    /// Consumes `self` and returns a new `CfgEnv` with the specified chain ID.
174    pub fn with_chain_id(mut self, chain_id: u64) -> Self {
175        self.chain_id = chain_id;
176        self
177    }
178
179    /// Enables the transaction's chain ID check.
180    pub fn enable_tx_chain_id_check(mut self) -> Self {
181        self.tx_chain_id_check = true;
182        self
183    }
184
185    /// Disables the transaction's chain ID check.
186    pub fn disable_tx_chain_id_check(mut self) -> Self {
187        self.tx_chain_id_check = false;
188        self
189    }
190
191    /// Consumes `self` and returns a new `CfgEnv` with the specified spec.
192    pub fn with_spec<OSPEC: Into<SpecId>>(self, spec: OSPEC) -> CfgEnv<OSPEC> {
193        CfgEnv {
194            chain_id: self.chain_id,
195            tx_chain_id_check: self.tx_chain_id_check,
196            limit_contract_code_size: self.limit_contract_code_size,
197            limit_contract_initcode_size: self.limit_contract_initcode_size,
198            spec,
199            disable_nonce_check: self.disable_nonce_check,
200            tx_gas_limit_cap: self.tx_gas_limit_cap,
201            max_blobs_per_tx: self.max_blobs_per_tx,
202            blob_base_fee_update_fraction: self.blob_base_fee_update_fraction,
203            #[cfg(feature = "memory_limit")]
204            memory_limit: self.memory_limit,
205            #[cfg(feature = "optional_balance_check")]
206            disable_balance_check: self.disable_balance_check,
207            #[cfg(feature = "optional_block_gas_limit")]
208            disable_block_gas_limit: self.disable_block_gas_limit,
209            #[cfg(feature = "optional_eip3541")]
210            disable_eip3541: self.disable_eip3541,
211            #[cfg(feature = "optional_eip3607")]
212            disable_eip3607: self.disable_eip3607,
213            #[cfg(feature = "optional_no_base_fee")]
214            disable_base_fee: self.disable_base_fee,
215            #[cfg(feature = "optional_priority_fee_check")]
216            disable_priority_fee_check: self.disable_priority_fee_check,
217            #[cfg(feature = "optional_fee_charge")]
218            disable_fee_charge: self.disable_fee_charge,
219        }
220    }
221
222    /// Sets the blob target
223    pub fn with_max_blobs_per_tx(mut self, max_blobs_per_tx: u64) -> Self {
224        self.set_max_blobs_per_tx(max_blobs_per_tx);
225        self
226    }
227
228    /// Sets the blob target
229    pub fn set_max_blobs_per_tx(&mut self, max_blobs_per_tx: u64) {
230        self.max_blobs_per_tx = Some(max_blobs_per_tx);
231    }
232
233    /// Clears the blob target and max count over hardforks.
234    pub fn clear_max_blobs_per_tx(&mut self) {
235        self.max_blobs_per_tx = None;
236    }
237
238    /// Sets the disable priority fee check flag.
239    #[cfg(feature = "optional_priority_fee_check")]
240    pub fn with_disable_priority_fee_check(mut self, disable: bool) -> Self {
241        self.disable_priority_fee_check = disable;
242        self
243    }
244
245    /// Sets the disable fee charge flag.
246    #[cfg(feature = "optional_fee_charge")]
247    pub fn with_disable_fee_charge(mut self, disable: bool) -> Self {
248        self.disable_fee_charge = disable;
249        self
250    }
251}
252
253impl<SPEC: Into<SpecId> + Copy> Cfg for CfgEnv<SPEC> {
254    type Spec = SPEC;
255
256    #[inline]
257    fn chain_id(&self) -> u64 {
258        self.chain_id
259    }
260
261    #[inline]
262    fn spec(&self) -> Self::Spec {
263        self.spec
264    }
265
266    #[inline]
267    fn tx_chain_id_check(&self) -> bool {
268        self.tx_chain_id_check
269    }
270
271    #[inline]
272    fn tx_gas_limit_cap(&self) -> u64 {
273        self.tx_gas_limit_cap
274            .unwrap_or(if self.spec.into().is_enabled_in(SpecId::OSAKA) {
275                eip7825::TX_GAS_LIMIT_CAP
276            } else {
277                u64::MAX
278            })
279    }
280
281    #[inline]
282    fn max_blobs_per_tx(&self) -> Option<u64> {
283        self.max_blobs_per_tx
284    }
285
286    fn max_code_size(&self) -> usize {
287        self.limit_contract_code_size
288            .unwrap_or(eip170::MAX_CODE_SIZE)
289    }
290
291    fn max_initcode_size(&self) -> usize {
292        self.limit_contract_initcode_size
293            .or_else(|| {
294                self.limit_contract_code_size
295                    .map(|size| size.saturating_mul(2))
296            })
297            .unwrap_or(eip3860::MAX_INITCODE_SIZE)
298    }
299
300    fn is_eip3541_disabled(&self) -> bool {
301        cfg_if::cfg_if! {
302            if #[cfg(feature = "optional_eip3541")] {
303                self.disable_eip3541
304            } else {
305                false
306            }
307        }
308    }
309
310    fn is_eip3607_disabled(&self) -> bool {
311        cfg_if::cfg_if! {
312            if #[cfg(feature = "optional_eip3607")] {
313                self.disable_eip3607
314            } else {
315                false
316            }
317        }
318    }
319
320    fn is_balance_check_disabled(&self) -> bool {
321        cfg_if::cfg_if! {
322            if #[cfg(feature = "optional_balance_check")] {
323                self.disable_balance_check
324            } else {
325                false
326            }
327        }
328    }
329
330    /// Returns `true` if the block gas limit is disabled.
331    fn is_block_gas_limit_disabled(&self) -> bool {
332        cfg_if::cfg_if! {
333            if #[cfg(feature = "optional_block_gas_limit")] {
334                self.disable_block_gas_limit
335            } else {
336                false
337            }
338        }
339    }
340
341    fn is_nonce_check_disabled(&self) -> bool {
342        self.disable_nonce_check
343    }
344
345    fn is_base_fee_check_disabled(&self) -> bool {
346        cfg_if::cfg_if! {
347            if #[cfg(feature = "optional_no_base_fee")] {
348                self.disable_base_fee
349            } else {
350                false
351            }
352        }
353    }
354
355    fn is_priority_fee_check_disabled(&self) -> bool {
356        cfg_if::cfg_if! {
357            if #[cfg(feature = "optional_priority_fee_check")] {
358                self.disable_priority_fee_check
359            } else {
360                false
361            }
362        }
363    }
364
365    fn is_fee_charge_disabled(&self) -> bool {
366        cfg_if::cfg_if! {
367            if #[cfg(feature = "optional_fee_charge")] {
368                self.disable_fee_charge
369            } else {
370                false
371            }
372        }
373    }
374}
375
376impl<SPEC: Default> Default for CfgEnv<SPEC> {
377    fn default() -> Self {
378        Self::new_with_spec(SPEC::default())
379    }
380}
381
382#[cfg(test)]
383mod test {
384    use super::*;
385
386    #[test]
387    fn blob_max_and_target_count() {
388        let cfg: CfgEnv = Default::default();
389        assert_eq!(cfg.max_blobs_per_tx(), None);
390    }
391}