ethrex_levm/environment.rs
1use ethrex_common::{
2 Address, H256, U256,
3 types::{BlockHeader, ChainConfig, Fork, ForkBlobSchedule},
4};
5
6use crate::constants::{
7 BLOB_BASE_FEE_UPDATE_FRACTION, BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE, MAX_BLOB_COUNT,
8 MAX_BLOB_COUNT_ELECTRA, TARGET_BLOB_GAS_PER_BLOCK, TARGET_BLOB_GAS_PER_BLOCK_PECTRA,
9};
10
11/// [EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153#reference-implementation
12///
13/// Seeded per process and per map: contracts choose these keys, and an unseeded
14/// hasher's public constants (FxHash's) let them pick keys that all collide.
15pub type TransientStorage = foldhash::HashMap<(Address, U256), U256>;
16
17#[derive(Debug, Default, Clone)]
18/// Environmental information that the execution agent must provide.
19pub struct Environment {
20 /// The sender address of the external transaction.
21 pub origin: Address,
22 /// Gas limit of the Transaction
23 pub gas_limit: u64,
24 pub config: EVMConfig,
25 pub block_number: u64,
26 /// Coinbase is the block's beneficiary - the address that receives the block rewards and fees.
27 pub coinbase: Address,
28 pub timestamp: u64,
29 pub prev_randao: Option<H256>,
30 pub difficulty: U256,
31 pub slot_number: U256,
32 pub chain_id: U256,
33 pub base_fee_per_gas: U256,
34 pub base_blob_fee_per_gas: U256,
35 pub gas_price: U256, // Effective gas price
36 pub block_excess_blob_gas: Option<u64>,
37 pub block_blob_gas_used: Option<u64>,
38 pub tx_blob_hashes: Vec<H256>,
39 pub tx_max_priority_fee_per_gas: Option<U256>,
40 pub tx_max_fee_per_gas: Option<U256>,
41 pub tx_max_fee_per_blob_gas: Option<U256>,
42 pub tx_nonce: u64,
43 pub block_gas_limit: u64,
44 pub is_privileged: bool,
45 pub fee_token: Option<Address>,
46 /// When true, skip balance deduction in `deduct_caller`. Used by the prewarmer
47 /// to avoid early reverts on insufficient balance so that warming touches more storage.
48 pub disable_balance_check: bool,
49 /// When true, skip the sender nonce-mismatch validation. Used by the simulation
50 /// RPCs (eth_call, eth_estimateGas, eth_createAccessList): call objects may omit
51 /// the nonce, and no client enforces it there. The account nonce still increments
52 /// during execution. `debug_traceCall` relies on it too (geth's
53 /// `ToMessage(_, skipNonceCheck=true)`): the synthetic call may run on top of a
54 /// mid-block state (`txIndex`) whose nonce differs from the value the caller
55 /// supplied, so enforcing the check would spuriously reject the trace.
56 pub disable_nonce_check: bool,
57 /// When true, skip the gas limits that gate a transaction's *admission* rather than
58 /// its execution: the block-level gas allowance and the EIP-7825 per-transaction cap.
59 /// Used by every simulation RPC (eth_call, eth_estimateGas, eth_createAccessList,
60 /// debug_traceCall), whose callers routinely pass a `gas` above either bound — tools
61 /// commonly pass the block gas limit — and still expect an answer, since nothing is
62 /// being submitted.
63 /// This exists so `block_gas_limit` can keep the block's real value: that field is
64 /// observable through the GASLIMIT opcode and feeds the EIP-8037 cost-per-state-byte
65 /// formula, so raising it to bypass the allowance corrupts both.
66 pub disable_gas_allowance_check: bool,
67 /// When true, skip the EIP-3607 validation that rejects a sender carrying code.
68 /// Used by every simulation RPC (eth_call, eth_estimateGas, eth_createAccessList,
69 /// debug_traceCall). EIP-3607 exists because a contract has no private key, so no
70 /// valid signature should exist for its address; a simulated call carries no
71 /// signature at all and its `from` is just an assertion by the caller, so the rule
72 /// has nothing to protect there. Enforcing it breaks the common practice of
73 /// simulating a call whose sender is a contract, such as a smart contract wallet
74 /// previewing its own transaction. Transaction admission keeps enforcing it: the
75 /// mempool rejects such a sender outright, and block execution never sets this flag.
76 pub disable_sender_eoa_check: bool,
77 /// When true, the tx is a pre-execution system contract call (EIP-2935, EIP-4788,
78 /// EIP-7002, EIP-7251 etc.). Skips the block-level gas-allowance check since system
79 /// calls are allowed to exceed `block_gas_limit` (their 30M cap is a separate rule).
80 pub is_system_call: bool,
81}
82
83/// This struct holds special configuration variables specific to the
84/// EVM. In most cases, at least at the time of writing (February
85/// 2025), you want to use the default blob_schedule values for the
86/// specified Fork. The "intended" way to do this is by using the `EVMConfig::canonical_values(fork: Fork)` function.
87///
88/// However, that function should NOT be used IF you want to use a
89/// custom `ForkBlobSchedule`, like it's described in [EIP-7840](https://eips.ethereum.org/EIPS/eip-7840)
90/// Values are determined by [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691#specification)
91#[derive(Debug, Clone, Copy)]
92pub struct EVMConfig {
93 pub fork: Fork,
94 pub blob_schedule: ForkBlobSchedule,
95}
96
97impl EVMConfig {
98 pub fn new(fork: Fork, blob_schedule: ForkBlobSchedule) -> EVMConfig {
99 EVMConfig {
100 fork,
101 blob_schedule,
102 }
103 }
104
105 pub fn new_from_chain_config(chain_config: &ChainConfig, block_header: &BlockHeader) -> Self {
106 let fork = chain_config.fork(block_header.timestamp);
107
108 let blob_schedule = chain_config
109 .get_fork_blob_schedule(block_header.timestamp)
110 .unwrap_or_else(|| EVMConfig::canonical_values(fork));
111
112 EVMConfig::new(fork, blob_schedule)
113 }
114
115 /// This function is used for running the EF tests. If you don't
116 /// have acces to a EVMConfig (mainly in the form of a
117 /// genesis.json file) you can use this function to get the
118 /// "Default" ForkBlobSchedule for that specific Fork.
119 /// NOTE: This function could potentially be expanded to include
120 /// other types of "default"s.
121 pub fn canonical_values(fork: Fork) -> ForkBlobSchedule {
122 let max_blobs_per_block = Self::max_blobs_per_block(fork);
123 let target = Self::get_target_blob_gas_per_block_(fork);
124 let base_fee_update_fraction: u64 = Self::get_blob_base_fee_update_fraction_value(fork);
125
126 ForkBlobSchedule {
127 target,
128 max: max_blobs_per_block,
129 base_fee_update_fraction,
130 }
131 }
132
133 fn max_blobs_per_block(fork: Fork) -> u32 {
134 if fork >= Fork::Prague {
135 MAX_BLOB_COUNT_ELECTRA
136 } else {
137 MAX_BLOB_COUNT
138 }
139 }
140
141 fn get_blob_base_fee_update_fraction_value(fork: Fork) -> u64 {
142 if fork >= Fork::Prague {
143 BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE
144 } else {
145 BLOB_BASE_FEE_UPDATE_FRACTION
146 }
147 }
148
149 fn get_target_blob_gas_per_block_(fork: Fork) -> u32 {
150 if fork >= Fork::Prague {
151 TARGET_BLOB_GAS_PER_BLOCK_PECTRA
152 } else {
153 TARGET_BLOB_GAS_PER_BLOCK
154 }
155 }
156}
157
158impl Default for EVMConfig {
159 /// The default EVMConfig depends on the default Fork.
160 fn default() -> Self {
161 let fork = core::default::Default::default();
162 EVMConfig {
163 fork,
164 blob_schedule: Self::canonical_values(fork),
165 }
166 }
167}