Skip to content

Commit a169985

Browse files
SDartayetJereSalo
andauthored
refactor(levm): decluttering vm.rs (#2733)
**Motivation** Making the code of the vm.rs file cleaner, since it's a bit cluttered right now. **Description** The code of vm.rs is a bit messy right now. This PR moves EVM config to environment, moves a few attributes from environment to substate that make more sense there, and removes the StateBackup struct since it's made unnecessary by this change. Closes #2731, Closes #2717 Resolves most of #2718 --------- Co-authored-by: JereSalo <[email protected]>
1 parent b0cdf7d commit a169985

File tree

8 files changed

+132
-166
lines changed

8 files changed

+132
-166
lines changed

cmd/ef_tests/state/runner/levm_runner.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,13 @@ use ethrex_common::{
1111
use ethrex_levm::{
1212
db::gen_db::GeneralizedDatabase,
1313
errors::{ExecutionReport, TxValidationError, VMError},
14-
vm::{EVMConfig, VM},
15-
Environment,
14+
vm::VM,
15+
EVMConfig, Environment,
1616
};
1717
use ethrex_rlp::encode::RLPEncode;
1818
use ethrex_storage::AccountUpdate;
1919
use ethrex_vm::backends;
2020
use keccak_hash::keccak;
21-
use std::collections::HashMap;
2221

2322
pub async fn run_ef_test(test: &EFTest) -> Result<EFTestReport, EFTestRunnerError> {
2423
// There are some tests that don't have a hash, unwrap will panic
@@ -168,7 +167,6 @@ pub fn prepare_vm_for_tx<'a>(
168167
VM::new(
169168
Environment {
170169
origin: test_tx.sender,
171-
refunded_gas: 0,
172170
gas_limit: test_tx.gas_limit,
173171
config,
174172
block_number: test.env.current_number,
@@ -187,7 +185,6 @@ pub fn prepare_vm_for_tx<'a>(
187185
tx_max_fee_per_blob_gas: test_tx.max_fee_per_blob_gas,
188186
tx_nonce: test_tx.nonce,
189187
block_gas_limit: test.env.current_gas_limit,
190-
transient_storage: HashMap::new(),
191188
is_privileged: false,
192189
},
193190
db,

crates/vm/backends/levm/mod.rs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ use ethrex_common::{
1919
};
2020
use ethrex_levm::db::gen_db::GeneralizedDatabase;
2121
use ethrex_levm::errors::TxValidationError;
22+
use ethrex_levm::EVMConfig;
2223
use ethrex_levm::{
2324
errors::{ExecutionReport, TxResult, VMError},
24-
vm::{EVMConfig, Substate, VM},
25+
vm::{Substate, VM},
2526
Environment,
2627
};
2728
use ethrex_storage::error::StoreError;
@@ -115,7 +116,6 @@ impl LEVM {
115116
let config = EVMConfig::new_from_chain_config(&chain_config, block_header);
116117
let env = Environment {
117118
origin: tx_sender,
118-
refunded_gas: 0,
119119
gas_limit: tx.gas_limit(),
120120
config,
121121
block_number: block_header.number.into(),
@@ -133,7 +133,6 @@ impl LEVM {
133133
tx_max_fee_per_blob_gas: tx.max_fee_per_blob_gas().map(U256::from),
134134
tx_nonce: tx.nonce(),
135135
block_gas_limit: block_header.gas_limit,
136-
transient_storage: HashMap::new(),
137136
difficulty: block_header.difficulty,
138137
is_privileged: matches!(tx, Transaction::PrivilegedL2Transaction(_)),
139138
};
@@ -593,7 +592,6 @@ pub fn generic_system_contract_levm(
593592
block_excess_blob_gas: block_header.excess_blob_gas.map(U256::from),
594593
block_blob_gas_used: block_header.blob_gas_used.map(U256::from),
595594
block_gas_limit: 30_000_000,
596-
transient_storage: HashMap::new(),
597595
config,
598596
..Default::default()
599597
};
@@ -708,7 +706,6 @@ fn env_from_generic(
708706
let config = EVMConfig::new_from_chain_config(&chain_config, header);
709707
Ok(Environment {
710708
origin: tx.from.0.into(),
711-
refunded_gas: 0,
712709
gas_limit: tx.gas.unwrap_or(header.gas_limit), // Ensure tx doesn't fail due to gas limit
713710
config,
714711
block_number: header.number.into(),
@@ -726,7 +723,6 @@ fn env_from_generic(
726723
tx_max_fee_per_blob_gas: tx.max_fee_per_blob_gas,
727724
tx_nonce: tx.nonce.unwrap_or_default(),
728725
block_gas_limit: header.gas_limit,
729-
transient_storage: HashMap::new(),
730726
difficulty: header.difficulty,
731727
is_privileged: false,
732728
})

crates/vm/levm/src/environment.rs

Lines changed: 92 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
use ethrex_common::{Address, H256, U256};
1+
use ethrex_common::{
2+
types::{BlockHeader, ChainConfig, Fork, ForkBlobSchedule},
3+
Address, H256, U256,
4+
};
25

3-
use crate::vm::EVMConfig;
6+
use 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+
};
410

511
use std::collections::HashMap;
612
/// [EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153#reference-implementation
@@ -11,7 +17,6 @@ pub type TransientStorage = HashMap<(Address, U256), U256>;
1117
pub struct Environment {
1218
/// The sender address of the external transaction.
1319
pub origin: Address,
14-
pub refunded_gas: u64,
1520
/// Gas limit of the Transaction
1621
pub gas_limit: u64,
1722
pub config: EVMConfig,
@@ -32,6 +37,89 @@ pub struct Environment {
3237
pub tx_max_fee_per_blob_gas: Option<U256>,
3338
pub tx_nonce: u64,
3439
pub block_gas_limit: u64,
35-
pub transient_storage: TransientStorage,
3640
pub is_privileged: bool,
3741
}
42+
43+
#[derive(Debug, Clone, Copy)]
44+
/// This struct holds special configuration variables specific to the
45+
/// EVM. In most cases, at least at the time of writing (February
46+
/// 2025), you want to use the default blob_schedule values for the
47+
/// specified Fork. The "intended" way to do this is by using the `EVMConfig::canonical_values(fork: Fork)` function.
48+
///
49+
/// However, that function should NOT be used IF you want to use a
50+
/// custom `ForkBlobSchedule`, like it's described in [EIP-7840](https://eips.ethereum.org/EIPS/eip-7840)
51+
/// Values are determined by [EIP-7691](https://eips.ethereum.org/EIPS/eip-7691#specification)
52+
pub struct EVMConfig {
53+
pub fork: Fork,
54+
pub blob_schedule: ForkBlobSchedule,
55+
}
56+
57+
impl EVMConfig {
58+
pub fn new(fork: Fork, blob_schedule: ForkBlobSchedule) -> EVMConfig {
59+
EVMConfig {
60+
fork,
61+
blob_schedule,
62+
}
63+
}
64+
65+
pub fn new_from_chain_config(chain_config: &ChainConfig, block_header: &BlockHeader) -> Self {
66+
let fork = chain_config.fork(block_header.timestamp);
67+
68+
let blob_schedule = chain_config
69+
.get_fork_blob_schedule(block_header.timestamp)
70+
.unwrap_or_else(|| EVMConfig::canonical_values(fork));
71+
72+
EVMConfig::new(fork, blob_schedule)
73+
}
74+
75+
/// This function is used for running the EF tests. If you don't
76+
/// have acces to a EVMConfig (mainly in the form of a
77+
/// genesis.json file) you can use this function to get the
78+
/// "Default" ForkBlobSchedule for that specific Fork.
79+
/// NOTE: This function could potentially be expanded to include
80+
/// other types of "default"s.
81+
pub fn canonical_values(fork: Fork) -> ForkBlobSchedule {
82+
let max_blobs_per_block: u64 = Self::max_blobs_per_block(fork);
83+
let target: u64 = Self::get_target_blob_gas_per_block_(fork);
84+
let base_fee_update_fraction: u64 = Self::get_blob_base_fee_update_fraction_value(fork);
85+
86+
ForkBlobSchedule {
87+
target,
88+
max: max_blobs_per_block,
89+
base_fee_update_fraction,
90+
}
91+
}
92+
93+
const fn max_blobs_per_block(fork: Fork) -> u64 {
94+
match fork {
95+
Fork::Prague => MAX_BLOB_COUNT_ELECTRA,
96+
Fork::Osaka => MAX_BLOB_COUNT_ELECTRA,
97+
_ => MAX_BLOB_COUNT,
98+
}
99+
}
100+
101+
const fn get_blob_base_fee_update_fraction_value(fork: Fork) -> u64 {
102+
match fork {
103+
Fork::Prague | Fork::Osaka => BLOB_BASE_FEE_UPDATE_FRACTION_PRAGUE,
104+
_ => BLOB_BASE_FEE_UPDATE_FRACTION,
105+
}
106+
}
107+
108+
const fn get_target_blob_gas_per_block_(fork: Fork) -> u64 {
109+
match fork {
110+
Fork::Prague | Fork::Osaka => TARGET_BLOB_GAS_PER_BLOCK_PECTRA,
111+
_ => TARGET_BLOB_GAS_PER_BLOCK,
112+
}
113+
}
114+
}
115+
116+
impl Default for EVMConfig {
117+
/// The default EVMConfig depends on the default Fork.
118+
fn default() -> Self {
119+
let fork = core::default::Default::default();
120+
EVMConfig {
121+
fork,
122+
blob_schedule: Self::canonical_values(fork),
123+
}
124+
}
125+
}

crates/vm/levm/src/execution_handlers.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
gas_cost::CODE_DEPOSIT_COST,
66
opcodes::Opcode,
77
utils::*,
8-
vm::{StateBackup, VM},
8+
vm::{Substate, VM},
99
};
1010

1111
use bytes::Bytes;
@@ -14,14 +14,14 @@ impl<'a> VM<'a> {
1414
pub fn handle_precompile_result(
1515
&mut self,
1616
precompile_result: Result<Bytes, VMError>,
17-
backup: StateBackup,
17+
backup: Substate,
1818
current_call_frame: &mut CallFrame,
1919
) -> Result<ExecutionReport, VMError> {
2020
match precompile_result {
2121
Ok(output) => Ok(ExecutionReport {
2222
result: TxResult::Success,
2323
gas_used: current_call_frame.gas_used,
24-
gas_refunded: self.env.refunded_gas,
24+
gas_refunded: self.accrued_substate.refunded_gas,
2525
output,
2626
logs: std::mem::take(&mut current_call_frame.logs),
2727
}),
@@ -35,7 +35,7 @@ impl<'a> VM<'a> {
3535
Ok(ExecutionReport {
3636
result: TxResult::Revert(error),
3737
gas_used: current_call_frame.gas_limit,
38-
gas_refunded: self.env.refunded_gas,
38+
gas_refunded: self.accrued_substate.refunded_gas,
3939
output: Bytes::new(),
4040
logs: vec![],
4141
})
@@ -153,7 +153,7 @@ impl<'a> VM<'a> {
153153
executed_call_frame: &mut CallFrame,
154154
) -> Result<ExecutionReport, VMError> {
155155
let backup = self
156-
.backups
156+
.substate_backups
157157
.pop()
158158
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
159159
// On successful create check output validity
@@ -207,7 +207,7 @@ impl<'a> VM<'a> {
207207
return Ok(ExecutionReport {
208208
result: TxResult::Revert(error),
209209
gas_used: executed_call_frame.gas_used,
210-
gas_refunded: self.env.refunded_gas,
210+
gas_refunded: self.accrued_substate.refunded_gas,
211211
output: std::mem::take(&mut executed_call_frame.output),
212212
logs: vec![],
213213
});
@@ -218,7 +218,7 @@ impl<'a> VM<'a> {
218218
Ok(ExecutionReport {
219219
result: TxResult::Success,
220220
gas_used: executed_call_frame.gas_used,
221-
gas_refunded: self.env.refunded_gas,
221+
gas_refunded: self.accrued_substate.refunded_gas,
222222
output: std::mem::take(&mut executed_call_frame.output),
223223
logs: std::mem::take(&mut executed_call_frame.logs),
224224
})
@@ -230,7 +230,7 @@ impl<'a> VM<'a> {
230230
executed_call_frame: &mut CallFrame,
231231
) -> Result<ExecutionReport, VMError> {
232232
let backup = self
233-
.backups
233+
.substate_backups
234234
.pop()
235235
.ok_or(VMError::Internal(InternalError::CouldNotPopCallframe))?;
236236
if error.should_propagate() {

crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ impl<'a> VM<'a> {
3030
let key = self.current_call_frame_mut()?.stack.pop()?;
3131
let to = self.current_call_frame()?.to;
3232
let value = self
33-
.env
33+
.accrued_substate
3434
.transient_storage
3535
.get(&(to, key))
3636
.cloned()
@@ -63,7 +63,9 @@ impl<'a> VM<'a> {
6363
let value = current_call_frame.stack.pop()?;
6464
(key, value, current_call_frame.to)
6565
};
66-
self.env.transient_storage.insert((to, key), value);
66+
self.accrued_substate
67+
.transient_storage
68+
.insert((to, key), value);
6769

6870
Ok(OpcodeResult::Continue { pc_increment: 1 })
6971
}
@@ -186,7 +188,7 @@ impl<'a> VM<'a> {
186188

187189
// Gas Refunds
188190
// Sync gas refund with global env, ensuring consistency accross contexts.
189-
let mut gas_refunds = self.env.refunded_gas;
191+
let mut gas_refunds = self.accrued_substate.refunded_gas;
190192

191193
// https://eips.ethereum.org/EIPS/eip-2929
192194
let (remove_slot_cost, restore_empty_slot_cost, restore_slot_cost) = (4800, 19900, 2800);
@@ -224,7 +226,7 @@ impl<'a> VM<'a> {
224226
}
225227
}
226228

227-
self.env.refunded_gas = gas_refunds;
229+
self.accrued_substate.refunded_gas = gas_refunds;
228230

229231
self.current_call_frame_mut()?
230232
.increase_consumed_gas(gas_cost::sstore(

crates/vm/levm/src/opcode_handlers/system.rs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::{
77
memory::{self, calculate_memory_size},
88
precompiles::is_precompile,
99
utils::{address_to_word, word_to_address, *},
10-
vm::{StateBackup, VM},
10+
vm::VM,
1111
};
1212
use bytes::Bytes;
1313
use ethrex_common::{
@@ -725,13 +725,10 @@ impl<'a> VM<'a> {
725725

726726
self.accrued_substate.created_accounts.insert(new_address); // Mostly for SELFDESTRUCT during initcode.
727727

728-
// Backup of Database, Substate, Gas Refunds and Transient Storage if sub-context is reverted
729-
let backup = StateBackup::new(
730-
self.accrued_substate.clone(),
731-
self.env.refunded_gas,
732-
self.env.transient_storage.clone(),
733-
);
734-
self.backups.push(backup);
728+
// Backup of Substate, a copy of the substate to restore if sub-context is reverted
729+
let backup = self.accrued_substate.clone();
730+
self.substate_backups.push(backup);
731+
735732
Ok(OpcodeResult::Continue { pc_increment: 0 })
736733
}
737734

@@ -831,13 +828,9 @@ impl<'a> VM<'a> {
831828
ret_size,
832829
);
833830
self.call_frames.push(new_call_frame);
834-
// Backup of Database, Substate, Gas Refunds and Transient Storage if sub-context is reverted
835-
let backup = StateBackup::new(
836-
self.accrued_substate.clone(),
837-
self.env.refunded_gas,
838-
self.env.transient_storage.clone(),
839-
);
840-
self.backups.push(backup);
831+
// Backup of Substate, a copy of the substate to restore if sub-context is reverted
832+
let backup = self.accrued_substate.clone();
833+
self.substate_backups.push(backup);
841834

842835
if is_precompile(&code_address, self.env.config.fork) {
843836
let _report = self.run_execution()?;

crates/vm/levm/src/utils.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ use crate::{
88
TOTAL_COST_FLOOR_PER_TOKEN, WARM_ADDRESS_ACCESS_COST,
99
},
1010
opcodes::Opcode,
11-
vm::{EVMConfig, Substate, VM},
11+
vm::{Substate, VM},
12+
EVMConfig,
1213
};
1314
use bytes::Bytes;
1415
use ethrex_common::types::Account;
@@ -462,7 +463,7 @@ impl<'a> VM<'a> {
462463
self.current_call_frame_mut()?.valid_jump_destinations =
463464
get_valid_jump_destinations(&self.current_call_frame()?.bytecode).unwrap_or_default();
464465

465-
self.env.refunded_gas = refunded_gas;
466+
self.accrued_substate.refunded_gas = refunded_gas;
466467

467468
Ok(())
468469
}

0 commit comments

Comments
 (0)