revm_primitives/
env.rs

1pub mod handler_cfg;
2
3pub use handler_cfg::{CfgEnvWithHandlerCfg, EnvWithHandlerCfg, HandlerCfg};
4
5use crate::{
6    calc_blob_gasprice, AccessListItem, Account, Address, AuthorizationList, Bytes, InvalidHeader,
7    InvalidTransaction, Spec, SpecId, B256, GAS_PER_BLOB, MAX_BLOB_NUMBER_PER_BLOCK, MAX_CODE_SIZE,
8    MAX_INITCODE_SIZE, U256, VERSIONED_HASH_VERSION_KZG,
9};
10use alloy_primitives::TxKind;
11use core::cmp::{min, Ordering};
12use core::hash::Hash;
13use std::boxed::Box;
14use std::vec::Vec;
15
16/// EVM environment configuration.
17#[derive(Clone, Debug, Default, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19pub struct Env {
20    /// Configuration of the EVM itself.
21    pub cfg: CfgEnv,
22    /// Configuration of the block the transaction is in.
23    pub block: BlockEnv,
24    /// Configuration of the transaction that is being executed.
25    pub tx: TxEnv,
26}
27
28impl Env {
29    /// Resets environment to default values.
30    #[inline]
31    pub fn clear(&mut self) {
32        *self = Self::default();
33    }
34
35    /// Create boxed [Env].
36    #[inline]
37    pub fn boxed(cfg: CfgEnv, block: BlockEnv, tx: TxEnv) -> Box<Self> {
38        Box::new(Self { cfg, block, tx })
39    }
40
41    /// Calculates the effective gas price of the transaction.
42    #[inline]
43    pub fn effective_gas_price(&self) -> U256 {
44        if let Some(priority_fee) = self.tx.gas_priority_fee {
45            min(self.tx.gas_price, self.block.basefee + priority_fee)
46        } else {
47            self.tx.gas_price
48        }
49    }
50
51    /// Calculates the [EIP-4844] `data_fee` of the transaction.
52    ///
53    /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`].
54    ///
55    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
56    #[inline]
57    pub fn calc_data_fee(&self) -> Option<U256> {
58        self.block.get_blob_gasprice().map(|blob_gas_price| {
59            U256::from(blob_gas_price).saturating_mul(U256::from(self.tx.get_total_blob_gas()))
60        })
61    }
62
63    /// Calculates the maximum [EIP-4844] `data_fee` of the transaction.
64    ///
65    /// This is used for ensuring that the user has at least enough funds to pay the
66    /// `max_fee_per_blob_gas * total_blob_gas`, on top of regular gas costs.
67    ///
68    /// See EIP-4844:
69    /// <https://github.com/ethereum/EIPs/blob/master/EIPS/eip-4844.md#execution-layer-validation>
70    pub fn calc_max_data_fee(&self) -> Option<U256> {
71        self.tx.max_fee_per_blob_gas.map(|max_fee_per_blob_gas| {
72            max_fee_per_blob_gas.saturating_mul(U256::from(self.tx.get_total_blob_gas()))
73        })
74    }
75
76    /// Validate the block environment.
77    #[inline]
78    pub fn validate_block_env<SPEC: Spec>(&self) -> Result<(), InvalidHeader> {
79        // `prevrandao` is required for the merge
80        if SPEC::enabled(SpecId::MERGE) && self.block.prevrandao.is_none() {
81            return Err(InvalidHeader::PrevrandaoNotSet);
82        }
83        // `excess_blob_gas` is required for Cancun
84        if SPEC::enabled(SpecId::CANCUN) && self.block.blob_excess_gas_and_price.is_none() {
85            return Err(InvalidHeader::ExcessBlobGasNotSet);
86        }
87        Ok(())
88    }
89
90    /// Validate transaction data that is set inside ENV and return error if something is wrong.
91    ///
92    /// Return initial spend gas (Gas needed to execute transaction).
93    #[inline]
94    pub fn validate_tx<SPEC: Spec>(&self) -> Result<(), InvalidTransaction> {
95        // Check if the transaction's chain id is correct
96        if let Some(tx_chain_id) = self.tx.chain_id {
97            if tx_chain_id != self.cfg.chain_id {
98                return Err(InvalidTransaction::InvalidChainId);
99            }
100        }
101
102        // Check if gas_limit is more than block_gas_limit
103        if !self.cfg.is_block_gas_limit_disabled()
104            && U256::from(self.tx.gas_limit) > self.block.gas_limit
105        {
106            return Err(InvalidTransaction::CallerGasLimitMoreThanBlock);
107        }
108
109        // Check that access list is empty for transactions before BERLIN
110        if !SPEC::enabled(SpecId::BERLIN) && !self.tx.access_list.is_empty() {
111            return Err(InvalidTransaction::AccessListNotSupported);
112        }
113
114        // BASEFEE tx check
115        if SPEC::enabled(SpecId::LONDON) {
116            if let Some(priority_fee) = self.tx.gas_priority_fee {
117                if priority_fee > self.tx.gas_price {
118                    // or gas_max_fee for eip1559
119                    return Err(InvalidTransaction::PriorityFeeGreaterThanMaxFee);
120                }
121            }
122
123            // check minimal cost against basefee
124            if !self.cfg.is_base_fee_check_disabled()
125                && self.effective_gas_price() < self.block.basefee
126            {
127                return Err(InvalidTransaction::GasPriceLessThanBasefee);
128            }
129        }
130
131        // EIP-3860: Limit and meter initcode
132        if SPEC::enabled(SpecId::SHANGHAI) && self.tx.transact_to.is_create() {
133            let max_initcode_size = self
134                .cfg
135                .limit_contract_code_size
136                .map(|limit| limit.saturating_mul(2))
137                .unwrap_or(MAX_INITCODE_SIZE);
138            if self.tx.data.len() > max_initcode_size {
139                return Err(InvalidTransaction::CreateInitCodeSizeLimit);
140            }
141        }
142
143        // - For before CANCUN, check that `blob_hashes` and `max_fee_per_blob_gas` are empty / not set
144        if !SPEC::enabled(SpecId::CANCUN)
145            && (self.tx.max_fee_per_blob_gas.is_some() || !self.tx.blob_hashes.is_empty())
146        {
147            return Err(InvalidTransaction::BlobVersionedHashesNotSupported);
148        }
149
150        // Presence of max_fee_per_blob_gas means that this is blob transaction.
151        if let Some(max) = self.tx.max_fee_per_blob_gas {
152            // ensure that the user was willing to at least pay the current blob gasprice
153            let price = self.block.get_blob_gasprice().expect("already checked");
154            if U256::from(price) > max {
155                return Err(InvalidTransaction::BlobGasPriceGreaterThanMax);
156            }
157
158            // there must be at least one blob
159            if self.tx.blob_hashes.is_empty() {
160                return Err(InvalidTransaction::EmptyBlobs);
161            }
162
163            // The field `to` deviates slightly from the semantics with the exception
164            // that it MUST NOT be nil and therefore must always represent
165            // a 20-byte address. This means that blob transactions cannot
166            // have the form of a create transaction.
167            if self.tx.transact_to.is_create() {
168                return Err(InvalidTransaction::BlobCreateTransaction);
169            }
170
171            // all versioned blob hashes must start with VERSIONED_HASH_VERSION_KZG
172            for blob in self.tx.blob_hashes.iter() {
173                if blob[0] != VERSIONED_HASH_VERSION_KZG {
174                    return Err(InvalidTransaction::BlobVersionNotSupported);
175                }
176            }
177
178            // ensure the total blob gas spent is at most equal to the limit
179            // assert blob_gas_used <= MAX_BLOB_GAS_PER_BLOCK
180            let num_blobs = self.tx.blob_hashes.len();
181            if num_blobs > MAX_BLOB_NUMBER_PER_BLOCK as usize {
182                return Err(InvalidTransaction::TooManyBlobs {
183                    have: num_blobs,
184                    max: MAX_BLOB_NUMBER_PER_BLOCK as usize,
185                });
186            }
187        } else {
188            // if max_fee_per_blob_gas is not set, then blob_hashes must be empty
189            if !self.tx.blob_hashes.is_empty() {
190                return Err(InvalidTransaction::BlobVersionedHashesNotSupported);
191            }
192        }
193
194        // check if EIP-7702 transaction is enabled.
195        if !SPEC::enabled(SpecId::PRAGUE) && self.tx.authorization_list.is_some() {
196            return Err(InvalidTransaction::AuthorizationListNotSupported);
197        }
198
199        if let Some(auth_list) = &self.tx.authorization_list {
200            // The transaction is considered invalid if the length of authorization_list is zero.
201            if auth_list.is_empty() {
202                return Err(InvalidTransaction::EmptyAuthorizationList);
203            }
204
205            // Check if other fields are unset.
206            if self.tx.max_fee_per_blob_gas.is_some() || !self.tx.blob_hashes.is_empty() {
207                return Err(InvalidTransaction::AuthorizationListInvalidFields);
208            }
209        }
210
211        Ok(())
212    }
213
214    /// Validate transaction against state.
215    ///
216    /// # Panics
217    ///
218    /// If account code is not loaded.
219    #[inline]
220    pub fn validate_tx_against_state<SPEC: Spec>(
221        &self,
222        account: &mut Account,
223    ) -> Result<(), InvalidTransaction> {
224        // EIP-3607: Reject transactions from senders with deployed code
225        // This EIP is introduced after london but there was no collision in past
226        // so we can leave it enabled always
227        if !self.cfg.is_eip3607_disabled() {
228            let bytecode = &account.info.code.as_ref().unwrap();
229            // allow EOAs whose code is a valid delegation designation,
230            // i.e. 0xef0100 || address, to continue to originate transactions.
231            if !bytecode.is_empty() && !bytecode.is_eip7702() {
232                return Err(InvalidTransaction::RejectCallerWithCode);
233            }
234        }
235
236        // Check that the transaction's nonce is correct
237        if let Some(tx) = self.tx.nonce {
238            let state = account.info.nonce;
239            match tx.cmp(&state) {
240                Ordering::Greater => {
241                    return Err(InvalidTransaction::NonceTooHigh { tx, state });
242                }
243                Ordering::Less => {
244                    return Err(InvalidTransaction::NonceTooLow { tx, state });
245                }
246                _ => {}
247            }
248        }
249
250        let mut balance_check = U256::from(self.tx.gas_limit)
251            .checked_mul(self.tx.gas_price)
252            .and_then(|gas_cost| gas_cost.checked_add(self.tx.value))
253            .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
254
255        if SPEC::enabled(SpecId::CANCUN) {
256            // if the tx is not a blob tx, this will be None, so we add zero
257            let data_fee = self.calc_max_data_fee().unwrap_or_default();
258            balance_check = balance_check
259                .checked_add(U256::from(data_fee))
260                .ok_or(InvalidTransaction::OverflowPaymentInTransaction)?;
261        }
262
263        // Check if account has enough balance for gas_limit*gas_price and value transfer.
264        // Transfer will be done inside `*_inner` functions.
265        if balance_check > account.info.balance {
266            if self.cfg.is_balance_check_disabled() {
267                // Add transaction cost to balance to ensure execution doesn't fail.
268                account.info.balance = balance_check;
269            } else {
270                return Err(InvalidTransaction::LackOfFundForMaxFee {
271                    fee: Box::new(balance_check),
272                    balance: Box::new(account.info.balance),
273                });
274            }
275        }
276
277        Ok(())
278    }
279}
280
281/// EVM configuration.
282#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
283#[derive(Clone, Debug, Eq, PartialEq)]
284#[non_exhaustive]
285pub struct CfgEnv {
286    /// Chain ID of the EVM, it will be compared to the transaction's Chain ID.
287    /// Chain ID is introduced EIP-155
288    pub chain_id: u64,
289    /// KZG Settings for point evaluation precompile. By default, this is loaded from the ethereum mainnet trusted setup.
290    #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))]
291    #[cfg_attr(feature = "serde", serde(skip))]
292    pub kzg_settings: crate::kzg::EnvKzgSettings,
293    /// Bytecode that is created with CREATE/CREATE2 is by default analysed and jumptable is created.
294    /// This is very beneficial for testing and speeds up execution of that bytecode if called multiple times.
295    ///
296    /// Default: Analyse
297    pub perf_analyse_created_bytecodes: AnalysisKind,
298    /// If some it will effects EIP-170: Contract code size limit. Useful to increase this because of tests.
299    /// By default it is 0x6000 (~25kb).
300    pub limit_contract_code_size: Option<usize>,
301    /// A hard memory limit in bytes beyond which [crate::result::OutOfGasError::Memory] cannot be resized.
302    ///
303    /// In cases where the gas limit may be extraordinarily high, it is recommended to set this to
304    /// a sane value to prevent memory allocation panics. Defaults to `2^32 - 1` bytes per
305    /// EIP-1985.
306    #[cfg(feature = "memory_limit")]
307    pub memory_limit: u64,
308    /// Skip balance checks if true. Adds transaction cost to balance to ensure execution doesn't fail.
309    #[cfg(feature = "optional_balance_check")]
310    pub disable_balance_check: bool,
311    /// There are use cases where it's allowed to provide a gas limit that's higher than a block's gas limit. To that
312    /// end, you can disable the block gas limit validation.
313    /// By default, it is set to `false`.
314    #[cfg(feature = "optional_block_gas_limit")]
315    pub disable_block_gas_limit: bool,
316    /// EIP-3607 rejects transactions from senders with deployed code. In development, it can be desirable to simulate
317    /// calls from contracts, which this setting allows.
318    /// By default, it is set to `false`.
319    #[cfg(feature = "optional_eip3607")]
320    pub disable_eip3607: bool,
321    /// Disables all gas refunds. This is useful when using chains that have gas refunds disabled e.g. Avalanche.
322    /// Reasoning behind removing gas refunds can be found in EIP-3298.
323    /// By default, it is set to `false`.
324    #[cfg(feature = "optional_gas_refund")]
325    pub disable_gas_refund: bool,
326    /// Disables base fee checks for EIP-1559 transactions.
327    /// This is useful for testing method calls with zero gas price.
328    /// By default, it is set to `false`.
329    #[cfg(feature = "optional_no_base_fee")]
330    pub disable_base_fee: bool,
331    /// Disables the payout of the reward to the beneficiary.
332    /// By default, it is set to `false`.
333    #[cfg(feature = "optional_beneficiary_reward")]
334    pub disable_beneficiary_reward: bool,
335}
336
337impl CfgEnv {
338    /// Returns max code size from [`Self::limit_contract_code_size`] if set
339    /// or default [`MAX_CODE_SIZE`] value.
340    pub fn max_code_size(&self) -> usize {
341        self.limit_contract_code_size.unwrap_or(MAX_CODE_SIZE)
342    }
343
344    pub fn with_chain_id(mut self, chain_id: u64) -> Self {
345        self.chain_id = chain_id;
346        self
347    }
348
349    #[cfg(feature = "optional_eip3607")]
350    pub fn is_eip3607_disabled(&self) -> bool {
351        self.disable_eip3607
352    }
353
354    #[cfg(not(feature = "optional_eip3607"))]
355    pub fn is_eip3607_disabled(&self) -> bool {
356        false
357    }
358
359    #[cfg(feature = "optional_balance_check")]
360    pub fn is_balance_check_disabled(&self) -> bool {
361        self.disable_balance_check
362    }
363
364    #[cfg(not(feature = "optional_balance_check"))]
365    pub fn is_balance_check_disabled(&self) -> bool {
366        false
367    }
368
369    #[cfg(feature = "optional_gas_refund")]
370    pub fn is_gas_refund_disabled(&self) -> bool {
371        self.disable_gas_refund
372    }
373
374    #[cfg(not(feature = "optional_gas_refund"))]
375    pub fn is_gas_refund_disabled(&self) -> bool {
376        false
377    }
378
379    #[cfg(feature = "optional_no_base_fee")]
380    pub fn is_base_fee_check_disabled(&self) -> bool {
381        self.disable_base_fee
382    }
383
384    #[cfg(not(feature = "optional_no_base_fee"))]
385    pub fn is_base_fee_check_disabled(&self) -> bool {
386        false
387    }
388
389    #[cfg(feature = "optional_block_gas_limit")]
390    pub fn is_block_gas_limit_disabled(&self) -> bool {
391        self.disable_block_gas_limit
392    }
393
394    #[cfg(not(feature = "optional_block_gas_limit"))]
395    pub fn is_block_gas_limit_disabled(&self) -> bool {
396        false
397    }
398
399    #[cfg(feature = "optional_beneficiary_reward")]
400    pub fn is_beneficiary_reward_disabled(&self) -> bool {
401        self.disable_beneficiary_reward
402    }
403
404    #[cfg(not(feature = "optional_beneficiary_reward"))]
405    pub fn is_beneficiary_reward_disabled(&self) -> bool {
406        false
407    }
408}
409
410impl Default for CfgEnv {
411    fn default() -> Self {
412        Self {
413            chain_id: 1,
414            perf_analyse_created_bytecodes: AnalysisKind::default(),
415            limit_contract_code_size: None,
416            #[cfg(any(feature = "c-kzg", feature = "kzg-rs"))]
417            kzg_settings: crate::kzg::EnvKzgSettings::Default,
418            #[cfg(feature = "memory_limit")]
419            memory_limit: (1 << 32) - 1,
420            #[cfg(feature = "optional_balance_check")]
421            disable_balance_check: false,
422            #[cfg(feature = "optional_block_gas_limit")]
423            disable_block_gas_limit: false,
424            #[cfg(feature = "optional_eip3607")]
425            disable_eip3607: false,
426            #[cfg(feature = "optional_gas_refund")]
427            disable_gas_refund: false,
428            #[cfg(feature = "optional_no_base_fee")]
429            disable_base_fee: false,
430            #[cfg(feature = "optional_beneficiary_reward")]
431            disable_beneficiary_reward: false,
432        }
433    }
434}
435
436/// The block environment.
437#[derive(Clone, Debug, PartialEq, Eq, Hash)]
438#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
439pub struct BlockEnv {
440    /// The number of ancestor blocks of this block (block height).
441    pub number: U256,
442    /// Coinbase or miner or address that created and signed the block.
443    ///
444    /// This is the receiver address of all the gas spent in the block.
445    pub coinbase: Address,
446
447    /// The timestamp of the block in seconds since the UNIX epoch.
448    pub timestamp: U256,
449    /// The gas limit of the block.
450    pub gas_limit: U256,
451    /// The base fee per gas, added in the London upgrade with [EIP-1559].
452    ///
453    /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559
454    pub basefee: U256,
455    /// The difficulty of the block.
456    ///
457    /// Unused after the Paris (AKA the merge) upgrade, and replaced by `prevrandao`.
458    pub difficulty: U256,
459    /// The output of the randomness beacon provided by the beacon chain.
460    ///
461    /// Replaces `difficulty` after the Paris (AKA the merge) upgrade with [EIP-4399].
462    ///
463    /// NOTE: `prevrandao` can be found in a block in place of `mix_hash`.
464    ///
465    /// [EIP-4399]: https://eips.ethereum.org/EIPS/eip-4399
466    pub prevrandao: Option<B256>,
467    /// Excess blob gas and blob gasprice.
468    /// See also [`crate::calc_excess_blob_gas`]
469    /// and [`calc_blob_gasprice`].
470    ///
471    /// Incorporated as part of the Cancun upgrade via [EIP-4844].
472    ///
473    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
474    pub blob_excess_gas_and_price: Option<BlobExcessGasAndPrice>,
475}
476
477impl BlockEnv {
478    /// Takes `blob_excess_gas` saves it inside env
479    /// and calculates `blob_fee` with [`BlobExcessGasAndPrice`].
480    pub fn set_blob_excess_gas_and_price(&mut self, excess_blob_gas: u64) {
481        self.blob_excess_gas_and_price = Some(BlobExcessGasAndPrice::new(excess_blob_gas));
482    }
483    /// See [EIP-4844] and [`crate::calc_blob_gasprice`].
484    ///
485    /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`].
486    ///
487    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
488    #[inline]
489    pub fn get_blob_gasprice(&self) -> Option<u128> {
490        self.blob_excess_gas_and_price
491            .as_ref()
492            .map(|a| a.blob_gasprice)
493    }
494
495    /// Return `blob_excess_gas` header field. See [EIP-4844].
496    ///
497    /// Returns `None` if `Cancun` is not enabled. This is enforced in [`Env::validate_block_env`].
498    ///
499    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
500    #[inline]
501    pub fn get_blob_excess_gas(&self) -> Option<u64> {
502        self.blob_excess_gas_and_price
503            .as_ref()
504            .map(|a| a.excess_blob_gas)
505    }
506
507    /// Clears environment and resets fields to default values.
508    #[inline]
509    pub fn clear(&mut self) {
510        *self = Self::default();
511    }
512}
513
514impl Default for BlockEnv {
515    fn default() -> Self {
516        Self {
517            number: U256::ZERO,
518            coinbase: Address::ZERO,
519            timestamp: U256::from(1),
520            gas_limit: U256::MAX,
521            basefee: U256::ZERO,
522            difficulty: U256::ZERO,
523            prevrandao: Some(B256::ZERO),
524            blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new(0)),
525        }
526    }
527}
528
529/// The transaction environment.
530#[derive(Clone, Debug, PartialEq, Eq)]
531#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
532pub struct TxEnv {
533    /// Caller aka Author aka transaction signer.
534    pub caller: Address,
535    /// The gas limit of the transaction.
536    pub gas_limit: u64,
537    /// The gas price of the transaction.
538    pub gas_price: U256,
539    /// The destination of the transaction.
540    pub transact_to: TxKind,
541    /// The value sent to `transact_to`.
542    pub value: U256,
543    /// The data of the transaction.
544    pub data: Bytes,
545
546    /// The nonce of the transaction.
547    ///
548    /// Caution: If set to `None`, then nonce validation against the account's nonce is skipped: [InvalidTransaction::NonceTooHigh] and [InvalidTransaction::NonceTooLow]
549    pub nonce: Option<u64>,
550
551    /// The chain ID of the transaction. If set to `None`, no checks are performed.
552    ///
553    /// Incorporated as part of the Spurious Dragon upgrade via [EIP-155].
554    ///
555    /// [EIP-155]: https://eips.ethereum.org/EIPS/eip-155
556    pub chain_id: Option<u64>,
557
558    /// A list of addresses and storage keys that the transaction plans to access.
559    ///
560    /// Added in [EIP-2930].
561    ///
562    /// [EIP-2930]: https://eips.ethereum.org/EIPS/eip-2930
563    pub access_list: Vec<AccessListItem>,
564
565    /// The priority fee per gas.
566    ///
567    /// Incorporated as part of the London upgrade via [EIP-1559].
568    ///
569    /// [EIP-1559]: https://eips.ethereum.org/EIPS/eip-1559
570    pub gas_priority_fee: Option<U256>,
571
572    /// The list of blob versioned hashes. Per EIP there should be at least
573    /// one blob present if [`Self::max_fee_per_blob_gas`] is `Some`.
574    ///
575    /// Incorporated as part of the Cancun upgrade via [EIP-4844].
576    ///
577    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
578    pub blob_hashes: Vec<B256>,
579
580    /// The max fee per blob gas.
581    ///
582    /// Incorporated as part of the Cancun upgrade via [EIP-4844].
583    ///
584    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
585    pub max_fee_per_blob_gas: Option<U256>,
586
587    /// List of authorizations, that contains the signature that authorizes this
588    /// caller to place the code to signer account.
589    ///
590    /// Set EOA account code for one transaction
591    ///
592    /// [EIP-Set EOA account code for one transaction](https://eips.ethereum.org/EIPS/eip-7702)
593    pub authorization_list: Option<AuthorizationList>,
594
595    #[cfg_attr(feature = "serde", serde(flatten))]
596    #[cfg(feature = "optimism")]
597    /// Optimism fields.
598    pub optimism: OptimismFields,
599}
600
601pub enum TxType {
602    Legacy,
603    Eip1559,
604    BlobTx,
605    EofCreate,
606}
607
608impl TxEnv {
609    /// See [EIP-4844], [`Env::calc_data_fee`], and [`Env::calc_max_data_fee`].
610    ///
611    /// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
612    #[inline]
613    pub fn get_total_blob_gas(&self) -> u64 {
614        GAS_PER_BLOB * self.blob_hashes.len() as u64
615    }
616
617    /// Clears environment and resets fields to default values.
618    #[inline]
619    pub fn clear(&mut self) {
620        *self = Self::default();
621    }
622}
623
624impl Default for TxEnv {
625    fn default() -> Self {
626        Self {
627            caller: Address::ZERO,
628            gas_limit: u64::MAX,
629            gas_price: U256::ZERO,
630            gas_priority_fee: None,
631            transact_to: TxKind::Call(Address::ZERO), // will do nothing
632            value: U256::ZERO,
633            data: Bytes::new(),
634            chain_id: None,
635            nonce: None,
636            access_list: Vec::new(),
637            blob_hashes: Vec::new(),
638            max_fee_per_blob_gas: None,
639            authorization_list: None,
640            #[cfg(feature = "optimism")]
641            optimism: OptimismFields::default(),
642        }
643    }
644}
645
646/// Structure holding block blob excess gas and it calculates blob fee.
647///
648/// Incorporated as part of the Cancun upgrade via [EIP-4844].
649///
650/// [EIP-4844]: https://eips.ethereum.org/EIPS/eip-4844
651#[derive(Clone, Debug, PartialEq, Eq, Hash)]
652#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
653pub struct BlobExcessGasAndPrice {
654    /// The excess blob gas of the block.
655    pub excess_blob_gas: u64,
656    /// The calculated blob gas price based on the `excess_blob_gas`, See [calc_blob_gasprice]
657    pub blob_gasprice: u128,
658}
659
660impl BlobExcessGasAndPrice {
661    /// Creates a new instance by calculating the blob gas price with [`calc_blob_gasprice`].
662    pub fn new(excess_blob_gas: u64) -> Self {
663        let blob_gasprice = calc_blob_gasprice(excess_blob_gas);
664        Self {
665            excess_blob_gas,
666            blob_gasprice,
667        }
668    }
669}
670
671/// Additional [TxEnv] fields for optimism.
672#[cfg(feature = "optimism")]
673#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
674#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
675pub struct OptimismFields {
676    /// The source hash is used to make sure that deposit transactions do
677    /// not have identical hashes.
678    ///
679    /// L1 originated deposit transaction source hashes are computed using
680    /// the hash of the l1 block hash and the l1 log index.
681    /// L1 attributes deposit source hashes are computed with the l1 block
682    /// hash and the sequence number = l2 block number - l2 epoch start
683    /// block number.
684    ///
685    /// These two deposit transaction sources specify a domain in the outer
686    /// hash so there are no collisions.
687    pub source_hash: Option<B256>,
688    /// The amount to increase the balance of the `from` account as part of
689    /// a deposit transaction. This is unconditional and is applied to the
690    /// `from` account even if the deposit transaction fails since
691    /// the deposit is pre-paid on L1.
692    pub mint: Option<u128>,
693    /// Whether or not the transaction is a system transaction.
694    pub is_system_transaction: Option<bool>,
695    /// An enveloped EIP-2718 typed transaction. This is used
696    /// to compute the L1 tx cost using the L1 block info, as
697    /// opposed to requiring downstream apps to compute the cost
698    /// externally.
699    /// This field is optional to allow the [TxEnv] to be constructed
700    /// for non-optimism chains when the `optimism` feature is enabled,
701    /// but the [CfgEnv] `optimism` field is set to false.
702    pub enveloped_tx: Option<Bytes>,
703}
704
705/// Transaction destination
706pub type TransactTo = TxKind;
707
708/// Create scheme.
709#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
710#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
711pub enum CreateScheme {
712    /// Legacy create scheme of `CREATE`.
713    Create,
714    /// Create scheme of `CREATE2`.
715    Create2 {
716        /// Salt.
717        salt: U256,
718    },
719}
720
721/// What bytecode analysis to perform.
722#[derive(Clone, Default, Debug, Eq, PartialEq, Hash)]
723#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
724pub enum AnalysisKind {
725    /// Do not perform bytecode analysis.
726    Raw,
727    /// Perform bytecode analysis.
728    #[default]
729    Analyse,
730}
731
732#[cfg(test)]
733mod tests {
734    use super::*;
735
736    #[test]
737    fn test_validate_tx_chain_id() {
738        let mut env = Env::default();
739        env.tx.chain_id = Some(1);
740        env.cfg.chain_id = 2;
741        assert_eq!(
742            env.validate_tx::<crate::LatestSpec>(),
743            Err(InvalidTransaction::InvalidChainId)
744        );
745    }
746
747    #[test]
748    fn test_validate_tx_access_list() {
749        let mut env = Env::default();
750        env.tx.access_list = vec![AccessListItem {
751            address: Address::ZERO,
752            storage_keys: vec![],
753        }];
754        assert_eq!(
755            env.validate_tx::<crate::FrontierSpec>(),
756            Err(InvalidTransaction::AccessListNotSupported)
757        );
758    }
759}