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}