Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(levm): fix execution of eftests #1169

Open
wants to merge 51 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
8d5b4f4
delete revert for create
JereSalo Nov 14, 2024
1588136
change return type of execute function
JereSalo Nov 14, 2024
a1cc72d
propagate internal errors in execute()
JereSalo Nov 14, 2024
edbcf91
uncomment particular test
JereSalo Nov 14, 2024
b260fac
remove initial gas for env in transact()
JereSalo Nov 14, 2024
30d82cf
remove duplicate initial_call_frame
JereSalo Nov 14, 2024
e7992f6
start with transact refactor
JereSalo Nov 14, 2024
952174b
propagate adequate errors in validate_transaction
JereSalo Nov 14, 2024
7707224
make some changes in transact and validations
JereSalo Nov 19, 2024
704eec6
change add_gas_with_max of report
JereSalo Nov 19, 2024
4f52377
add '?' to add_gas_with_max
JereSalo Nov 19, 2024
33c5fca
make changes in gas consumption, intrinsic gas
JereSalo Nov 19, 2024
fb6684f
add some todos
JereSalo Nov 19, 2024
4c2479c
add early validations
JereSalo Nov 19, 2024
5d2eae2
add intrinsic gas validation
JereSalo Nov 19, 2024
1a57c89
add some relevante fields to environment
JereSalo Nov 19, 2024
1512f35
add more validations
JereSalo Nov 19, 2024
fec5343
fix some tests
JereSalo Nov 19, 2024
7bff0c1
fix intrinsic gas test
JereSalo Nov 19, 2024
f810b6b
make minor changes
JereSalo Nov 19, 2024
6285a25
make little change in execute_tx_levm
JereSalo Nov 19, 2024
5ce9714
merge main into fixing_ef_tests_execution
JereSalo Nov 20, 2024
86c9c2e
Handle all post states instead of taking first
maximopalopoli Nov 20, 2024
5352e7b
fmt changes
maximopalopoli Nov 20, 2024
4310222
Merge branch 'levm/fix/handle-all-post-states' into levm/fixing_ef_te…
JereSalo Nov 20, 2024
92d3f6b
add comments and organise validate_transaction
JereSalo Nov 20, 2024
2deff65
Aviod printing twice the report
maximopalopoli Nov 20, 2024
827d9bf
Fetch the correct post state from tuple of indexes
maximopalopoli Nov 20, 2024
c488efe
Merge branch 'main' into levm/fix/handle-all-post-states
maximopalopoli Nov 20, 2024
d844d92
merge handle_all_post_states
JereSalo Nov 20, 2024
926e9c0
fix failing test of InsufficientMaxFeePerGas
JereSalo Nov 20, 2024
d33f82a
fix error in return
JereSalo Nov 20, 2024
a8a39bd
fix error with revert
JereSalo Nov 20, 2024
6288388
comment debugging stuff
JereSalo Nov 20, 2024
6b34d91
Change bytes assignment in extcodecopy
maximopalopoli Nov 20, 2024
753f00f
Fix clippy alerts about unexpected side effects
maximopalopoli Nov 20, 2024
eb337c4
Prevent overflows in intrinsic gas cost calc
maximopalopoli Nov 20, 2024
0847204
Fix an alert from benchmarks
maximopalopoli Nov 20, 2024
09cd80b
fix failing unit tests (they were adding TX_BASE_COST for gas calcula…
JereSalo Nov 21, 2024
79b3d73
remove vectors folder
JereSalo Nov 21, 2024
12ece12
make little adjustments
JereSalo Nov 21, 2024
1601505
change some OutOfGas for VeryLargeNumber in some opcodes
JereSalo Nov 21, 2024
819ff65
replace some internal errors for VeryLargeNumber
JereSalo Nov 21, 2024
97efbe1
add type 3 validations
JereSalo Nov 21, 2024
52bcc2c
add constant of VALID_BLOB_PREFIXES for versioned blob hashes
JereSalo Nov 21, 2024
3738159
make little fix
JereSalo Nov 21, 2024
38928e7
make some comments (every test executes properly now, except some tes…
JereSalo Nov 21, 2024
616f207
remove comment
JereSalo Nov 21, 2024
55517d5
feat(levm): ensure returned errors are correct (#1225)
maximopalopoli Nov 22, 2024
c9058f8
feat(levm): improve revert behavior and reorganize transact (#1231)
JereSalo Nov 22, 2024
2d1568b
uncomment 'running test'
JereSalo Nov 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 96 additions & 3 deletions cmd/ef_tests/levm/deserialize.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,77 @@
use crate::types::EFTest;
use crate::types::{EFTest, TransactionExpectedException};
use bytes::Bytes;
use ethrex_core::U256;
use serde::Deserialize;
use ethrex_core::{H256, U256};
use serde::{Deserialize, Deserializer};
use std::{collections::HashMap, str::FromStr};

use crate::types::{EFTestRawTransaction, EFTestTransaction};

pub fn deserialize_transaction_expected_exception<'de, D>(
deserializer: D,
) -> Result<Option<Vec<TransactionExpectedException>>, D::Error>
where
D: Deserializer<'de>,
{
let option: Option<String> = Option::deserialize(deserializer)?;

if let Some(value) = option {
let exceptions = value
.split('|')
.map(|s| match s.trim() {
"TransactionException.INITCODE_SIZE_EXCEEDED" => {
TransactionExpectedException::InitcodeSizeExceeded
}
"TransactionException.NONCE_IS_MAX" => TransactionExpectedException::NonceIsMax,
"TransactionException.TYPE_3_TX_BLOB_COUNT_EXCEEDED" => {
TransactionExpectedException::Type3TxBlobCountExceeded
}
"TransactionException.TYPE_3_TX_ZERO_BLOBS" => {
TransactionExpectedException::Type3TxZeroBlobs
}
"TransactionException.TYPE_3_TX_CONTRACT_CREATION" => {
TransactionExpectedException::Type3TxContractCreation
}
"TransactionException.TYPE_3_TX_INVALID_BLOB_VERSIONED_HASH" => {
TransactionExpectedException::Type3TxInvalidBlobVersionedHash
}
"TransactionException.INTRINSIC_GAS_TOO_LOW" => {
TransactionExpectedException::IntrinsicGasTooLow
}
"TransactionException.INSUFFICIENT_ACCOUNT_FUNDS" => {
TransactionExpectedException::InsufficientAccountFunds
}
"TransactionException.SENDER_NOT_EOA" => TransactionExpectedException::SenderNotEoa,
"TransactionException.PRIORITY_GREATER_THAN_MAX_FEE_PER_GAS" => {
TransactionExpectedException::PriorityGreaterThanMaxFeePerGas
}
"TransactionException.GAS_ALLOWANCE_EXCEEDED" => {
TransactionExpectedException::GasAllowanceExceeded
}
"TransactionException.INSUFFICIENT_MAX_FEE_PER_GAS" => {
TransactionExpectedException::InsufficientMaxFeePerGas
}
"TransactionException.RLP_INVALID_VALUE" => {
TransactionExpectedException::RlpInvalidValue
}
"TransactionException.GASLIMIT_PRICE_PRODUCT_OVERFLOW" => {
TransactionExpectedException::GasLimitPriceProductOverflow
}
"TransactionException.TYPE_3_TX_PRE_FORK" => {
TransactionExpectedException::Type3TxPreFork
}
"TransactionException.INSUFFICIENT_MAX_FEE_PER_BLOB_GAS" => {
TransactionExpectedException::InsufficientMaxFeePerBlobGas
}
other => panic!("Unexpected error type: {}", other), // Should not fail, TODO is to return an error
})
.collect();

Ok(Some(exceptions))
} else {
Ok(None)
}
}

pub fn deserialize_ef_post_value_indexes<'de, D>(
deserializer: D,
) -> Result<HashMap<String, U256>, D::Error>
Expand Down Expand Up @@ -52,6 +118,29 @@ where
Ok(ret)
}

pub fn deserialize_h256_vec_optional_safe<'de, D>(
deserializer: D,
) -> Result<Option<Vec<H256>>, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = Option::<Vec<String>>::deserialize(deserializer)?;
match s {
Some(s) => {
let mut ret = Vec::new();
for s in s {
ret.push(H256::from_str(s.trim_start_matches("0x")).map_err(|err| {
serde::de::Error::custom(format!(
"error parsing H256 when deserializing H256 vec optional: {err}"
))
})?);
}
Ok(Some(ret))
}
None => Ok(None),
}
}

