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