Skip to content

Commit e08e959

Browse files
authored
refactor(levm): improve and simplify some db functions (#2651)
**Motivation** <!-- Why does this pull request exist? What are its goals? --> - Try to remove `account_exists` if possible because it adds complexity and unnecessary checks to the DB. - Try to finally remove `get_account_no_push_cache`, which is related to the previous thing too. **Description** - We now ignore a specific test because [EIP-7702 spec has changed](ethereum/EIPs#9710) and we no longer need to check if the account exists in the trie. - Remove `Option` from `specific_tests` - Remove `get_account_no_push_cache` and the usage of `account_exists` in LEVM. This method is not deleted from the Database because it's used in `get_state_transitions`, and even here it could be removed but I think it is better to keep it in this PR and maybe decide later what to do with this function. (If we remove it it wouldn't make a difference to the state though). - We were able to remove a SpuriousDragon check because we don't support pre-merge forks now Note: `account_exists` hasn't been completely removed from `Database` because we use it in `get_state_transitions` but that is going to change soon and we'll be able to remove it. <!-- Link to issues: Resolves #111, Resolves #222 --> Closes #issue_number
1 parent e920898 commit e08e959

File tree

6 files changed

+32
-54
lines changed

6 files changed

+32
-54
lines changed

cmd/ef_tests/state/parser.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ const IGNORED_TESTS: [&str; 11] = [
3232
"InitCollision.json", // Skip because it fails on REVM
3333
];
3434

35+
// One .json can have multiple tests, sometimes we want to skip one of those.
36+
pub const SPECIFIC_IGNORED_TESTS: [&str; 1] = [
37+
"test_set_code_to_non_empty_storage[fork_Prague-state_test-zero_nonce]", // Skip because EIP-7702 has changed. See https://github.com/ethereum/EIPs/pull/9710
38+
];
39+
3540
pub fn parse_ef_tests(opts: &EFTestRunnerOptions) -> Result<Vec<EFTest>, EFTestParseError> {
3641
let parsing_time = std::time::Instant::now();
3742
let cargo_manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));

cmd/ef_tests/state/runner/mod.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::collections::HashMap;
22

33
use crate::{
4+
parser::SPECIFIC_IGNORED_TESTS,
45
report::{self, format_duration_as_mm_ss, EFTestReport, TestReRunReport},
56
types::EFTest,
67
};
@@ -61,8 +62,8 @@ pub struct EFTestRunnerOptions {
6162
#[arg(short, long, value_name = "TESTS", use_value_delimiter = true)]
6263
pub tests: Vec<String>,
6364
/// For running tests with a specific name
64-
#[arg(value_name = "SPECIFIC_TESTS", use_value_delimiter = true)]
65-
pub specific_tests: Option<Vec<String>>,
65+
#[arg(long, value_name = "SPECIFIC_TESTS", use_value_delimiter = true)]
66+
pub specific_tests: Vec<String>,
6667
/// For running tests only with LEVM without the REVM re-run.
6768
#[arg(short, long, value_name = "SUMMARY", default_value = "false")]
6869
pub summary: bool,
@@ -106,9 +107,17 @@ async fn run_with_levm(
106107
println!("{}", report::progress(reports, levm_run_time.elapsed()));
107108

108109
for test in ef_tests.iter() {
109-
if opts.specific_tests.is_some()
110-
&& !opts.specific_tests.clone().unwrap().contains(&test.name)
111-
{
110+
let is_not_specific = !opts.specific_tests.is_empty()
111+
&& !opts
112+
.specific_tests
113+
.iter()
114+
.any(|name| test.name.contains(name));
115+
let is_ignored = SPECIFIC_IGNORED_TESTS
116+
.iter()
117+
.any(|skip| test.name.contains(skip));
118+
119+
// Skip tests that are not specific (if specific tests were indicated) and ignored ones.
120+
if is_not_specific || is_ignored {
112121
continue;
113122
}
114123
if opts.verbose {

crates/vm/levm/src/db/gen_db.rs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,6 @@ impl GeneralizedDatabase {
4242
}
4343
}
4444

45-
/// Gets account without pushing it to the cache
46-
pub fn get_account_no_push_cache(&self, address: Address) -> Result<Account, DatabaseError> {
47-
match cache::get_account(&self.cache, &address) {
48-
Some(acc) => Ok(acc.clone()),
49-
None => self.store.get_account(address),
50-
}
51-
}
52-
5345
/// **Accesses to an account's information.**
5446
///
5547
/// Accessed accounts are stored in the `touched_accounts` set.

crates/vm/levm/src/gas_cost.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -832,7 +832,6 @@ pub fn call(
832832
current_memory_size: usize,
833833
address_was_cold: bool,
834834
address_is_empty: bool,
835-
address_exists: bool,
836835
value_to_transfer: U256,
837836
gas_from_stack: U256,
838837
gas_left: u64,
@@ -858,10 +857,8 @@ pub fn call(
858857
} else {
859858
0
860859
};
861-
// https://eips.ethereum.org/EIPS/eip-161 change the definition of empty address
862-
let value_to_empty_account = if (!address_exists && fork < Fork::SpuriousDragon)
863-
|| address_is_empty && !value_to_transfer.is_zero() && fork >= Fork::SpuriousDragon
864-
{
860+
861+
let value_to_empty_account = if address_is_empty && !value_to_transfer.is_zero() {
865862
CALL_TO_EMPTY_ACCOUNT
866863
} else {
867864
0

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

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ impl<'a> VM<'a> {
7171
calculate_memory_size(return_data_start_offset, return_data_size)?;
7272
let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data);
7373

74-
let (account_info, address_was_cold) =
74+
let (account, address_was_cold) =
7575
self.db.access_account(&mut self.accrued_substate, callee)?;
7676

7777
let (is_delegation, eip7702_gas_consumed, code_address, bytecode) =
@@ -89,8 +89,7 @@ impl<'a> VM<'a> {
8989
new_memory_size,
9090
current_memory_size,
9191
address_was_cold,
92-
account_info.is_empty(),
93-
account_exists(self.db, callee),
92+
account.is_empty(),
9493
value_to_transfer,
9594
gas,
9695
gas_left,
@@ -589,15 +588,10 @@ impl<'a> VM<'a> {
589588
self.db.access_account(&mut self.accrued_substate, to)?;
590589
let balance_to_transfer = current_account.info.balance;
591590

592-
let account_is_empty = if self.env.config.fork >= Fork::SpuriousDragon {
593-
target_account.is_empty()
594-
} else {
595-
!account_exists(self.db, target_address)
596-
};
597591
self.current_call_frame_mut()?
598592
.increase_consumed_gas(gas_cost::selfdestruct(
599593
target_account_is_cold,
600-
account_is_empty,
594+
target_account.is_empty(),
601595
balance_to_transfer,
602596
fork,
603597
)?)?;
@@ -632,11 +626,6 @@ impl<'a> VM<'a> {
632626

633627
self.accrued_substate.selfdestruct_set.insert(to);
634628
}
635-
if account_exists(self.db, target_address) && target_account.is_empty() {
636-
self.accrued_substate
637-
.touched_accounts
638-
.insert(target_address);
639-
}
640629

641630
Ok(OpcodeResult::Halt)
642631
}

crates/vm/levm/src/utils.rs

Lines changed: 8 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
use crate::{
22
constants::*,
3-
db::{
4-
cache::{self},
5-
gen_db::GeneralizedDatabase,
6-
},
3+
db::gen_db::GeneralizedDatabase,
74
errors::{InternalError, OutOfGasError, TxValidationError, VMError},
85
gas_cost::{
96
self, fake_exponential, ACCESS_LIST_ADDRESS_COST, ACCESS_LIST_STORAGE_KEY_COST,
@@ -331,12 +328,12 @@ pub fn eip7702_recover_address(
331328
/// The idea of this function comes from ethereum/execution-specs:
332329
/// https://github.com/ethereum/execution-specs/blob/951fc43a709b493f27418a8e57d2d6f3608cef84/src/ethereum/prague/vm/eoa_delegation.py#L115
333330
pub fn eip7702_get_code(
334-
db: &GeneralizedDatabase,
331+
db: &mut GeneralizedDatabase,
335332
accrued_substate: &mut Substate,
336333
address: Address,
337334
) -> Result<(bool, u64, Address, Bytes), VMError> {
338335
// Address is the delgated address
339-
let account = db.get_account_no_push_cache(address)?;
336+
let account = db.get_account(address)?;
340337
let bytecode = account.code.clone();
341338

342339
// If the Address doesn't have a delegation code
@@ -358,19 +355,11 @@ pub fn eip7702_get_code(
358355
COLD_ADDRESS_ACCESS_COST
359356
};
360357

361-
let authorized_bytecode = db.get_account_no_push_cache(auth_address)?.code;
358+
let authorized_bytecode = db.get_account(auth_address)?.code;
362359

363360
Ok((true, access_cost, auth_address, authorized_bytecode))
364361
}
365362

366-
/// Checks if a given account exists in the database or cache
367-
pub fn account_exists(db: &mut GeneralizedDatabase, address: Address) -> bool {
368-
match cache::get_account(&db.cache, &address) {
369-
Some(_) => true,
370-
None => db.store.account_exists(address),
371-
}
372-
}
373-
374363
impl<'a> VM<'a> {
375364
/// Sets the account code as the EIP7702 determines.
376365
pub fn eip7702_set_access_code(&mut self) -> Result<(), VMError> {
@@ -400,10 +389,9 @@ impl<'a> VM<'a> {
400389
};
401390

402391
// 4. Add authority to accessed_addresses (as defined in EIP-2929).
403-
self.accrued_substate
404-
.touched_accounts
405-
.insert(authority_address);
406-
let authority_account = self.db.get_account_no_push_cache(authority_address)?;
392+
let (authority_account, _address_was_cold) = self
393+
.db
394+
.access_account(&mut self.accrued_substate, authority_address)?;
407395

408396
// 5. Verify the code of authority is either empty or already delegated.
409397
let empty_or_delegated =
@@ -420,9 +408,7 @@ impl<'a> VM<'a> {
420408
}
421409

422410
// 7. Add PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST gas to the global refund counter if authority exists in the trie.
423-
if cache::is_account_cached(&self.db.cache, &authority_address)
424-
|| account_exists(self.db, authority_address)
425-
{
411+
if !authority_account.is_empty() {
426412
let refunded_gas_if_exists = PER_EMPTY_ACCOUNT_COST - PER_AUTH_BASE_COST;
427413
refunded_gas = refunded_gas
428414
.checked_add(refunded_gas_if_exists)

0 commit comments

Comments
 (0)