pub fn deserialize_u256_safe<'de, D>(deserializer: D) -> Result<U256, D::Error>
where
D: serde::Deserializer<'de>,
Expand Down Expand Up @@ -165,6 +254,10 @@ impl<'de> Deserialize<'de> for EFTest {
sender: raw_tx.sender,
to: raw_tx.to.clone(),
value: *value,
max_fee_per_gas: raw_tx.max_fee_per_gas,
max_priority_fee_per_gas: raw_tx.max_priority_fee_per_gas,
max_fee_per_blob_gas: raw_tx.max_fee_per_blob_gas,
blob_versioned_hashes: raw_tx.blob_versioned_hashes.clone(),
};
transactions.push(((data_id, gas_limit_id, value_id), tx));
}
Expand Down
191 changes: 164 additions & 27 deletions cmd/ef_tests/levm/runner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
use crate::{report::EFTestsReport, types::EFTest, utils};
use crate::{
report::EFTestsReport,
types::{EFTest, EFTestPostValue, TransactionExpectedException},
utils,
};
use ethrex_core::{
types::{code_hash, AccountInfo},
H256, U256,
Expand Down Expand Up @@ -32,21 +36,29 @@ pub fn run_ef_tests() -> Result<EFTestsReport, Box<dyn Error>> {
})
{
// TODO: Figure out what to do with overflowed value: 0x10000000000000000000000000000000000000000000000000000000000000001.
// Deserialization fails because the value is too big for U256.
// Deserialization of ValueOverflowParis fails because the value is too big for U256.
// Intrinsic is skipped because execution fails as access lists are not yet implemented.
if test
.path()
.file_name()
.is_some_and(|name| name == "ValueOverflowParis.json")
.is_some_and(|name| name == "ValueOverflowParis.json" || name == "intrinsic.json")
{
continue;
}
// 'If' for running a specific test when necessary.
// if test
// .path()
// .file_name()
// .is_some_and(|name| name == "valCausesOOF.json")
// {
let test_result = run_ef_test(
serde_json::from_reader(std::fs::File::open(test.path())?)?,
&mut report,
);
if test_result.is_err() {
continue;
}
// }
}
spinner.update_text(report.progress());
}
Expand All @@ -64,13 +76,18 @@ pub fn run_ef_test_tx(
let mut evm = prepare_vm_for_tx(tx_id, test)?;
ensure_pre_state(&evm, test)?;
let execution_result = evm.transact();
ensure_post_state(execution_result, test, report)?;
ensure_post_state(execution_result, test, report, tx_id)?;
Ok(())
}

pub fn run_ef_test(test: EFTest, report: &mut EFTestsReport) -> Result<(), Box<dyn Error>> {
println!("Running test: {}", &test.name);
let mut failed = false;
for (tx_id, (tx_indexes, _tx)) in test.transactions.iter().enumerate() {
// Code for debugging a specific case.
// if *tx_indexes != (346, 0, 0) {
// continue;
// }
match run_ef_test_tx(tx_id, &test, report) {
Ok(_) => {}
Err(e) => {
Expand Down Expand Up @@ -100,7 +117,7 @@ pub fn prepare_vm_for_tx(tx_id: usize, test: &EFTest) -> Result<VM, Box<dyn Erro
origin: test.transactions.get(tx_id).unwrap().1.sender,
consumed_gas: U256::default(),
refunded_gas: U256::default(),
gas_limit: test.env.current_gas_limit,
gas_limit: test.transactions.get(tx_id).unwrap().1.gas_limit, // Gas limit of Tx
block_number: test.env.current_number,
coinbase: test.env.current_coinbase,
timestamp: test.env.current_timestamp,
Expand All @@ -116,7 +133,22 @@ pub fn prepare_vm_for_tx(tx_id: usize, test: &EFTest) -> Result<VM, Box<dyn Erro
.unwrap_or_default(), // or max_fee_per_gas?
block_excess_blob_gas: Some(test.env.current_excess_blob_gas),
block_blob_gas_used: None,
tx_blob_hashes: None,
tx_blob_hashes: test
.transactions
.get(tx_id)
.unwrap()
.1
.blob_versioned_hashes
.clone(),
block_gas_limit: test.env.current_gas_limit,
tx_max_priority_fee_per_gas: test
.transactions
.get(tx_id)
.unwrap()
.1
.max_priority_fee_per_gas,
tx_max_fee_per_gas: test.transactions.get(tx_id).unwrap().1.max_fee_per_gas,
tx_max_fee_per_blob_gas: test.transactions.get(tx_id).unwrap().1.max_fee_per_blob_gas,
},
test.transactions.get(tx_id).unwrap().1.value,
test.transactions.get(tx_id).unwrap().1.data.clone(),
Expand Down Expand Up @@ -184,31 +216,121 @@ fn ensure_pre_state_condition(condition: bool, error_reason: String) -> Result<(
Ok(())
}

fn get_indexes_tuple(post_value: &EFTestPostValue) -> Option<(usize, usize, usize)> {
let data_index: usize = post_value.indexes.get("data")?.as_usize();
let gas_index: usize = post_value.indexes.get("gas")?.as_usize();
let value_index: usize = post_value.indexes.get("value")?.as_usize();
Some((data_index, gas_index, value_index))
}

fn get_post_value(test: &EFTest, tx_id: usize) -> Option<EFTestPostValue> {
if let Some(transaction) = test.transactions.get(tx_id) {
let indexes = transaction.0;
test.post
.clone()
.iter()
.find(|post_value| {
if let Some(post_indexes) = get_indexes_tuple(post_value) {
indexes == post_indexes
} else {
false
}
})
.cloned()
} else {
None
}
}

// Exceptions not covered: RlpInvalidValue and Type3TxPreFork
fn exception_is_expected(
expected_exceptions: Vec<TransactionExpectedException>,
returned_error: VMError,
) -> bool {
expected_exceptions.iter().any(|exception| {
matches!(
(exception, &returned_error),
(
TransactionExpectedException::IntrinsicGasTooLow,
VMError::IntrinsicGasTooLow
) | (
TransactionExpectedException::InsufficientAccountFunds,
VMError::InsufficientAccountFunds
) | (
TransactionExpectedException::PriorityGreaterThanMaxFeePerGas,
VMError::PriorityGreaterThanMaxFeePerGas
) | (
TransactionExpectedException::GasLimitPriceProductOverflow,
VMError::GasLimitPriceProductOverflow
) | (
TransactionExpectedException::SenderNotEoa,
VMError::SenderNotEOA
) | (
TransactionExpectedException::InsufficientMaxFeePerGas,
VMError::InsufficientMaxFeePerGas
) | (
TransactionExpectedException::NonceIsMax,
VMError::NonceIsMax
) | (
TransactionExpectedException::GasAllowanceExceeded,
VMError::GasAllowanceExceeded
) | (
TransactionExpectedException::Type3TxBlobCountExceeded,
VMError::Type3TxBlobCountExceeded
) | (
TransactionExpectedException::Type3TxZeroBlobs,
VMError::Type3TxZeroBlobs
) | (
TransactionExpectedException::Type3TxContractCreation,
VMError::Type3TxContractCreation
) | (
TransactionExpectedException::Type3TxInvalidBlobVersionedHash,
VMError::Type3TxInvalidBlobVersionedHash
) | (
TransactionExpectedException::InsufficientMaxFeePerBlobGas,
VMError::InsufficientMaxFeePerBlobGas
) | (
TransactionExpectedException::InitcodeSizeExceeded,
VMError::InitcodeSizeExceeded
)
)
})
}

pub fn ensure_post_state(
execution_result: Result<TransactionReport, VMError>,
test: &EFTest,
report: &mut EFTestsReport,
tx_id: usize,
) -> Result<(), Box<dyn Error>> {
let post_value = get_post_value(test, tx_id);
match execution_result {
Ok(execution_report) => {
match test
.post
.clone()
.values()
.first()
.map(|v| v.clone().expect_exception)
{
match post_value.clone().map(|v| v.clone().expect_exception) {
// Execution result was successful but an exception was expected.
Some(Some(expected_exception)) => {
let error_reason = format!("Expected exception: {expected_exception}");
Some(Some(expected_exceptions)) => {
let error_reason = match expected_exceptions.get(1) {
Some(second_exception) => {
format!(
"Expected exception: {:?} or {:?}",
expected_exceptions.first().unwrap(),
second_exception
)
}
None => {
format!(
"Expected exception: {:?}",
expected_exceptions.first().unwrap()
)
}
};

return Err(format!("Post-state condition failed: {error_reason}").into());
}
// Execution result was successful and no exception was expected.
// TODO: Check that the post-state matches the expected post-state.
None | Some(None) => {
let pos_state_root = post_state_root(execution_report, test);
let expected_post_state_value = test.post.iter().next().cloned();
if let Some(expected_post_state_root_hash) = expected_post_state_value {
if let Some(expected_post_state_root_hash) = post_value {
let expected_post_state_root_hash = expected_post_state_root_hash.hash;
if expected_post_state_root_hash != pos_state_root {
let error_reason = format!(
Expand All @@ -226,16 +348,31 @@ pub fn ensure_post_state(
}
}
Err(err) => {
match test
.post
.clone()
.values()
.first()
.map(|v| v.clone().expect_exception)
{
match post_value.map(|v| v.clone().expect_exception) {
// Execution result was unsuccessful and an exception was expected.
// TODO: Check that the exception matches the expected exception.
Some(Some(_expected_exception)) => {}
Some(Some(expected_exceptions)) => {
// Instead of cloning could use references
if !exception_is_expected(expected_exceptions.clone(), err.clone()) {
let error_reason = match expected_exceptions.get(1) {
Some(second_exception) => {
format!(
"Returned exception is not the expected: Returned {:?} but expected {:?} or {:?}",
err,
expected_exceptions.first().unwrap(),
second_exception
)
}
None => {
format!(
"Returned exception is not the expected: Returned {:?} but expected {:?}",
err,
expected_exceptions.first().unwrap()
)
}
};
return Err(format!("Post-state condition failed: {error_reason}").into());
}
}
// Execution result was unsuccessful but no exception was expected.
None | Some(None) => {
let error_reason = format!("Unexpected exception: {err:?}");
Expand Down
3 changes: 1 addition & 2 deletions cmd/ef_tests/levm/tests/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use ef_tests_levm::runner;

fn main() {
let report = runner::run_ef_tests().unwrap();
println!("{report}");
runner::run_ef_tests().unwrap();
}
Loading
Loading