From 4de7a5e8b5f846248502f72a1be070f3902e1ad7 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 19 Nov 2024 12:45:10 -0300 Subject: [PATCH 01/49] Do not skip `VMTests` --- cmd/ef_tests/levm/runner.rs | 76 +++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/cmd/ef_tests/levm/runner.rs b/cmd/ef_tests/levm/runner.rs index 691b89adc..2c8e087bc 100644 --- a/cmd/ef_tests/levm/runner.rs +++ b/cmd/ef_tests/levm/runner.rs @@ -13,7 +13,7 @@ use ethereum_rust_storage::AccountUpdate; use ethereum_rust_vm::db::StoreWrapper; use keccak_hash::keccak; use spinoff::{spinners::Dots, Color, Spinner}; -use std::{collections::HashMap, error::Error, sync::Arc}; +use std::{collections::HashMap, error::Error, fs::DirEntry, sync::Arc}; pub fn run_ef_tests() -> Result> { let mut report = EFTestsReport::default(); @@ -21,34 +21,7 @@ pub fn run_ef_tests() -> Result> { let ef_general_state_tests_path = cargo_manifest_dir.join("vectors/GeneralStateTests"); let mut spinner = Spinner::new(Dots, report.progress(), Color::Cyan); for test_dir in std::fs::read_dir(ef_general_state_tests_path)?.flatten() { - for test in std::fs::read_dir(test_dir.path())? - .flatten() - .filter(|entry| { - entry - .path() - .extension() - .map(|ext| ext == "json") - .unwrap_or(false) - }) - { - // TODO: Figure out what to do with overflowed value: 0x10000000000000000000000000000000000000000000000000000000000000001. - // Deserialization fails because the value is too big for U256. - if test - .path() - .file_name() - .is_some_and(|name| name == "ValueOverflowParis.json") - { - continue; - } - 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()); + run_ef_test_dir(test_dir, &mut report, &mut spinner)?; } spinner.success(&report.progress()); let mut spinner = Spinner::new(Dots, "Loading report...".to_owned(), Color::Cyan); @@ -56,6 +29,51 @@ pub fn run_ef_tests() -> Result> { Ok(report) } +fn run_ef_test_dir( + test_dir: DirEntry, + report: &mut EFTestsReport, + spinner: &mut Spinner, +) -> Result<(), Box> { + if test_dir + .path() + .file_name() + .is_some_and(|name| name == "VMTests") + { + for sub_test_dir in std::fs::read_dir(test_dir.path())?.flatten() { + run_ef_test_dir(sub_test_dir, report, spinner)?; + } + } + for test in std::fs::read_dir(test_dir.path())? + .flatten() + .filter(|entry| { + entry + .path() + .extension() + .map(|ext| ext == "json") + .unwrap_or(false) + }) + { + // TODO: Figure out what to do with overflowed value: 0x10000000000000000000000000000000000000000000000000000000000000001. + // Deserialization fails because the value is too big for U256. + if test + .path() + .file_name() + .is_some_and(|name| name == "ValueOverflowParis.json") + { + continue; + } + let test_result = run_ef_test( + serde_json::from_reader(std::fs::File::open(test.path())?)?, + report, + ); + if test_result.is_err() { + continue; + } + } + spinner.update_text(report.progress()); + Ok(()) +} + pub fn run_ef_test_tx( tx_id: usize, test: &EFTest, From 33b948fdc055a34ed15169095d523191d2f2fba6 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:01:43 -0300 Subject: [PATCH 02/49] Refactor runner Now we have a levm_runner in charge of running the whole suite of tests (or the chosen ones) and then a revm_runner that re-runs the tests that levm failed to run successfully --- cmd/ef_tests/levm/runner.rs | 307 ------------------------ cmd/ef_tests/levm/runner/levm_runner.rs | 252 +++++++++++++++++++ cmd/ef_tests/levm/runner/mod.rs | 99 ++++++++ cmd/ef_tests/levm/runner/revm_runner.rs | 272 +++++++++++++++++++++ 4 files changed, 623 insertions(+), 307 deletions(-) delete mode 100644 cmd/ef_tests/levm/runner.rs create mode 100644 cmd/ef_tests/levm/runner/levm_runner.rs create mode 100644 cmd/ef_tests/levm/runner/mod.rs create mode 100644 cmd/ef_tests/levm/runner/revm_runner.rs diff --git a/cmd/ef_tests/levm/runner.rs b/cmd/ef_tests/levm/runner.rs deleted file mode 100644 index 2c8e087bc..000000000 --- a/cmd/ef_tests/levm/runner.rs +++ /dev/null @@ -1,307 +0,0 @@ -use crate::{report::EFTestsReport, types::EFTest, utils}; -use ethereum_rust_core::{ - types::{code_hash, AccountInfo}, - H256, U256, -}; -use ethereum_rust_levm::{ - db::Cache, - errors::{TransactionReport, VMError}, - vm::VM, - Environment, -}; -use ethereum_rust_storage::AccountUpdate; -use ethereum_rust_vm::db::StoreWrapper; -use keccak_hash::keccak; -use spinoff::{spinners::Dots, Color, Spinner}; -use std::{collections::HashMap, error::Error, fs::DirEntry, sync::Arc}; - -pub fn run_ef_tests() -> Result> { - let mut report = EFTestsReport::default(); - let cargo_manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); - let ef_general_state_tests_path = cargo_manifest_dir.join("vectors/GeneralStateTests"); - let mut spinner = Spinner::new(Dots, report.progress(), Color::Cyan); - for test_dir in std::fs::read_dir(ef_general_state_tests_path)?.flatten() { - run_ef_test_dir(test_dir, &mut report, &mut spinner)?; - } - spinner.success(&report.progress()); - let mut spinner = Spinner::new(Dots, "Loading report...".to_owned(), Color::Cyan); - spinner.success(&report.to_string()); - Ok(report) -} - -fn run_ef_test_dir( - test_dir: DirEntry, - report: &mut EFTestsReport, - spinner: &mut Spinner, -) -> Result<(), Box> { - if test_dir - .path() - .file_name() - .is_some_and(|name| name == "VMTests") - { - for sub_test_dir in std::fs::read_dir(test_dir.path())?.flatten() { - run_ef_test_dir(sub_test_dir, report, spinner)?; - } - } - for test in std::fs::read_dir(test_dir.path())? - .flatten() - .filter(|entry| { - entry - .path() - .extension() - .map(|ext| ext == "json") - .unwrap_or(false) - }) - { - // TODO: Figure out what to do with overflowed value: 0x10000000000000000000000000000000000000000000000000000000000000001. - // Deserialization fails because the value is too big for U256. - if test - .path() - .file_name() - .is_some_and(|name| name == "ValueOverflowParis.json") - { - continue; - } - let test_result = run_ef_test( - serde_json::from_reader(std::fs::File::open(test.path())?)?, - report, - ); - if test_result.is_err() { - continue; - } - } - spinner.update_text(report.progress()); - Ok(()) -} - -pub fn run_ef_test_tx( - tx_id: usize, - test: &EFTest, - report: &mut EFTestsReport, -) -> Result<(), Box> { - 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)?; - Ok(()) -} - -pub fn run_ef_test(test: EFTest, report: &mut EFTestsReport) -> Result<(), Box> { - let mut failed = false; - for (tx_id, (tx_indexes, _tx)) in test.transactions.iter().enumerate() { - match run_ef_test_tx(tx_id, &test, report) { - Ok(_) => {} - Err(e) => { - failed = true; - let error_message: &str = &e.to_string(); - report.register_fail(tx_indexes.to_owned(), &test.name, error_message); - } - } - } - if failed { - report.register_group_fail(); - } else { - report.register_group_pass(); - } - Ok(()) -} - -pub fn prepare_vm_for_tx(tx_id: usize, test: &EFTest) -> Result> { - let (initial_state, block_hash) = utils::load_initial_state(test); - let db = Arc::new(StoreWrapper { - store: initial_state.database().unwrap().clone(), - block_hash, - }); - let vm_result = VM::new( - test.transactions.get(tx_id).unwrap().1.to.clone(), - Environment { - origin: test.transactions.get(tx_id).unwrap().1.sender, - consumed_gas: U256::default(), - refunded_gas: U256::default(), - gas_limit: test.env.current_gas_limit, - block_number: test.env.current_number, - coinbase: test.env.current_coinbase, - timestamp: test.env.current_timestamp, - prev_randao: Some(test.env.current_random), - chain_id: U256::from(1729), - base_fee_per_gas: test.env.current_base_fee, - gas_price: test - .transactions - .get(tx_id) - .unwrap() - .1 - .gas_price - .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, - }, - test.transactions.get(tx_id).unwrap().1.value, - test.transactions.get(tx_id).unwrap().1.data.clone(), - db, - Cache::default(), - ); - - match vm_result { - Ok(vm) => Ok(vm), - Err(err) => { - let error_reason = format!("VM initialization failed: {err:?}"); - Err(error_reason.into()) - } - } -} - -pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), Box> { - let world_state = &evm.db; - for (address, pre_value) in &test.pre.0 { - let account = world_state.get_account_info(*address); - ensure_pre_state_condition( - account.nonce == pre_value.nonce.as_u64(), - format!( - "Nonce mismatch for account {:#x}: expected {}, got {}", - address, pre_value.nonce, account.nonce - ), - )?; - ensure_pre_state_condition( - account.balance == pre_value.balance, - format!( - "Balance mismatch for account {:#x}: expected {}, got {}", - address, pre_value.balance, account.balance - ), - )?; - for (k, v) in &pre_value.storage { - let mut key_bytes = [0u8; 32]; - k.to_big_endian(&mut key_bytes); - let storage_slot = world_state.get_storage_slot(*address, H256::from_slice(&key_bytes)); - ensure_pre_state_condition( - &storage_slot == v, - format!( - "Storage slot mismatch for account {:#x} at key {:?}: expected {}, got {}", - address, k, v, storage_slot - ), - )?; - } - ensure_pre_state_condition( - keccak(account.bytecode.clone()) == keccak(pre_value.code.as_ref()), - format!( - "Code hash mismatch for account {:#x}: expected {}, got {}", - address, - keccak(pre_value.code.as_ref()), - keccak(account.bytecode) - ), - )?; - } - Ok(()) -} - -fn ensure_pre_state_condition(condition: bool, error_reason: String) -> Result<(), Box> { - if !condition { - let error_reason = format!("Pre-state condition failed: {error_reason}"); - return Err(error_reason.into()); - } - Ok(()) -} - -pub fn ensure_post_state( - execution_result: Result, - test: &EFTest, - report: &mut EFTestsReport, -) -> Result<(), Box> { - match execution_result { - Ok(execution_report) => { - match test - .post - .clone() - .values() - .first() - .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}"); - 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 { - 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!( - "Post-state root mismatch: expected {expected_post_state_root_hash:#x}, got {pos_state_root:#x}", - ); - return Err( - format!("Post-state condition failed: {error_reason}").into() - ); - } - } else { - let error_reason = "No post-state root hash provided"; - return Err(format!("Post-state condition failed: {error_reason}").into()); - } - } - } - } - Err(err) => { - match test - .post - .clone() - .values() - .first() - .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)) => {} - // Execution result was unsuccessful but no exception was expected. - None | Some(None) => { - let error_reason = format!("Unexpected exception: {err:?}"); - return Err(format!("Post-state condition failed: {error_reason}").into()); - } - } - } - }; - report.register_pass(&test.name); - Ok(()) -} - -pub fn post_state_root(execution_report: TransactionReport, test: &EFTest) -> H256 { - let (initial_state, block_hash) = utils::load_initial_state(test); - - let mut account_updates: Vec = vec![]; - for (address, account) in execution_report.new_state { - let mut added_storage = HashMap::new(); - - for (key, value) in account.storage { - added_storage.insert(key, value.current_value); - } - - let code = if account.info.bytecode.is_empty() { - None - } else { - Some(account.info.bytecode.clone()) - }; - - let account_update = AccountUpdate { - address, - removed: false, - info: Some(AccountInfo { - code_hash: code_hash(&account.info.bytecode), - balance: account.info.balance, - nonce: account.info.nonce, - }), - code, - added_storage, - }; - - account_updates.push(account_update); - } - - initial_state - .database() - .unwrap() - .apply_account_updates(block_hash, &account_updates) - .unwrap() - .unwrap() -} diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs new file mode 100644 index 000000000..39b43127a --- /dev/null +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -0,0 +1,252 @@ +use crate::{ + report::{EFTestReport, TestVector}, + runner::EFTestRunnerError, + types::EFTest, + utils, +}; +use ethereum_rust_core::{ + types::{code_hash, AccountInfo}, + H256, U256, +}; +use ethereum_rust_levm::{ + db::Cache, + errors::{TransactionReport, VMError}, + vm::VM, + Environment, +}; +use ethereum_rust_storage::AccountUpdate; +use ethereum_rust_vm::db::StoreWrapper; +use keccak_hash::keccak; +use std::{collections::HashMap, sync::Arc}; + +pub fn run_ef_test(test: &EFTest) -> Result { + let mut ef_test_report = EFTestReport::new(test.name.clone(), test.fork()); + for (vector, _tx) in test.transactions.iter() { + match run_ef_test_tx(vector, test) { + Ok(_) => continue, + Err(EFTestRunnerError::VMInitializationFailed(reason)) => { + ef_test_report.register_vm_initialization_failure(reason, *vector); + } + Err(EFTestRunnerError::FailedToEnsurePreState(reason)) => { + ef_test_report.register_pre_state_validation_failure(reason, *vector); + } + Err(EFTestRunnerError::ExecutionFailedUnexpectedly(error)) => { + ef_test_report.register_unexpected_execution_failure(error, *vector); + } + Err(EFTestRunnerError::FailedToEnsurePostState(transaction_report, reason)) => { + ef_test_report.register_post_state_validation_failure( + transaction_report, + reason, + *vector, + ); + } + Err(EFTestRunnerError::VMExecutionMismatch(_)) => { + return Err(EFTestRunnerError::Internal( + "VM execution mismatch errors should only happen when running with revm. This failed during levm's execution." + .to_owned(), + )); + } + Err(EFTestRunnerError::Internal(reason)) => { + return Err(EFTestRunnerError::Internal(reason)); + } + } + } + Ok(ef_test_report) +} + +pub fn run_ef_test_tx(vector: &TestVector, test: &EFTest) -> Result<(), EFTestRunnerError> { + let mut levm = prepare_vm_for_tx(vector, test)?; + ensure_pre_state(&levm, test)?; + let levm_execution_result = levm.transact(); + ensure_post_state(&levm_execution_result, vector, test)?; + Ok(()) +} + +pub fn prepare_vm_for_tx(vector: &TestVector, test: &EFTest) -> Result { + let (initial_state, block_hash) = utils::load_initial_state(test); + let db = Arc::new(StoreWrapper { + store: initial_state.database().unwrap().clone(), + block_hash, + }); + VM::new( + test.transactions.get(vector).unwrap().to.clone(), + Environment { + origin: test.transactions.get(vector).unwrap().sender, + consumed_gas: U256::default(), + refunded_gas: U256::default(), + gas_limit: test.env.current_gas_limit, + block_number: test.env.current_number, + coinbase: test.env.current_coinbase, + timestamp: test.env.current_timestamp, + prev_randao: test.env.current_random, + chain_id: U256::from(1729), + base_fee_per_gas: test.env.current_base_fee.unwrap_or_default(), + gas_price: test + .transactions + .get(vector) + .unwrap() + .gas_price + .unwrap_or_default(), // or max_fee_per_gas? + block_excess_blob_gas: test.env.current_excess_blob_gas, + block_blob_gas_used: None, + tx_blob_hashes: None, + }, + test.transactions.get(vector).unwrap().value, + test.transactions.get(vector).unwrap().data.clone(), + db, + Cache::default(), + ) + .map_err(|err| EFTestRunnerError::VMInitializationFailed(err.to_string())) +} + +pub fn ensure_pre_state(evm: &VM, test: &EFTest) -> Result<(), EFTestRunnerError> { + let world_state = &evm.db; + for (address, pre_value) in &test.pre.0 { + let account = world_state.get_account_info(*address); + ensure_pre_state_condition( + account.nonce == pre_value.nonce.as_u64(), + format!( + "Nonce mismatch for account {:#x}: expected {}, got {}", + address, pre_value.nonce, account.nonce + ), + )?; + ensure_pre_state_condition( + account.balance == pre_value.balance, + format!( + "Balance mismatch for account {:#x}: expected {}, got {}", + address, pre_value.balance, account.balance + ), + )?; + for (k, v) in &pre_value.storage { + let mut key_bytes = [0u8; 32]; + k.to_big_endian(&mut key_bytes); + let storage_slot = world_state.get_storage_slot(*address, H256::from_slice(&key_bytes)); + ensure_pre_state_condition( + &storage_slot == v, + format!( + "Storage slot mismatch for account {:#x} at key {:?}: expected {}, got {}", + address, k, v, storage_slot + ), + )?; + } + ensure_pre_state_condition( + keccak(account.bytecode.clone()) == keccak(pre_value.code.as_ref()), + format!( + "Code hash mismatch for account {:#x}: expected {}, got {}", + address, + keccak(pre_value.code.as_ref()), + keccak(account.bytecode) + ), + )?; + } + Ok(()) +} + +fn ensure_pre_state_condition( + condition: bool, + error_reason: String, +) -> Result<(), EFTestRunnerError> { + if !condition { + return Err(EFTestRunnerError::FailedToEnsurePreState(error_reason)); + } + Ok(()) +} + +pub fn ensure_post_state( + levm_execution_result: &Result, + vector: &TestVector, + test: &EFTest, +) -> Result<(), EFTestRunnerError> { + match levm_execution_result { + Ok(execution_report) => { + match test.post.vector_post_value(vector).expect_exception { + // Execution result was successful but an exception was expected. + Some(expected_exception) => { + let error_reason = format!("Expected exception: {expected_exception}"); + return Err(EFTestRunnerError::FailedToEnsurePostState( + execution_report.clone(), + error_reason, + )); + } + // Execution result was successful and no exception was expected. + None => { + let levm_account_updates = get_state_transitions(execution_report); + let pos_state_root = post_state_root(&levm_account_updates, test); + let expected_post_state_value = test.post.iter().next().cloned(); + if let Some(expected_post_state_root_hash) = expected_post_state_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!( + "Post-state root mismatch: expected {expected_post_state_root_hash:#x}, got {pos_state_root:#x}", + ); + return Err(EFTestRunnerError::FailedToEnsurePostState( + execution_report.clone(), + error_reason, + )); + } + } else { + let error_reason = "No post-state root hash provided"; + return Err(EFTestRunnerError::FailedToEnsurePostState( + execution_report.clone(), + error_reason.to_owned(), + )); + } + } + } + } + Err(err) => { + match test.post.vector_post_value(vector).expect_exception { + // Execution result was unsuccessful and an exception was expected. + // TODO: Check that the exception matches the expected exception. + Some(_expected_exception) => {} + // Execution result was unsuccessful but no exception was expected. + None => { + return Err(EFTestRunnerError::ExecutionFailedUnexpectedly(err.clone())); + } + } + } + }; + Ok(()) +} + +pub fn get_state_transitions(execution_report: &TransactionReport) -> Vec { + let mut account_updates: Vec = vec![]; + for (address, account) in &execution_report.new_state { + let mut added_storage = HashMap::new(); + + for (key, value) in &account.storage { + added_storage.insert(*key, value.current_value); + } + + let code = if account.info.bytecode.is_empty() { + None + } else { + Some(account.info.bytecode.clone()) + }; + + let account_update = AccountUpdate { + address: *address, + removed: false, + info: Some(AccountInfo { + code_hash: code_hash(&account.info.bytecode), + balance: account.info.balance, + nonce: account.info.nonce, + }), + code, + added_storage, + }; + + account_updates.push(account_update); + } + account_updates +} + +pub fn post_state_root(account_updates: &[AccountUpdate], test: &EFTest) -> H256 { + let (initial_state, block_hash) = utils::load_initial_state(test); + initial_state + .database() + .unwrap() + .apply_account_updates(block_hash, account_updates) + .unwrap() + .unwrap() +} diff --git a/cmd/ef_tests/levm/runner/mod.rs b/cmd/ef_tests/levm/runner/mod.rs new file mode 100644 index 000000000..63217ea9f --- /dev/null +++ b/cmd/ef_tests/levm/runner/mod.rs @@ -0,0 +1,99 @@ +use crate::{ + report::{self, format_duration_as_mm_ss}, + types::EFTest, +}; +use clap::Parser; +use colored::Colorize; +use ethereum_rust_levm::errors::{TransactionReport, VMError}; +use ethereum_rust_vm::SpecId; +use spinoff::{spinners::Dots, Color, Spinner}; + +pub mod levm_runner; +pub mod revm_runner; + +#[derive(Debug, thiserror::Error, Clone)] +pub enum EFTestRunnerError { + #[error("VM initialization failed: {0}")] + VMInitializationFailed(String), + #[error("Transaction execution failed when it was not expected to fail: {0}")] + ExecutionFailedUnexpectedly(VMError), + #[error("Failed to ensure post-state: {0}")] + FailedToEnsurePreState(String), + #[error("Failed to ensure post-state: {1}")] + FailedToEnsurePostState(TransactionReport, String), + #[error("VM run mismatch: {0}")] + VMExecutionMismatch(String), + #[error("This is a bug: {0}")] + Internal(String), +} + +#[derive(Parser)] +pub struct EFTestRunnerOptions { + #[arg(short, long, value_name = "FORK", default_value = "Cancun")] + pub fork: Vec, + #[arg(short, long, value_name = "TESTS")] + pub tests: Vec, +} + +pub fn run_ef_tests(ef_tests: Vec) -> Result<(), EFTestRunnerError> { + let mut reports = Vec::new(); + // Run the tests with LEVM. + let levm_run_time = std::time::Instant::now(); + let mut levm_run_spinner = Spinner::new( + Dots, + report::progress(&reports, levm_run_time.elapsed()), + Color::Cyan, + ); + for test in ef_tests.iter() { + let ef_test_report = match levm_runner::run_ef_test(test) { + Ok(ef_test_report) => ef_test_report, + Err(EFTestRunnerError::Internal(err)) => return Err(EFTestRunnerError::Internal(err)), + non_internal_errors => { + return Err(EFTestRunnerError::Internal(format!( + "Non-internal error raised when executing levm. This should not happen: {non_internal_errors:?}", + ))) + } + }; + reports.push(ef_test_report); + levm_run_spinner.update_text(report::progress(&reports, levm_run_time.elapsed())); + } + levm_run_spinner.success(&report::progress(&reports, levm_run_time.elapsed())); + + let mut summary_spinner = Spinner::new(Dots, "Loading summary...".to_owned(), Color::Cyan); + summary_spinner.success(&report::summary(&reports)); + + // Run the failed tests with REVM + let revm_run_time = std::time::Instant::now(); + let mut revm_run_spinner = Spinner::new( + Dots, + "Running failed tests with REVM...".to_owned(), + Color::Cyan, + ); + let failed_tests = reports.iter().filter(|report| !report.passed()).count(); + for (idx, failed_test_report) in reports.iter_mut().enumerate() { + if failed_test_report.passed() { + continue; + } + revm_run_spinner.update_text(format!( + "{} {}/{failed_tests} - {}", + "Re-running failed tests with REVM".bold(), + idx + 1, + format_duration_as_mm_ss(revm_run_time.elapsed()) + )); + let re_run_report = revm_runner::re_run_failed_ef_test( + ef_tests + .iter() + .find(|test| test.name == failed_test_report.name) + .unwrap(), + failed_test_report, + )?; + failed_test_report.register_re_run_report(re_run_report.clone()); + } + + let mut report_spinner = Spinner::new(Dots, "Loading report...".to_owned(), Color::Cyan); + // Write report in .txt file + let report_file_path = report::write(reports)?; + report_spinner.success(&format!("Report written to file {report_file_path:?}")); + + Ok(()) +} diff --git a/cmd/ef_tests/levm/runner/revm_runner.rs b/cmd/ef_tests/levm/runner/revm_runner.rs new file mode 100644 index 000000000..dc09a372c --- /dev/null +++ b/cmd/ef_tests/levm/runner/revm_runner.rs @@ -0,0 +1,272 @@ +use crate::{ + report::{AccountUpdatesReport, EFTestReport, TestReRunReport, TestVector}, + runner::{levm_runner, EFTestRunnerError}, + types::EFTest, + utils::load_initial_state, +}; +use ethereum_rust_core::{types::TxKind, Address}; +use ethereum_rust_levm::errors::{TransactionReport, TxResult}; +use ethereum_rust_storage::{error::StoreError, AccountUpdate}; +use ethereum_rust_vm::{db::StoreWrapper, spec_id, EvmState, RevmAddress, RevmU256}; +use revm::{ + db::State, + inspectors::TracerEip3155 as RevmTracerEip3155, + primitives::{ + BlobExcessGasAndPrice, BlockEnv as RevmBlockEnv, EVMError as REVMError, + ExecutionResult as RevmExecutionResult, TxEnv as RevmTxEnv, TxKind as RevmTxKind, + }, + Evm as Revm, +}; +use std::collections::HashSet; + +pub fn re_run_failed_ef_test( + test: &EFTest, + failed_test_report: &EFTestReport, +) -> Result { + let mut re_run_report = TestReRunReport::new(); + for (vector, _tx) in test.transactions.iter() { + let vector_failure = failed_test_report + .failed_vectors + .get(vector) + .cloned() + .ok_or(EFTestRunnerError::Internal( + "Failed vector not found in the failed test report".to_owned(), + ))?; + match vector_failure { + // We only want to re-run tests that failed in the post-state validation. + EFTestRunnerError::FailedToEnsurePostState(transaction_report, _) => { + match re_run_failed_ef_test_tx(vector, test, &transaction_report, &mut re_run_report) { + Ok(_) => continue, + Err(EFTestRunnerError::VMInitializationFailed(reason)) => { + return Err(EFTestRunnerError::Internal( + format!("REVM initialization failed when re-running failed test: {reason}"), + )); + } + Err(EFTestRunnerError::Internal(reason)) => { + return Err(EFTestRunnerError::Internal(reason)); + } + unexpected_error => { + return Err(EFTestRunnerError::Internal(format!( + "Unexpected error when re-running failed test: {unexpected_error:?}" + ))); + } + } + }, + EFTestRunnerError::VMInitializationFailed(_) + | EFTestRunnerError::ExecutionFailedUnexpectedly(_) + | EFTestRunnerError::FailedToEnsurePreState(_) => continue, + EFTestRunnerError::VMExecutionMismatch(reason) => return Err(EFTestRunnerError::Internal( + format!("VM execution mismatch errors should only happen when running with revm. This failed during levm's execution: {reason}") + )), + EFTestRunnerError::Internal(reason) => return Err(EFTestRunnerError::Internal(reason.to_owned())), + } + } + Ok(re_run_report) +} + +pub fn re_run_failed_ef_test_tx( + vector: &TestVector, + test: &EFTest, + levm_execution_report: &TransactionReport, + re_run_report: &mut TestReRunReport, +) -> Result<(), EFTestRunnerError> { + let (mut state, _block_hash) = load_initial_state(test); + let mut revm = prepare_revm_for_tx(&mut state, vector, test)?; + let revm_execution_result = revm.transact_commit(); + drop(revm); // Need to drop the state mutable reference. + compare_levm_revm_execution_results( + levm_execution_report, + revm_execution_result, + re_run_report, + )?; + ensure_post_state( + levm_execution_report, + vector, + &mut state, + test, + re_run_report, + )?; + Ok(()) +} + +pub fn prepare_revm_for_tx<'state>( + initial_state: &'state mut EvmState, + vector: &TestVector, + test: &EFTest, +) -> Result>, EFTestRunnerError> { + let chain_spec = initial_state + .chain_config() + .map_err(|err| EFTestRunnerError::VMInitializationFailed(err.to_string()))?; + let block_env = RevmBlockEnv { + number: RevmU256::from_limbs(test.env.current_number.0), + coinbase: RevmAddress(test.env.current_coinbase.0.into()), + timestamp: RevmU256::from_limbs(test.env.current_timestamp.0), + gas_limit: RevmU256::from_limbs(test.env.current_gas_limit.0), + basefee: RevmU256::from_limbs(test.env.current_base_fee.unwrap_or_default().0), + difficulty: RevmU256::from_limbs(test.env.current_difficulty.0), + prevrandao: test.env.current_random.map(|v| v.0.into()), + blob_excess_gas_and_price: Some(BlobExcessGasAndPrice::new( + test.env + .current_excess_blob_gas + .unwrap_or_default() + .as_u64(), + )), + }; + let tx = &test.transactions.get(vector).unwrap(); + let tx_env = RevmTxEnv { + caller: tx.sender.0.into(), + gas_limit: tx.gas_limit.as_u64(), + gas_price: RevmU256::from_limbs(tx.gas_price.unwrap_or_default().0), + transact_to: match tx.to { + TxKind::Call(to) => RevmTxKind::Call(to.0.into()), + TxKind::Create => RevmTxKind::Create, + }, + value: RevmU256::from_limbs(tx.value.0), + data: tx.data.to_vec().into(), + nonce: Some(tx.nonce.as_u64()), + chain_id: None, + access_list: Vec::default(), + gas_priority_fee: None, + blob_hashes: Vec::default(), + max_fee_per_blob_gas: None, + authorization_list: None, + }; + let evm_builder = Revm::builder() + .with_block_env(block_env) + .with_tx_env(tx_env) + .modify_cfg_env(|cfg| cfg.chain_id = chain_spec.chain_id) + .with_spec_id(spec_id(&chain_spec, test.env.current_timestamp.as_u64())) + .with_external_context( + RevmTracerEip3155::new(Box::new(std::io::stderr())).without_summary(), + ); + match initial_state { + EvmState::Store(db) => Ok(evm_builder.with_db(db).build()), + _ => Err(EFTestRunnerError::VMInitializationFailed( + "Expected LEVM state to be a Store".to_owned(), + )), + } +} + +pub fn compare_levm_revm_execution_results( + levm_execution_report: &TransactionReport, + revm_execution_result: Result>, + re_run_report: &mut TestReRunReport, +) -> Result<(), EFTestRunnerError> { + match (levm_execution_report, &revm_execution_result) { + (levm_tx_report, Ok(revm_execution_result)) => { + match (&levm_tx_report.result, revm_execution_result) { + ( + TxResult::Success, + RevmExecutionResult::Success { + reason: _, + gas_used: revm_gas_used, + gas_refunded: revm_gas_refunded, + logs: _, + output: _, + }, + ) => { + if levm_tx_report.gas_used != *revm_gas_used { + re_run_report + .register_gas_used_mismatch(levm_tx_report.gas_used, *revm_gas_used); + } + if levm_tx_report.gas_refunded != *revm_gas_refunded { + re_run_report.register_gas_refunded_mismatch( + levm_tx_report.gas_refunded, + *revm_gas_refunded, + ); + } + } + ( + TxResult::Revert(_error), + RevmExecutionResult::Revert { + gas_used: revm_gas_used, + output: _, + }, + ) => { + if &levm_tx_report.gas_used != revm_gas_used { + re_run_report + .register_gas_used_mismatch(levm_tx_report.gas_used, *revm_gas_used); + } + } + ( + TxResult::Revert(_error), + RevmExecutionResult::Halt { + reason: _, + gas_used: revm_gas_used, + }, + ) => { + // TODO: Register the revert reasons. + if &levm_tx_report.gas_used != revm_gas_used { + re_run_report + .register_gas_used_mismatch(levm_tx_report.gas_used, *revm_gas_used); + } + } + _ => { + re_run_report.register_execution_result_mismatch( + levm_tx_report.result.clone(), + revm_execution_result.clone(), + ); + } + } + } + (_levm_transaction_report, Err(_)) => { + return Err(EFTestRunnerError::Internal( + "REVM execution failed unexpectedly".to_owned(), + )); + } + } + Ok(()) +} + +pub fn ensure_post_state( + levm_execution_report: &TransactionReport, + vector: &TestVector, + revm_state: &mut EvmState, + test: &EFTest, + re_run_report: &mut TestReRunReport, +) -> Result<(), EFTestRunnerError> { + match test.post.vector_post_value(vector).expect_exception { + Some(_expected_exception) => {} + // We only want to compare account updates when no exception is expected. + None => { + let levm_account_updates = levm_runner::get_state_transitions(levm_execution_report); + let revm_account_updates = ethereum_rust_vm::get_state_transitions(revm_state); + let account_updates_report = + compare_levm_revm_account_updates(&levm_account_updates, &revm_account_updates); + re_run_report.register_account_updates_report(*vector, account_updates_report); + } + } + + Ok(()) +} + +pub fn compare_levm_revm_account_updates( + levm_account_updates: &[AccountUpdate], + revm_account_updates: &[AccountUpdate], +) -> AccountUpdatesReport { + let levm_updated_accounts = levm_account_updates + .iter() + .map(|account_update| account_update.address) + .collect::>(); + let revm_updated_accounts = revm_account_updates + .iter() + .map(|account_update| account_update.address) + .collect::>(); + + AccountUpdatesReport { + levm_account_updates: levm_account_updates.to_vec(), + revm_account_updates: revm_account_updates.to_vec(), + levm_updated_accounts_only: levm_updated_accounts + .difference(&revm_updated_accounts) + .cloned() + .collect::>(), + revm_updated_accounts_only: revm_updated_accounts + .difference(&levm_updated_accounts) + .cloned() + .collect::>(), + shared_updated_accounts: levm_updated_accounts + .intersection(&revm_updated_accounts) + .cloned() + .collect::>(), + } +} From 950f2aadb2424c702513aa640f274b2a7c82795d Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:02:13 -0300 Subject: [PATCH 03/49] Isolate EF test parsing --- cmd/ef_tests/levm/parser.rs | 99 +++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 cmd/ef_tests/levm/parser.rs diff --git a/cmd/ef_tests/levm/parser.rs b/cmd/ef_tests/levm/parser.rs new file mode 100644 index 000000000..491e82890 --- /dev/null +++ b/cmd/ef_tests/levm/parser.rs @@ -0,0 +1,99 @@ +use crate::{ + runner::EFTestRunnerOptions, + types::{EFTest, EFTests}, +}; +use spinoff::{spinners::Dots, Color, Spinner}; +use std::fs::DirEntry; + +#[derive(Debug, thiserror::Error)] +pub enum EFTestParseError { + #[error("Failed to read directory: {0}")] + FailedToReadDirectory(String), + #[error("Failed to read file: {0}")] + FailedToReadFile(String), + #[error("Failed to get file type: {0}")] + FailedToGetFileType(String), + #[error("Failed to parse test file: {0}")] + FailedToParseTestFile(String), +} + +pub fn parse_ef_tests(opts: &EFTestRunnerOptions) -> Result, EFTestParseError> { + let cargo_manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let ef_general_state_tests_path = cargo_manifest_dir.join("vectors/GeneralStateTests"); + let mut spinner = Spinner::new(Dots, "Parsing EF Tests", Color::Cyan); + let mut tests = Vec::new(); + for test_dir in std::fs::read_dir(ef_general_state_tests_path.clone()) + .map_err(|err| { + EFTestParseError::FailedToReadDirectory(format!( + "{:?}: {err}", + ef_general_state_tests_path.file_name() + )) + })? + .flatten() + { + let directory_tests = parse_ef_test_dir(test_dir, opts, &mut spinner)?; + tests.extend(directory_tests); + } + spinner.success("Parsed EF Tests"); + Ok(tests) +} + +pub fn parse_ef_test_dir( + test_dir: DirEntry, + opts: &EFTestRunnerOptions, + directory_parsing_spinner: &mut Spinner, +) -> Result, EFTestParseError> { + let mut directory_tests = Vec::new(); + for test in std::fs::read_dir(test_dir.path()) + .map_err(|err| { + EFTestParseError::FailedToReadDirectory(format!("{:?}: {err}", test_dir.file_name())) + })? + .flatten() + { + if test + .file_type() + .map_err(|err| { + EFTestParseError::FailedToGetFileType(format!("{:?}: {err}", test.file_name())) + })? + .is_dir() + { + let sub_directory_tests = parse_ef_test_dir(test, opts, directory_parsing_spinner)?; + directory_tests.extend(sub_directory_tests); + continue; + } + // Skip non-JSON files. + if test.path().extension().is_some_and(|ext| ext != "json") + | test.path().extension().is_none() + { + continue; + } + // Skip the ValueOverflowParis.json file. + if test + .path() + .file_name() + .is_some_and(|name| name == "ValueOverflowParis.json") + { + continue; + } + + // Skip tests that are not in the list of tests to run. + if !opts + .tests + .contains(&test_dir.file_name().to_str().unwrap().to_owned()) + { + continue; + } + + directory_parsing_spinner + .update_text(format!("Parsing directory {:?}", test_dir.file_name())); + + let test_file = std::fs::File::open(test.path()).map_err(|err| { + EFTestParseError::FailedToReadFile(format!("{:?}: {err}", test.path())) + })?; + let test: EFTests = serde_json::from_reader(test_file).map_err(|err| { + EFTestParseError::FailedToParseTestFile(format!("{:?} parse error: {err}", test.path())) + })?; + directory_tests.extend(test.0); + } + Ok(directory_tests) +} From 9eaab483c70b3011c192cfc9d3f147b8063dbca2 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:02:35 -0300 Subject: [PATCH 04/49] Fix initial state loading --- cmd/ef_tests/levm/utils.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ef_tests/levm/utils.rs b/cmd/ef_tests/levm/utils.rs index 512175697..3e4dbe329 100644 --- a/cmd/ef_tests/levm/utils.rs +++ b/cmd/ef_tests/levm/utils.rs @@ -9,10 +9,11 @@ pub fn load_initial_state(test: &EFTest) -> (EvmState, H256) { let storage = Store::new("./temp", EngineType::InMemory).expect("Failed to create Store"); storage.add_initial_state(genesis.clone()).unwrap(); - let parent_hash = genesis.get_block().header.parent_hash; - ( - evm_state(storage.clone(), parent_hash), + evm_state( + storage.clone(), + genesis.get_block().header.compute_block_hash(), + ), genesis.get_block().header.compute_block_hash(), ) } From cdc76d4b41c5986b1f63a834fc1ecfc0983eb1df Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:02:53 -0300 Subject: [PATCH 05/49] Fix types --- cmd/ef_tests/levm/types.rs | 111 ++++++++++++++++++++++++++++++++----- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/cmd/ef_tests/levm/types.rs b/cmd/ef_tests/levm/types.rs index d1403195f..ebca565db 100644 --- a/cmd/ef_tests/levm/types.rs +++ b/cmd/ef_tests/levm/types.rs @@ -1,16 +1,23 @@ -use crate::deserialize::{ - deserialize_ef_post_value_indexes, deserialize_hex_bytes, deserialize_hex_bytes_vec, - deserialize_u256_optional_safe, deserialize_u256_safe, deserialize_u256_valued_hashmap_safe, - deserialize_u256_vec_safe, +use crate::{ + deserialize::{ + deserialize_ef_post_value_indexes, deserialize_hex_bytes, deserialize_hex_bytes_vec, + deserialize_u256_optional_safe, deserialize_u256_safe, + deserialize_u256_valued_hashmap_safe, deserialize_u256_vec_safe, + }, + report::TestVector, }; use bytes::Bytes; use ethereum_rust_core::{ types::{Genesis, GenesisAccount, TxKind}, Address, H256, U256, }; +use ethereum_rust_vm::SpecId; use serde::Deserialize; use std::collections::HashMap; +#[derive(Debug)] +pub struct EFTests(pub Vec); + #[derive(Debug)] pub struct EFTest { pub name: String, @@ -18,7 +25,26 @@ pub struct EFTest { pub env: EFTestEnv, pub post: EFTestPost, pub pre: EFTestPre, - pub transactions: Vec<((usize, usize, usize), EFTestTransaction)>, + pub transactions: HashMap, +} + +impl EFTest { + pub fn fork(&self) -> SpecId { + match &self.post { + EFTestPost::Cancun(_) => SpecId::CANCUN, + EFTestPost::Shanghai(_) => SpecId::SHANGHAI, + EFTestPost::Homestead(_) => SpecId::HOMESTEAD, + EFTestPost::Istanbul(_) => SpecId::ISTANBUL, + EFTestPost::London(_) => SpecId::LONDON, + EFTestPost::Byzantium(_) => SpecId::BYZANTIUM, + EFTestPost::Berlin(_) => SpecId::BERLIN, + EFTestPost::Constantinople(_) | EFTestPost::ConstantinopleFix(_) => { + SpecId::CONSTANTINOPLE + } + EFTestPost::Paris(_) => SpecId::MERGE, + EFTestPost::Frontier(_) => SpecId::FRONTIER, + } + } } impl From<&EFTest> for Genesis { @@ -34,10 +60,10 @@ impl From<&EFTest> for Genesis { coinbase: test.env.current_coinbase, difficulty: test.env.current_difficulty, gas_limit: test.env.current_gas_limit.as_u64(), - mix_hash: test.env.current_random, + mix_hash: test.env.current_random.unwrap_or_default(), timestamp: test.env.current_timestamp.as_u64(), - base_fee_per_gas: Some(test.env.current_base_fee.as_u64()), - excess_blob_gas: Some(test.env.current_excess_blob_gas.as_u64()), + base_fee_per_gas: test.env.current_base_fee.map(|v| v.as_u64()), + excess_blob_gas: test.env.current_excess_blob_gas.map(|v| v.as_u64()), ..Default::default() } } @@ -64,18 +90,18 @@ pub struct EFTestInfo { #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct EFTestEnv { - #[serde(deserialize_with = "deserialize_u256_safe")] - pub current_base_fee: U256, + #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] + pub current_base_fee: Option, pub current_coinbase: Address, #[serde(deserialize_with = "deserialize_u256_safe")] pub current_difficulty: U256, - #[serde(deserialize_with = "deserialize_u256_safe")] - pub current_excess_blob_gas: U256, + #[serde(default, deserialize_with = "deserialize_u256_optional_safe")] + pub current_excess_blob_gas: Option, #[serde(deserialize_with = "deserialize_u256_safe")] pub current_gas_limit: U256, #[serde(deserialize_with = "deserialize_u256_safe")] pub current_number: U256, - pub current_random: H256, + pub current_random: Option, #[serde(deserialize_with = "deserialize_u256_safe")] pub current_timestamp: U256, } @@ -83,18 +109,77 @@ pub struct EFTestEnv { #[derive(Debug, Deserialize, Clone)] pub enum EFTestPost { Cancun(Vec), + Shanghai(Vec), + Homestead(Vec), + Istanbul(Vec), + London(Vec), + Byzantium(Vec), + Berlin(Vec), + Constantinople(Vec), + Paris(Vec), + ConstantinopleFix(Vec), + Frontier(Vec), } impl EFTestPost { pub fn values(self) -> Vec { match self { EFTestPost::Cancun(v) => v, + EFTestPost::Shanghai(v) => v, + EFTestPost::Homestead(v) => v, + EFTestPost::Istanbul(v) => v, + EFTestPost::London(v) => v, + EFTestPost::Byzantium(v) => v, + EFTestPost::Berlin(v) => v, + EFTestPost::Constantinople(v) => v, + EFTestPost::Paris(v) => v, + EFTestPost::ConstantinopleFix(v) => v, + EFTestPost::Frontier(v) => v, + } + } + + pub fn vector_post_value(&self, vector: &TestVector) -> EFTestPostValue { + match self { + EFTestPost::Cancun(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Shanghai(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Homestead(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Istanbul(v) => Self::find_vector_post_value(v, vector), + EFTestPost::London(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Byzantium(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Berlin(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Constantinople(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Paris(v) => Self::find_vector_post_value(v, vector), + EFTestPost::ConstantinopleFix(v) => Self::find_vector_post_value(v, vector), + EFTestPost::Frontier(v) => Self::find_vector_post_value(v, vector), } } + fn find_vector_post_value(values: &[EFTestPostValue], vector: &TestVector) -> EFTestPostValue { + values + .iter() + .find(|v| { + let data_index = v.indexes.get("data").unwrap().as_usize(); + let gas_limit_index = v.indexes.get("gas").unwrap().as_usize(); + let value_index = v.indexes.get("value").unwrap().as_usize(); + vector == &(data_index, gas_limit_index, value_index) + }) + .unwrap() + .clone() + } + pub fn iter(&self) -> impl Iterator { match self { EFTestPost::Cancun(v) => v.iter(), + EFTestPost::Shanghai(v) => v.iter(), + EFTestPost::Homestead(v) => v.iter(), + EFTestPost::Istanbul(v) => v.iter(), + EFTestPost::London(v) => v.iter(), + EFTestPost::Byzantium(v) => v.iter(), + EFTestPost::Berlin(v) => v.iter(), + EFTestPost::Constantinople(v) => v.iter(), + EFTestPost::Paris(v) => v.iter(), + EFTestPost::ConstantinopleFix(v) => v.iter(), + EFTestPost::Frontier(v) => v.iter(), } } } From e389c73bb373c9ce68727c7dc4a219f03c743a47 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:03:02 -0300 Subject: [PATCH 06/49] Fix deserialization --- cmd/ef_tests/levm/deserialize.rs | 171 ++++++++++++++++--------------- 1 file changed, 86 insertions(+), 85 deletions(-) diff --git a/cmd/ef_tests/levm/deserialize.rs b/cmd/ef_tests/levm/deserialize.rs index 1fd978a4f..81590178f 100644 --- a/cmd/ef_tests/levm/deserialize.rs +++ b/cmd/ef_tests/levm/deserialize.rs @@ -1,4 +1,4 @@ -use crate::types::EFTest; +use crate::types::{EFTest, EFTests}; use bytes::Bytes; use ethereum_rust_core::U256; use serde::Deserialize; @@ -122,103 +122,104 @@ where .collect() } -impl<'de> Deserialize<'de> for EFTest { +impl<'de> Deserialize<'de> for EFTests { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { + let mut ef_tests = Vec::new(); let aux: HashMap> = HashMap::deserialize(deserializer)?; - let test_name = aux - .keys() - .next() - .ok_or(serde::de::Error::missing_field("test name"))?; - let test_data = aux - .get(test_name) - .ok_or(serde::de::Error::missing_field("test data value"))?; - let raw_tx: EFTestRawTransaction = serde_json::from_value( - test_data - .get("transaction") - .ok_or(serde::de::Error::missing_field("transaction"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"transaction\" field: {err}" - )) - })?; - - let mut transactions = Vec::new(); + for test_name in aux.keys() { + let test_data = aux + .get(test_name) + .ok_or(serde::de::Error::missing_field("test data value"))?; - // Note that inthis order of iteration, in an example tx with 2 datas, 2 gasLimit and 2 values, order would be - // 111, 112, 121, 122, 211, 212, 221, 222 - for (data_id, data) in raw_tx.data.iter().enumerate() { - for (gas_limit_id, gas_limit) in raw_tx.gas_limit.iter().enumerate() { - for (value_id, value) in raw_tx.value.iter().enumerate() { - let tx = EFTestTransaction { - data: data.clone(), - gas_limit: *gas_limit, - gas_price: raw_tx.gas_price, - nonce: raw_tx.nonce, - secret_key: raw_tx.secret_key, - sender: raw_tx.sender, - to: raw_tx.to.clone(), - value: *value, - }; - transactions.push(((data_id, gas_limit_id, value_id), tx)); - } - } - } - - let ef_test = Self { - name: test_name.to_owned().to_owned(), - _info: serde_json::from_value( - test_data - .get("_info") - .ok_or(serde::de::Error::missing_field("_info"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"_info\" field: {err}" - )) - })?, - env: serde_json::from_value( - test_data - .get("env") - .ok_or(serde::de::Error::missing_field("env"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"env\" field: {err}" - )) - })?, - post: serde_json::from_value( - test_data - .get("post") - .ok_or(serde::de::Error::missing_field("post"))? - .clone(), - ) - .map_err(|err| { - serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"post\" field: {err}" - )) - })?, - pre: serde_json::from_value( + let raw_tx: EFTestRawTransaction = serde_json::from_value( test_data - .get("pre") - .ok_or(serde::de::Error::missing_field("pre"))? + .get("transaction") + .ok_or(serde::de::Error::missing_field("transaction"))? .clone(), ) .map_err(|err| { serde::de::Error::custom(format!( - "error deserializing test \"{test_name}\", \"pre\" field: {err}" + "error deserializing test \"{test_name}\", \"transaction\" field: {err}" )) - })?, - transactions, - }; - Ok(ef_test) + })?; + + let mut transactions = HashMap::new(); + + // Note that inthis order of iteration, in an example tx with 2 datas, 2 gasLimit and 2 values, order would be + // 111, 112, 121, 122, 211, 212, 221, 222 + for (data_id, data) in raw_tx.data.iter().enumerate() { + for (gas_limit_id, gas_limit) in raw_tx.gas_limit.iter().enumerate() { + for (value_id, value) in raw_tx.value.iter().enumerate() { + let tx = EFTestTransaction { + data: data.clone(), + gas_limit: *gas_limit, + gas_price: raw_tx.gas_price, + nonce: raw_tx.nonce, + secret_key: raw_tx.secret_key, + sender: raw_tx.sender, + to: raw_tx.to.clone(), + value: *value, + }; + transactions.insert((data_id, gas_limit_id, value_id), tx); + } + } + } + + let ef_test = EFTest { + name: test_name.to_owned().to_owned(), + _info: serde_json::from_value( + test_data + .get("_info") + .ok_or(serde::de::Error::missing_field("_info"))? + .clone(), + ) + .map_err(|err| { + serde::de::Error::custom(format!( + "error deserializing test \"{test_name}\", \"_info\" field: {err}" + )) + })?, + env: serde_json::from_value( + test_data + .get("env") + .ok_or(serde::de::Error::missing_field("env"))? + .clone(), + ) + .map_err(|err| { + serde::de::Error::custom(format!( + "error deserializing test \"{test_name}\", \"env\" field: {err}" + )) + })?, + post: serde_json::from_value( + test_data + .get("post") + .ok_or(serde::de::Error::missing_field("post"))? + .clone(), + ) + .map_err(|err| { + serde::de::Error::custom(format!( + "error deserializing test \"{test_name}\", \"post\" field: {err}" + )) + })?, + pre: serde_json::from_value( + test_data + .get("pre") + .ok_or(serde::de::Error::missing_field("pre"))? + .clone(), + ) + .map_err(|err| { + serde::de::Error::custom(format!( + "error deserializing test \"{test_name}\", \"pre\" field: {err}" + )) + })?, + transactions, + }; + ef_tests.push(ef_test); + } + Ok(Self(ef_tests)) } } From a28aa00df98058d452418225a0cdc524829f1c39 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:03:18 -0300 Subject: [PATCH 07/49] Refactor report module --- cmd/ef_tests/levm/report.rs | 474 ++++++++++++++++++++++++++++++------ 1 file changed, 404 insertions(+), 70 deletions(-) diff --git a/cmd/ef_tests/levm/report.rs b/cmd/ef_tests/levm/report.rs index 012af8b01..513c2c363 100644 --- a/cmd/ef_tests/levm/report.rs +++ b/cmd/ef_tests/levm/report.rs @@ -1,101 +1,435 @@ -// Note: I use this to do not affect the EF tests logic with this side effects -// The cost to add this would be to return a Result<(), InternalError> in EFTestsReport methods - +use crate::runner::EFTestRunnerError; use colored::Colorize; -use std::{collections::HashMap, fmt}; - -#[derive(Debug, Default)] -pub struct EFTestsReport { - group_passed: u64, - group_failed: u64, - group_run: u64, - test_reports: HashMap, - passed_tests: Vec, - failed_tests: Vec<(String, (usize, usize, usize), String)>, +use ethereum_rust_core::Address; +use ethereum_rust_levm::errors::{TransactionReport, TxResult, VMError}; +use ethereum_rust_storage::AccountUpdate; +use ethereum_rust_vm::SpecId; +use revm::primitives::{ExecutionResult as RevmExecutionResult, HashSet}; +use std::{ + collections::HashMap, + fmt::{self, Display}, + path::PathBuf, + time::Duration, +}; + +pub type TestVector = (usize, usize, usize); + +pub fn progress(reports: &[EFTestReport], time: Duration) -> String { + format!( + "{}: {} {} {} - {}", + "Ethereum Foundation Tests".bold(), + format!( + "{} passed", + reports.iter().filter(|report| report.passed()).count() + ) + .green() + .bold(), + format!( + "{} failed", + reports.iter().filter(|report| !report.passed()).count() + ) + .red() + .bold(), + format!("{} total run", reports.len()).blue().bold(), + format_duration_as_mm_ss(time) + ) +} +pub fn summary(reports: &[EFTestReport]) -> String { + let total_passed = reports.iter().filter(|report| report.passed()).count(); + let total_run = reports.len(); + format!( + "{} {}/{total_run}\n\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n{}\n", + "Summary:".bold(), + if total_passed == total_run { + format!("{}", total_passed).green() + } else if total_passed > 0 { + format!("{}", total_passed).yellow() + } else { + format!("{}", total_passed).red() + }, + fork_summary(reports, SpecId::CANCUN), + fork_summary(reports, SpecId::SHANGHAI), + fork_summary(reports, SpecId::HOMESTEAD), + fork_summary(reports, SpecId::ISTANBUL), + fork_summary(reports, SpecId::LONDON), + fork_summary(reports, SpecId::BYZANTIUM), + fork_summary(reports, SpecId::BERLIN), + fork_summary(reports, SpecId::CONSTANTINOPLE), + fork_summary(reports, SpecId::MERGE), + fork_summary(reports, SpecId::FRONTIER), + ) +} + +pub fn write(reports: Vec) -> Result { + let report_file_path = PathBuf::from("./levm_ef_tests_report.txt"); + let failed_test_reports = EFTestsReport( + reports + .into_iter() + .filter(|report| !report.passed()) + .collect(), + ); + std::fs::write( + "./levm_ef_tests_report.txt", + failed_test_reports.to_string(), + ) + .map_err(|err| EFTestRunnerError::Internal(format!("Failed to write report to file: {err}")))?; + Ok(report_file_path) +} + +pub fn format_duration_as_mm_ss(duration: Duration) -> String { + let total_seconds = duration.as_secs(); + let minutes = total_seconds / 60; + let seconds = total_seconds % 60; + format!("{minutes:02}:{seconds:02}") +} + +#[derive(Debug, Default, Clone)] +pub struct EFTestsReport(pub Vec); + +impl Display for EFTestsReport { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let total_passed = self.0.iter().filter(|report| report.passed()).count(); + let total_run = self.0.len(); + writeln!( + f, + "{} {}/{total_run}", + "Summary:".bold(), + if total_passed == total_run { + format!("{}", total_passed).green() + } else if total_passed > 0 { + format!("{}", total_passed).yellow() + } else { + format!("{}", total_passed).red() + }, + )?; + writeln!(f)?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::CANCUN))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::SHANGHAI))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::HOMESTEAD))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::ISTANBUL))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::LONDON))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::BYZANTIUM))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::BERLIN))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::CONSTANTINOPLE))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::MERGE))?; + writeln!(f, "{}", fork_summary(&self.0, SpecId::FRONTIER))?; + writeln!(f)?; + writeln!(f, "{}", "Failed tests:".bold())?; + writeln!(f)?; + for report in self.0.iter() { + if report.failed_vectors.is_empty() { + continue; + } + writeln!(f, "{}", report.name.bold())?; + writeln!(f)?; + for (failed_vector, error) in &report.failed_vectors { + writeln!( + f, + "{} (data_index: {}, gas_limit_index: {}, value_index: {})", + "Vector:".bold(), + failed_vector.0, + failed_vector.1, + failed_vector.2 + )?; + writeln!(f, "{} {}", "Error:".bold(), error.to_string().red())?; + if let Some(re_run_report) = &report.re_run_report { + if let Some(account_update) = + re_run_report.account_updates_report.get(failed_vector) + { + writeln!(f, "{}", &account_update.to_string())?; + } else { + writeln!(f, "No account updates report found. Account update reports are only generated for tests that failed at the post-state validation stage.")?; + } + } else { + writeln!(f, "No re-run report found. Re-run reports are only generated for tests that failed at the post-state validation stage.")?; + } + writeln!(f)?; + } + } + Ok(()) + } +} + +fn fork_summary(reports: &[EFTestReport], fork: SpecId) -> String { + let fork_str: &str = fork.into(); + let fork_tests = reports.iter().filter(|report| report.fork == fork).count(); + let fork_passed_tests = reports + .iter() + .filter(|report| report.fork == fork && report.passed()) + .count(); + format!( + "{}: {}/{fork_tests}", + fork_str.bold(), + if fork_passed_tests == fork_tests { + format!("{}", fork_passed_tests).green() + } else if fork_passed_tests > 0 { + format!("{}", fork_passed_tests).yellow() + } else { + format!("{}", fork_passed_tests).red() + }, + ) } #[derive(Debug, Default, Clone)] pub struct EFTestReport { - passed: u64, - failed: u64, - run: u64, - failed_tests: Vec<((usize, usize, usize), String)>, + pub name: String, + pub fork: SpecId, + pub skipped: bool, + pub failed_vectors: HashMap, + pub re_run_report: Option, } -impl EFTestsReport { - pub fn register_pass(&mut self, test_name: &str) { - self.passed_tests.push(test_name.to_string()); +impl EFTestReport { + pub fn new(name: String, fork: SpecId) -> Self { + EFTestReport { + name, + fork, + ..Default::default() + } + } + + pub fn new_skipped() -> Self { + EFTestReport { + skipped: true, + ..Default::default() + } + } - let report = self.test_reports.entry(test_name.to_string()).or_default(); - report.passed += 1; - report.run += 1; + pub fn register_unexpected_execution_failure( + &mut self, + error: VMError, + failed_vector: TestVector, + ) { + self.failed_vectors.insert( + failed_vector, + EFTestRunnerError::ExecutionFailedUnexpectedly(error), + ); } - pub fn register_fail( + pub fn register_vm_initialization_failure( &mut self, - tx_indexes: (usize, usize, usize), - test_name: &str, - reason: &str, + reason: String, + failed_vector: TestVector, ) { - self.failed_tests - .push((test_name.to_owned(), tx_indexes, reason.to_owned())); + self.failed_vectors.insert( + failed_vector, + EFTestRunnerError::VMInitializationFailed(reason), + ); + } - let report = self.test_reports.entry(test_name.to_string()).or_default(); - report.failed += 1; - report.failed_tests.push((tx_indexes, reason.to_owned())); - report.run += 1; + pub fn register_pre_state_validation_failure( + &mut self, + reason: String, + failed_vector: TestVector, + ) { + self.failed_vectors.insert( + failed_vector, + EFTestRunnerError::FailedToEnsurePreState(reason), + ); } - pub fn register_group_pass(&mut self) { - self.group_passed += 1; - self.group_run += 1; + pub fn register_post_state_validation_failure( + &mut self, + transaction_report: TransactionReport, + reason: String, + failed_vector: TestVector, + ) { + self.failed_vectors.insert( + failed_vector, + EFTestRunnerError::FailedToEnsurePostState(transaction_report, reason), + ); } - pub fn register_group_fail(&mut self) { - self.group_failed += 1; - self.group_run += 1; + pub fn register_re_run_report(&mut self, re_run_report: TestReRunReport) { + self.re_run_report = Some(re_run_report); } - pub fn progress(&self) -> String { - format!( - "{}: {} {} {}", - "Ethereum Foundation Tests Run".bold(), - format!("{} passed", self.group_passed).green().bold(), - format!("{} failed", self.group_failed).red().bold(), - format!("{} total run", self.group_run).blue().bold(), - ) + pub fn iter_failed(&self) -> impl Iterator { + self.failed_vectors.iter() } + + pub fn passed(&self) -> bool { + self.failed_vectors.is_empty() + } +} + +#[derive(Debug, Default, Clone)] +pub struct AccountUpdatesReport { + pub levm_account_updates: Vec, + pub revm_account_updates: Vec, + pub levm_updated_accounts_only: HashSet
, + pub revm_updated_accounts_only: HashSet
, + pub shared_updated_accounts: HashSet
, } -impl fmt::Display for EFTestsReport { +impl fmt::Display for AccountUpdatesReport { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - writeln!(f, "Report:")?; - writeln!(f, "Total results: {}", self.progress())?; - for (test_name, report) in self.test_reports.clone() { - if report.failed == 0 { - continue; - } - writeln!(f)?; + writeln!(f, "Account Updates:")?; + for levm_updated_account_only in self.levm_updated_accounts_only.iter() { + writeln!(f, " {levm_updated_account_only:#x}:")?; + writeln!(f, "{}", " Was updated in LEVM but not in REVM".red())?; + } + for revm_updated_account_only in self.revm_updated_accounts_only.iter() { + writeln!(f, " {revm_updated_account_only:#x}:")?; + writeln!(f, "{}", " Was updated in REVM but not in LEVM".red())?; + } + for shared_updated_account in self.shared_updated_accounts.iter() { + writeln!(f, " {shared_updated_account:#x}:")?; + writeln!( f, - "Test results for {}: {} {} {}", - test_name, - format!("{} passed", report.passed).green().bold(), - format!("{} failed", report.failed).red().bold(), - format!("{} run", report.run).blue().bold(), + "{}", + " Was updated in both LEVM and REVM".to_string().green() )?; - for failing_test in report.failed_tests.clone() { - writeln!( - f, - "(data_index: {}, gas_limit_index: {}, value_index: {}). Err: {}", - failing_test.0 .0, - failing_test.0 .1, - failing_test.0 .2, - failing_test.1.bright_red().bold() - )?; + + let levm_updated_account = self + .levm_account_updates + .iter() + .find(|account_update| &account_update.address == shared_updated_account) + .unwrap(); + let revm_updated_account = self + .revm_account_updates + .iter() + .find(|account_update| &account_update.address == shared_updated_account) + .unwrap(); + + match (levm_updated_account.removed, revm_updated_account.removed) { + (true, false) => { + writeln!( + f, + "{}", + " Removed in LEVM but not in REVM".to_string().red() + )?; + } + (false, true) => { + writeln!( + f, + "{}", + " Removed in REVM but not in LEVM".to_string().red() + )?; + } + // Account was removed in both VMs. + (false, false) | (true, true) => {} } - } + match (&levm_updated_account.code, &revm_updated_account.code) { + (None, Some(_)) => { + writeln!( + f, + "{}", + " Has code in REVM but not in LEVM".to_string().red() + )?; + } + (Some(_), None) => { + writeln!( + f, + "{}", + " Has code in LEVM but not in REVM".to_string().red() + )?; + } + (Some(levm_account_code), Some(revm_account_code)) => { + if levm_account_code != revm_account_code { + writeln!(f, + "{}", + format!( + " Code mismatch: LEVM: {levm_account_code}, REVM: {revm_account_code}", + levm_account_code = hex::encode(levm_account_code), + revm_account_code = hex::encode(revm_account_code) + ) + .red() + )?; + } + } + (None, None) => {} + } + + match (&levm_updated_account.info, &revm_updated_account.info) { + (None, Some(_)) => { + writeln!( + f, + "{}", + format!(" Account {shared_updated_account:#x} has info in REVM but not in LEVM",) + .red() + .bold() + )?; + } + (Some(levm_account_info), Some(revm_account_info)) => { + if levm_account_info.balance != revm_account_info.balance { + writeln!(f, + "{}", + format!( + " Balance mismatch: LEVM: {levm_account_balance}, REVM: {revm_account_balance}", + levm_account_balance = levm_account_info.balance, + revm_account_balance = revm_account_info.balance + ) + .red() + .bold() + )?; + } + if levm_account_info.nonce != revm_account_info.nonce { + writeln!(f, + "{}", + format!( + " Nonce mismatch: LEVM: {levm_account_nonce}, REVM: {revm_account_nonce}", + levm_account_nonce = levm_account_info.nonce, + revm_account_nonce = revm_account_info.nonce + ) + .red() + .bold() + )?; + } + } + // We ignore the case (Some(_), None) because we always add the account info to the account update. + (Some(_), None) | (None, None) => {} + } + } Ok(()) } } + +#[derive(Debug, Default, Clone)] +pub struct TestReRunExecutionReport { + pub execution_result_mismatch: Option<(TxResult, RevmExecutionResult)>, + pub gas_used_mismatch: Option<(u64, u64)>, + pub gas_refunded_mismatch: Option<(u64, u64)>, +} + +#[derive(Debug, Default, Clone)] +pub struct TestReRunReport { + pub execution_report: TestReRunExecutionReport, + pub account_updates_report: HashMap, +} + +impl TestReRunReport { + pub fn new() -> Self { + Self::default() + } + + pub fn register_execution_result_mismatch( + &mut self, + levm_result: TxResult, + revm_result: RevmExecutionResult, + ) { + self.execution_report.execution_result_mismatch = Some((levm_result, revm_result)); + } + + pub fn register_gas_used_mismatch(&mut self, levm_gas_used: u64, revm_gas_used: u64) { + self.execution_report.gas_used_mismatch = Some((levm_gas_used, revm_gas_used)); + } + + pub fn register_gas_refunded_mismatch( + &mut self, + levm_gas_refunded: u64, + revm_gas_refunded: u64, + ) { + self.execution_report.gas_refunded_mismatch = Some((levm_gas_refunded, revm_gas_refunded)); + } + + pub fn register_account_updates_report( + &mut self, + vector: TestVector, + report: AccountUpdatesReport, + ) { + self.account_updates_report.insert(vector, report); + } +} From f72bd3f8d4d94517ef61ddb06d1d6a6adad368b8 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:03:32 -0300 Subject: [PATCH 08/49] Rename test file and modules --- cmd/ef_tests/levm/Cargo.toml | 13 ++++++++++++- cmd/ef_tests/levm/ef_tests.rs | 3 ++- cmd/ef_tests/levm/tests/ef_tests_levm.rs | 13 +++++++++++++ cmd/ef_tests/levm/tests/test.rs | 6 ------ 4 files changed, 27 insertions(+), 8 deletions(-) create mode 100644 cmd/ef_tests/levm/tests/ef_tests_levm.rs delete mode 100644 cmd/ef_tests/levm/tests/test.rs diff --git a/cmd/ef_tests/levm/Cargo.toml b/cmd/ef_tests/levm/Cargo.toml index 92e205352..5e038b111 100644 --- a/cmd/ef_tests/levm/Cargo.toml +++ b/cmd/ef_tests/levm/Cargo.toml @@ -17,6 +17,17 @@ hex.workspace = true keccak-hash = "0.11.0" colored = "2.1.0" spinoff = "0.8.0" +thiserror = "2.0.3" +clap = { version = "4.3", features = ["derive"] } +clap_complete = "4.5.17" + +revm = { version = "14.0.3", features = [ + "serde", + "std", + "serde-json", + "optional_no_base_fee", + "optional_block_gas_limit", +], default-features = false } [dev-dependencies] hex = "0.4.3" @@ -25,5 +36,5 @@ hex = "0.4.3" path = "./ef_tests.rs" [[test]] -name = "test" +name = "ef_tests_levm" harness = false diff --git a/cmd/ef_tests/levm/ef_tests.rs b/cmd/ef_tests/levm/ef_tests.rs index 74e2ea7d5..27e09b709 100644 --- a/cmd/ef_tests/levm/ef_tests.rs +++ b/cmd/ef_tests/levm/ef_tests.rs @@ -1,5 +1,6 @@ mod deserialize; +pub mod parser; mod report; pub mod runner; -mod types; +pub mod types; mod utils; diff --git a/cmd/ef_tests/levm/tests/ef_tests_levm.rs b/cmd/ef_tests/levm/tests/ef_tests_levm.rs new file mode 100644 index 000000000..699a567cf --- /dev/null +++ b/cmd/ef_tests/levm/tests/ef_tests_levm.rs @@ -0,0 +1,13 @@ +use clap::Parser; +use ef_tests_levm::{ + parser, + runner::{self, EFTestRunnerOptions}, +}; +use std::error::Error; + +fn main() -> Result<(), Box> { + let opts = EFTestRunnerOptions::parse(); + let ef_tests = parser::parse_ef_tests(&opts)?; + runner::run_ef_tests(ef_tests)?; + Ok(()) +} diff --git a/cmd/ef_tests/levm/tests/test.rs b/cmd/ef_tests/levm/tests/test.rs deleted file mode 100644 index e726edb91..000000000 --- a/cmd/ef_tests/levm/tests/test.rs +++ /dev/null @@ -1,6 +0,0 @@ -use ef_tests_levm::runner; - -fn main() { - let report = runner::run_ef_tests().unwrap(); - println!("{report}"); -} From 798ca198eb37d73f717dde8f5351d942dd6a51c4 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:03:45 -0300 Subject: [PATCH 09/49] Ignore report file --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index cbdf31f53..b595512e4 100644 --- a/.gitignore +++ b/.gitignore @@ -46,3 +46,5 @@ jwt.hex tests_v3.0.0.tar.gz .env + +levm_ef_tests_report.txt From 0df3e415121fb8455f393ce354af5a11dc5b81ab Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:03:49 -0300 Subject: [PATCH 10/49] Chore --- crates/storage/store/storage.rs | 2 +- crates/vm/vm.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index 862763b3f..6e440ae17 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -39,7 +39,7 @@ pub enum EngineType { Libmdbx, } -#[derive(Default, Debug)] +#[derive(Default, Debug, Clone)] pub struct AccountUpdate { pub address: Address, pub removed: bool, diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index fde940ad4..0733b095d 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -23,7 +23,7 @@ use revm::{ inspector_handle_register, inspectors::TracerEip3155, precompile::{PrecompileSpecId, Precompiles}, - primitives::{BlobExcessGasAndPrice, BlockEnv, TxEnv, B256, U256 as RevmU256}, + primitives::{BlobExcessGasAndPrice, BlockEnv, TxEnv, B256}, Database, DatabaseCommit, Evm, }; use revm_inspectors::access_list::AccessListInspector; @@ -35,7 +35,7 @@ use revm_primitives::{ // Export needed types pub use errors::EvmError; pub use execution_result::*; -pub use revm::primitives::{Address as RevmAddress, SpecId}; +pub use revm::primitives::{Address as RevmAddress, SpecId, U256 as RevmU256}; type AccessList = Vec<(Address, Vec)>; From 1391e03ad0dd6b979acc5b8e82342f11bf1041eb Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:06:48 -0300 Subject: [PATCH 11/49] Merge main --- .github/workflows/ci.yaml | 2 +- .github/workflows/docker-build.yaml | 30 + .github/workflows/hive_and_assertoor.yaml | 41 +- .github/workflows/hive_coverage.yaml | 51 ++ .github/workflows/l2_prover_ci.yaml | 11 +- .github/workflows/loc.yaml | 29 + .gitignore | 4 +- Cargo.toml | 35 +- Dockerfile | 9 +- Makefile | 27 +- README.md | 141 +-- .../{ethereum_rust => ethrex}/Cargo.toml | 10 +- .../{ethereum_rust => ethrex}/ef_tests.rs | 0 .../{ethereum_rust => ethrex}/network.rs | 2 +- .../{ethereum_rust => ethrex}/test_runner.rs | 8 +- .../{ethereum_rust => ethrex}/tests/cancun.rs | 2 +- .../tests/shanghai.rs | 2 +- .../{ethereum_rust => ethrex}/types.rs | 38 +- cmd/ef_tests/levm/Cargo.toml | 12 +- cmd/ef_tests/levm/deserialize.rs | 2 +- cmd/ef_tests/levm/report.rs | 8 +- cmd/ef_tests/levm/runner/levm_runner.rs | 8 +- cmd/ef_tests/levm/runner/mod.rs | 4 +- cmd/ef_tests/levm/runner/revm_runner.rs | 10 +- cmd/ef_tests/levm/types.rs | 4 +- cmd/ef_tests/levm/utils.rs | 6 +- cmd/ethereum_rust/Cargo.toml | 44 - cmd/ethrex/Cargo.toml | 44 + cmd/{ethereum_rust => ethrex}/cli.rs | 12 +- cmd/{ethereum_rust => ethrex}/decode.rs | 6 +- .../ethereum_rust.rs => ethrex/ethrex.rs} | 136 ++- .../Cargo.toml | 14 +- cmd/{ethereum_rust_l2 => ethrex_l2}/README.md | 45 +- .../assets/config_create.cast.gif | Bin .../assets/config_delete.cast.gif | Bin .../assets/config_edit.cast.gif | Bin .../assets/config_set.cast.gif | Bin .../assets/stack_init.cast.gif | Bin .../assets/stack_restart.cast.gif | Bin .../src/cli.rs | 24 +- .../src/commands/autocomplete.rs | 23 +- .../src/commands/config.rs | 0 .../src/commands/mod.rs | 0 .../src/commands/stack.rs | 34 +- .../src/commands/test.rs | 10 +- .../src/commands/utils.rs | 0 .../src/commands/wallet.rs | 12 +- .../src/config.rs | 10 +- .../src/lib.rs | 0 .../src/main.rs | 2 +- .../src/utils/config/default_values.rs | 0 .../src/utils/config/mod.rs | 36 +- .../src/utils/messages.rs | 2 +- .../src/utils/mod.rs | 0 cmd/hive_report/Cargo.toml | 8 + cmd/hive_report/src/main.rs | 69 ++ cmd/loc/Cargo.toml | 8 + cmd/loc/src/main.rs | 36 + crates/blockchain/Cargo.toml | 21 +- crates/blockchain/blockchain.rs | 12 +- crates/blockchain/constants.rs | 2 - crates/blockchain/dev/Cargo.toml | 4 +- crates/blockchain/dev/Dockerfile | 8 +- crates/blockchain/dev/block_producer.rs | 2 +- crates/blockchain/dev/docker-compose-dev.yaml | 6 +- .../blockchain/dev/utils/engine_client/mod.rs | 4 +- crates/blockchain/error.rs | 12 +- crates/blockchain/fork_choice.rs | 4 +- crates/blockchain/mempool.rs | 183 +--- crates/blockchain/payload.rs | 10 +- crates/blockchain/smoke_test.rs | 4 +- crates/common/Cargo.toml | 14 +- crates/common/rlp/Cargo.toml | 2 +- crates/common/rlp/decode.rs | 3 + crates/common/rlp/structs.rs | 10 +- crates/common/types/account.rs | 4 +- crates/common/types/blobs_bundle.rs | 305 ++++++- crates/common/types/block.rs | 6 +- crates/common/types/constants.rs | 1 + crates/common/types/fork_id.rs | 4 +- crates/common/types/genesis.rs | 4 +- crates/common/types/mod.rs | 2 +- crates/common/types/receipt.rs | 4 +- crates/common/types/transaction.rs | 2 +- crates/l2/.env.example | 1 - crates/l2/Cargo.toml | 20 +- crates/l2/Dockerfile | 8 +- crates/l2/Makefile | 66 +- crates/l2/contracts/Cargo.toml | 8 +- crates/l2/contracts/Dockerfile | 12 +- crates/l2/contracts/deployer.rs | 6 +- crates/l2/contracts/src/l1/CommonBridge.sol | 5 + .../src/l1/interfaces/ICommonBridge.sol | 5 + crates/l2/docker-compose-l2.yaml | 14 +- crates/l2/docs/README.md | 4 +- crates/l2/docs/contracts.md | 17 +- crates/l2/docs/overview.md | 10 +- crates/l2/docs/proposer.md | 15 +- crates/l2/docs/prover.md | 31 +- crates/l2/proposer/errors.rs | 11 +- crates/l2/proposer/l1_committer.rs | 84 +- crates/l2/proposer/l1_watcher.rs | 82 +- crates/l2/proposer/mod.rs | 6 +- crates/l2/proposer/prover_server.rs | 19 +- crates/l2/prover/Cargo.toml | 24 +- crates/l2/prover/src/lib.rs | 2 +- crates/l2/prover/src/main.rs | 4 +- crates/l2/prover/src/prover.rs | 8 +- crates/l2/prover/src/prover_client.rs | 2 +- crates/l2/prover/tests/perf_zkvm.rs | 29 +- crates/l2/prover/zkvm/interface/Cargo.toml | 2 +- .../l2/prover/zkvm/interface/guest/Cargo.toml | 8 +- .../prover/zkvm/interface/guest/src/main.rs | 8 +- crates/l2/sdk/Cargo.toml | 10 +- crates/l2/sdk/src/sdk.rs | 8 +- crates/l2/tests/tests.rs | 18 +- crates/l2/utils/config/l1_watcher.rs | 1 - crates/l2/utils/eth_client/errors.rs | 2 +- crates/l2/utils/eth_client/eth_sender.rs | 6 +- crates/l2/utils/eth_client/mod.rs | 38 +- crates/l2/utils/eth_client/transaction.rs | 24 - crates/l2/utils/test_data_io.rs | 14 +- crates/networking/p2p/Cargo.toml | 10 +- crates/networking/p2p/README.md | 14 +- crates/networking/p2p/bootnode.rs | 4 +- crates/networking/p2p/discv4.rs | 6 +- crates/networking/p2p/kademlia.rs | 2 +- crates/networking/p2p/net.rs | 43 +- crates/networking/p2p/rlpx/connection.rs | 82 +- crates/networking/p2p/rlpx/error.rs | 4 +- crates/networking/p2p/rlpx/eth/backend.rs | 93 +- crates/networking/p2p/rlpx/eth/blocks.rs | 8 +- crates/networking/p2p/rlpx/eth/receipts.rs | 6 +- crates/networking/p2p/rlpx/eth/status.rs | 4 +- .../networking/p2p/rlpx/eth/transactions.rs | 6 +- crates/networking/p2p/rlpx/frame.rs | 4 +- crates/networking/p2p/rlpx/handshake.rs | 6 +- crates/networking/p2p/rlpx/message.rs | 4 +- crates/networking/p2p/rlpx/p2p.rs | 4 +- crates/networking/p2p/rlpx/snap.rs | 4 +- crates/networking/p2p/rlpx/utils.rs | 4 +- crates/networking/p2p/snap.rs | 10 +- crates/networking/p2p/types.rs | 4 +- crates/networking/rpc/Cargo.toml | 14 +- crates/networking/rpc/admin/mod.rs | 8 +- .../rpc/engine/exchange_transition_config.rs | 2 +- crates/networking/rpc/engine/fork_choice.rs | 2 +- crates/networking/rpc/engine/payload.rs | 10 +- crates/networking/rpc/eth/account.rs | 4 +- crates/networking/rpc/eth/block.rs | 8 +- crates/networking/rpc/eth/fee_market.rs | 8 +- crates/networking/rpc/eth/filter.rs | 18 +- crates/networking/rpc/eth/gas_price.rs | 8 +- crates/networking/rpc/eth/logs.rs | 4 +- crates/networking/rpc/eth/transaction.rs | 21 +- crates/networking/rpc/rpc.rs | 10 +- crates/networking/rpc/types/account_proof.rs | 2 +- crates/networking/rpc/types/block.rs | 6 +- .../networking/rpc/types/block_identifier.rs | 4 +- crates/networking/rpc/types/fork_choice.rs | 2 +- crates/networking/rpc/types/payload.rs | 4 +- crates/networking/rpc/types/receipt.rs | 16 +- crates/networking/rpc/types/transaction.rs | 4 +- crates/networking/rpc/utils.rs | 12 +- crates/networking/rpc/web3/mod.rs | 4 +- crates/storage/store/Cargo.toml | 12 +- crates/storage/store/engines/api.rs | 6 +- crates/storage/store/engines/in_memory.rs | 6 +- crates/storage/store/engines/libmdbx.rs | 10 +- crates/storage/store/error.rs | 4 +- crates/storage/store/rlp.rs | 6 +- crates/storage/store/storage.rs | 16 +- crates/storage/trie/Cargo.toml | 6 +- crates/storage/trie/error.rs | 4 +- crates/storage/trie/nibbles.rs | 12 +- crates/storage/trie/node.rs | 2 +- crates/storage/trie/node/branch.rs | 2 +- crates/storage/trie/node/extension.rs | 2 +- crates/storage/trie/node/leaf.rs | 2 +- crates/storage/trie/node_hash.rs | 6 +- crates/storage/trie/rlp.rs | 2 +- crates/storage/trie/state.rs | 2 +- crates/storage/trie/trie.rs | 46 +- crates/storage/trie/verify_range.rs | 841 ++++++++++++++++++ crates/vm/Cargo.toml | 16 +- crates/vm/db.rs | 12 +- crates/vm/errors.rs | 6 +- crates/vm/execution_db.rs | 23 +- crates/vm/execution_result.rs | 4 +- crates/vm/levm/Cargo.toml | 6 +- crates/vm/levm/Makefile | 2 +- .../vm/levm/bench/revm_comparison/Cargo.toml | 2 +- .../vm/levm/bench/revm_comparison/src/lib.rs | 2 +- crates/vm/levm/src/account.rs | 2 +- crates/vm/levm/src/call_frame.rs | 2 +- crates/vm/levm/src/constants.rs | 4 +- crates/vm/levm/src/db.rs | 2 +- crates/vm/levm/src/environment.rs | 2 +- crates/vm/levm/src/errors.rs | 2 +- crates/vm/levm/src/gas_cost.rs | 2 +- crates/vm/levm/src/memory.rs | 2 +- .../vm/levm/src/opcode_handlers/arithmetic.rs | 2 +- .../src/opcode_handlers/bitwise_comparison.rs | 2 +- crates/vm/levm/src/opcode_handlers/block.rs | 2 +- .../levm/src/opcode_handlers/environment.rs | 2 +- crates/vm/levm/src/opcode_handlers/keccak.rs | 2 +- crates/vm/levm/src/opcode_handlers/logging.rs | 2 +- crates/vm/levm/src/opcode_handlers/push.rs | 2 +- .../stack_memory_storage_flow.rs | 4 +- crates/vm/levm/src/opcode_handlers/system.rs | 12 +- crates/vm/levm/src/operations.rs | 2 +- crates/vm/levm/src/utils.rs | 2 +- crates/vm/levm/src/vm.rs | 14 +- crates/vm/levm/tests/edge_case_tests.rs | 4 +- crates/vm/levm/tests/tests.rs | 4 +- crates/vm/vm.rs | 10 +- test_data/genesis-l2-old.json | 548 ++++++++++++ test_data/network_params.yaml | 4 +- 218 files changed, 3214 insertions(+), 1311 deletions(-) create mode 100644 .github/workflows/docker-build.yaml create mode 100644 .github/workflows/hive_coverage.yaml create mode 100644 .github/workflows/loc.yaml rename cmd/ef_tests/{ethereum_rust => ethrex}/Cargo.toml (66%) rename cmd/ef_tests/{ethereum_rust => ethrex}/ef_tests.rs (100%) rename cmd/ef_tests/{ethereum_rust => ethrex}/network.rs (97%) rename cmd/ef_tests/{ethereum_rust => ethrex}/test_runner.rs (97%) rename cmd/ef_tests/{ethereum_rust => ethrex}/tests/cancun.rs (91%) rename cmd/ef_tests/{ethereum_rust => ethrex}/tests/shanghai.rs (88%) rename cmd/ef_tests/{ethereum_rust => ethrex}/types.rs (91%) delete mode 100644 cmd/ethereum_rust/Cargo.toml create mode 100644 cmd/ethrex/Cargo.toml rename cmd/{ethereum_rust => ethrex}/cli.rs (92%) rename cmd/{ethereum_rust => ethrex}/decode.rs (94%) rename cmd/{ethereum_rust/ethereum_rust.rs => ethrex/ethrex.rs} (72%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/Cargo.toml (71%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/README.md (74%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/config_create.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/config_delete.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/config_edit.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/config_set.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/stack_init.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/assets/stack_restart.cast.gif (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/cli.rs (62%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/autocomplete.rs (85%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/config.rs (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/mod.rs (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/stack.rs (89%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/test.rs (93%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/utils.rs (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/commands/wallet.rs (98%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/config.rs (87%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/lib.rs (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/main.rs (90%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/utils/config/default_values.rs (100%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/utils/config/mod.rs (90%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/utils/messages.rs (97%) rename cmd/{ethereum_rust_l2 => ethrex_l2}/src/utils/mod.rs (100%) create mode 100644 cmd/hive_report/Cargo.toml create mode 100644 cmd/hive_report/src/main.rs create mode 100644 cmd/loc/Cargo.toml create mode 100644 cmd/loc/src/main.rs delete mode 100644 crates/l2/utils/eth_client/transaction.rs create mode 100644 crates/storage/trie/verify_range.rs create mode 100644 test_data/genesis-l2-old.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 7d50e032f..b98a92d72 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,7 +43,7 @@ jobs: - name: Run cargo clippy run: | - cargo clippy --all-targets --all-features --workspace --exclude ethereum_rust-prover -- -D warnings + cargo clippy --all-targets --all-features --workspace --exclude ethrex-prover -- -D warnings - name: Run cargo fmt run: | diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml new file mode 100644 index 000000000..4ae4be06e --- /dev/null +++ b/.github/workflows/docker-build.yaml @@ -0,0 +1,30 @@ +name: Docker build + +on: + workflow_call: + +jobs: + docker-build: + name: Docker Build image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + load: true + tags: ethrex + outputs: type=docker,dest=/tmp/ethrex_image.tar + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ethrex_image + path: /tmp/ethrex_image.tar + diff --git a/.github/workflows/hive_and_assertoor.yaml b/.github/workflows/hive_and_assertoor.yaml index 22a25aa6c..78287eab7 100644 --- a/.github/workflows/hive_and_assertoor.yaml +++ b/.github/workflows/hive_and_assertoor.yaml @@ -22,33 +22,12 @@ env: RUST_VERSION: 1.80.1 jobs: - docker-build: - name: Docker Build image - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - load: true - tags: ethereum_rust - outputs: type=docker,dest=/tmp/ethereum_rust_image.tar - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ethereum_rust_image - path: /tmp/ethereum_rust_image.tar + build: + uses: ./.github/workflows/docker-build.yaml run-hive: name: Hive - ${{ matrix.name }} - needs: [docker-build] + needs: [build] runs-on: ubuntu-latest strategy: matrix: @@ -67,7 +46,7 @@ jobs: run_command: make run-hive-on-latest SIMULATION=devp2p TEST_PATTERN="/AccountRange|StorageRanges|ByteCodes|TrieNodes" - simulation: eth name: "Devp2p eth tests" - run_command: make run-hive SIMULATION=devp2p TEST_PATTERN="eth/getblockheaders|getblockbodies|transaction" + run_command: make run-hive SIMULATION=devp2p TEST_PATTERN="eth/status|getblockheaders|getblockbodies|transaction" - simulation: engine name: "Engine tests" run_command: make run-hive-on-latest SIMULATION=ethereum/engine TEST_PATTERN="/Blob Transactions On Block 1, Cancun Genesis|Blob Transactions On Block 1, Shanghai Genesis|Blob Transaction Ordering, Single Account, Single Blob|Blob Transaction Ordering, Single Account, Dual Blob|Blob Transaction Ordering, Multiple Accounts|Replace Blob Transactions|Parallel Blob Transactions|ForkchoiceUpdatedV3 Modifies Payload ID on Different Beacon Root|NewPayloadV3 After Cancun|NewPayloadV3 Versioned Hashes|ForkchoiceUpdated Version on Payload Request" @@ -78,12 +57,12 @@ jobs: - name: Download artifacts uses: actions/download-artifact@v4 with: - name: ethereum_rust_image + name: ethrex_image path: /tmp - name: Load image run: | - docker load --input /tmp/ethereum_rust_image.tar + docker load --input /tmp/ethrex_image.tar - name: Checkout sources uses: actions/checkout@v3 @@ -102,24 +81,24 @@ jobs: run-assertoor: name: Assertoor - Stability Check runs-on: ubuntu-latest - needs: [docker-build] + needs: [build] steps: - uses: actions/checkout@v4 - name: Download artifacts uses: actions/download-artifact@v4 with: - name: ethereum_rust_image + name: ethrex_image path: /tmp - name: Load image run: | - docker load --input /tmp/ethereum_rust_image.tar + docker load --input /tmp/ethrex_image.tar - name: Setup kurtosis testnet and run assertoor tests uses: ethpandaops/kurtosis-assertoor-github-action@v1 with: kurtosis_version: '1.3.1' ethereum_package_url: 'github.com/lambdaclass/ethereum-package' - ethereum_package_branch: 'ethereum-rust-integration' + ethereum_package_branch: 'ethrex-integration' ethereum_package_args: './test_data/network_params.yaml' diff --git a/.github/workflows/hive_coverage.yaml b/.github/workflows/hive_coverage.yaml new file mode 100644 index 000000000..b11ec2ffb --- /dev/null +++ b/.github/workflows/hive_coverage.yaml @@ -0,0 +1,51 @@ +name: Daily Hive Coverage + +on: + schedule: + # Every day at UTC midnight + - cron: '0 0 * * *' + workflow_dispatch: + +env: + RUST_VERSION: 1.80.1 + +jobs: + build: + uses: ./.github/workflows/docker-build.yaml + + hive-coverage: + name: Run engine hive simulator to gather coverage information. + runs-on: ubuntu-latest + needs: [build] + steps: + # TODO: Maybe this can be reused as well. + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + name: ethrex_image + path: /tmp + + - name: Load image + run: | + docker load --input /tmp/ethrex_image.tar + + - name: Checkout sources + uses: actions/checkout@v3 + + - name: Rustup toolchain install + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_VERSION }} + + - name: Setup Go + uses: actions/setup-go@v3 + + - name: Run Hive Simulation + run: make run-hive-on-latest SIMULATION=ethereum/engine + continue-on-error: true + + - name: Caching + uses: Swatinem/rust-cache@v2 + + - name: Generate the hive report + run: cargo run -p hive_report diff --git a/.github/workflows/l2_prover_ci.yaml b/.github/workflows/l2_prover_ci.yaml index 9dc4fcb20..f5d27b0e6 100644 --- a/.github/workflows/l2_prover_ci.yaml +++ b/.github/workflows/l2_prover_ci.yaml @@ -1,13 +1,8 @@ name: L2 Prover CI on: - push: - branches: ["main"] - paths: - - "crates/l2/prover/**" + merge_group: pull_request: branches: ["**"] - paths: - - "crates/l2/prover/**" concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -22,9 +17,9 @@ jobs: matrix: action: - command: check - args: -p ethereum_rust-prover + args: -p ethrex-prover - command: clippy - args: -p ethereum_rust-prover --all-targets --no-default-features + args: -p ethrex-prover --all-targets --no-default-features steps: - name: Checkout sources uses: actions/checkout@v4 diff --git a/.github/workflows/loc.yaml b/.github/workflows/loc.yaml new file mode 100644 index 000000000..b8a7046b4 --- /dev/null +++ b/.github/workflows/loc.yaml @@ -0,0 +1,29 @@ +name: Weekly LoC + +on: + schedule: + # Every Friday at midnight + - cron: "0 0 * * 5" + workflow_dispatch: + +env: + RUST_VERSION: 1.80.1 + +jobs: + loc: + name: Count ethrex loc and generate report + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v4 + + - name: Rustup toolchain install + uses: dtolnay/rust-toolchain@stable + with: + toolchain: ${{ env.RUST_VERSION }} + + - name: Add Rust Cache + uses: Swatinem/rust-cache@v2 + + - name: Generate the loc report + run: make loc diff --git a/.gitignore b/.gitignore index b595512e4..3ee1c729c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb -cmd/ef_tests/ethereum_rust/vectors +cmd/ef_tests/ethrex/vectors cmd/ef_tests/levm/vectors # Repos checked out by make target @@ -36,7 +36,7 @@ crates/levm_mlir/output crates/levm_mlir/ethtests crates/levm_mlir/*.tar.gz -# Ethereum Rust L2 stuff +# ethrex L2 stuff volumes jwt.hex diff --git a/Cargo.toml b/Cargo.toml index 446ea3fa7..68e03158b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,40 +9,38 @@ members = [ "crates/vm", "crates/storage/trie", "crates/common/rlp", - "cmd/ethereum_rust", - "cmd/ef_tests/ethereum_rust", + "cmd/ethrex", + "cmd/ef_tests/ethrex", "cmd/ef_tests/levm", - "cmd/ethereum_rust_l2", + "cmd/ethrex_l2", + "cmd/hive_report", "crates/vm/levm", "crates/vm/levm/bench/revm_comparison", "crates/l2/", "crates/l2/prover", "crates/l2/contracts", "crates/l2/sdk", + "cmd/loc", ] resolver = "2" -default-members = [ - "cmd/ethereum_rust", - "cmd/ethereum_rust_l2", - "crates/l2/prover", -] +default-members = ["cmd/ethrex", "cmd/ethrex_l2", "crates/l2/prover", "cmd/loc"] [workspace.package] version = "0.1.0" edition = "2021" [workspace.dependencies] -ethereum_rust-blockchain = { path = "./crates/blockchain" } -ethereum_rust-core = { path = "./crates/common" } -ethereum_rust-net = { path = "./crates/networking/p2p" } -ethereum_rust-rpc = { path = "./crates/networking/rpc" } -ethereum_rust-storage = { path = "./crates/storage/store" } -ethereum_rust-vm = { path = "./crates/vm" } -ethereum_rust-trie = { path = "./crates/storage/trie" } -ethereum_rust-rlp = { path = "./crates/common/rlp" } -ethereum_rust-l2 = { path = "./crates/l2" } -ethereum_rust-prover = { path = "./crates/l2/prover" } +ethrex-blockchain = { path = "./crates/blockchain" } +ethrex-core = { path = "./crates/common" } +ethrex-net = { path = "./crates/networking/p2p" } +ethrex-rpc = { path = "./crates/networking/rpc" } +ethrex-storage = { path = "./crates/storage/store" } +ethrex-vm = { path = "./crates/vm" } +ethrex-trie = { path = "./crates/storage/trie" } +ethrex-rlp = { path = "./crates/common/rlp" } +ethrex-l2 = { path = "./crates/l2" } +ethrex-prover = { path = "./crates/l2/prover" } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = "0.3.0" @@ -65,6 +63,7 @@ rand = "0.8.5" cfg-if = "1.0.0" reqwest = { version = "0.12.7", features = ["json"] } snap = "1.1.1" +k256 = { version = "0.13.3", features = ["ecdh"] } secp256k1 = { version = "0.29", default-features = false, features = [ "global-context", "recovery", diff --git a/Dockerfile b/Dockerfile index c945fd449..0f3bec28b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethereum_rust +WORKDIR /ethrex FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethereum_rust/recipe.json recipe.json +COPY --from=planner /ethrex/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,7 +27,6 @@ RUN cargo build --release FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethereum_rust/target/release/ethereum_rust . +COPY --from=builder ethrex/target/release/ethrex . EXPOSE 8545 -ENTRYPOINT [ "./ethereum_rust" ] - +ENTRYPOINT [ "./ethrex" ] diff --git a/Makefile b/Makefile index 63cd3c479..706a9c7b0 100644 --- a/Makefile +++ b/Makefile @@ -8,15 +8,15 @@ build: ## 🔨 Build the client cargo build --workspace lint: ## 🧹 Linter check - cargo clippy --all-targets --all-features --workspace --exclude ethereum_rust-prover -- -D warnings + cargo clippy --all-targets --all-features --workspace --exclude ethrex-prover -- -D warnings SPECTEST_VERSION := v3.0.0 SPECTEST_ARTIFACT := tests_$(SPECTEST_VERSION).tar.gz -SPECTEST_VECTORS_DIR := cmd/ef_tests/ethereum_rust/vectors +SPECTEST_VECTORS_DIR := cmd/ef_tests/ethrex/vectors CRATE ?= * test: $(SPECTEST_VECTORS_DIR) ## 🧪 Run each crate's tests - cargo test -p '$(CRATE)' --workspace --exclude ethereum_rust-prover --exclude ethereum_rust-levm --exclude ef_tests-levm -- --skip test_contract_compilation --skip testito + cargo test -p '$(CRATE)' --workspace --exclude ethrex-prover --exclude ethrex-levm --exclude ef_tests-levm -- --skip test_contract_compilation --skip testito clean: clean-vectors ## 🧹 Remove build artifacts cargo clean @@ -24,13 +24,13 @@ clean: clean-vectors ## 🧹 Remove build artifacts STAMP_FILE := .docker_build_stamp $(STAMP_FILE): $(shell find crates cmd -type f -name '*.rs') Cargo.toml Dockerfile - docker build -t ethereum_rust . + docker build -t ethrex . touch $(STAMP_FILE) build-image: $(STAMP_FILE) ## 🐳 Build the Docker image run-image: build-image ## 🏃 Run the Docker image - docker run --rm -p 127.0.0.1:8545:8545 ethereum_rust --http.addr 0.0.0.0 + docker run --rm -p 127.0.0.1:8545:8545 ethrex --http.addr 0.0.0.0 $(SPECTEST_ARTIFACT): rm -f tests_*.tar.gz # Delete older versions @@ -46,12 +46,12 @@ download-test-vectors: $(SPECTEST_VECTORS_DIR) ## 📥 Download test vectors clean-vectors: ## 🗑️ Clean test vectors rm -rf $(SPECTEST_VECTORS_DIR) -ETHEREUM_PACKAGE_REVISION := c7952d75d72159d03aec423b46797df2ded11f99 +ETHEREUM_PACKAGE_REVISION := 5b49d02ee556232a73ea1e28000ec5b3fca1073f # Shallow clones can't specify a single revision, but at least we avoid working # the whole history by making it shallow since a given date (one day before our # target revision). ethereum-package: - git clone --single-branch --branch ethereum-rust-integration https://github.com/lambdaclass/ethereum-package + git clone --single-branch --branch ethrex-integration https://github.com/lambdaclass/ethereum-package checkout-ethereum-package: ethereum-package ## 📦 Checkout specific Ethereum package revision cd ethereum-package && \ @@ -60,7 +60,7 @@ checkout-ethereum-package: ethereum-package ## 📦 Checkout specific Ethereum p localnet: stop-localnet-silent build-image checkout-ethereum-package ## 🌐 Start local network kurtosis run --enclave lambdanet ethereum-package --args-file test_data/network_params.yaml - docker logs -f $$(docker ps -q --filter ancestor=ethereum_rust) + docker logs -f $$(docker ps -q --filter ancestor=ethrex) stop-localnet: ## 🛑 Stop local network kurtosis enclave stop lambdanet @@ -71,7 +71,7 @@ stop-localnet-silent: @kurtosis enclave stop lambdanet >/dev/null 2>&1 || true @kurtosis enclave rm lambdanet --force >/dev/null 2>&1 || true -HIVE_REVISION := 421852ec25e4e608fe5460656f4bf0637649619e +HIVE_REVISION := fc6ddec210095e2369019e7f4ab2f9f38e35a8e8 # Shallow clones can't specify a single revision, but at least we avoid working # the whole history by making it shallow since a given date (one day before our # target revision). @@ -95,13 +95,16 @@ TEST_PATTERN ?= / # For example, to run the rpc-compat suites for eth_chainId & eth_blockNumber you should run: # `make run-hive SIMULATION=ethereum/rpc-compat TEST_PATTERN="/eth_chainId|eth_blockNumber"` run-hive: build-image setup-hive ## 🧪 Run Hive testing suite - cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" + cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" run-hive-on-latest: setup-hive ## 🧪 Run Hive testing suite with the latest docker image - cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" + cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" run-hive-debug: build-image setup-hive ## 🐞 Run Hive testing suite in debug mode - cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" --docker.output + cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" --docker.output clean-hive-logs: ## 🧹 Clean Hive logs rm -rf ./hive/workspace/logs + +loc: + cargo run -p loc diff --git a/README.md b/README.md index eb4c8fb2e..a5c10da9f 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# Lambda Ethereum Rust +# ethrex [![Telegram Chat][tg-badge]][tg-url] -[![license](https://img.shields.io/github/license/lambdaclass/ethereum_rust)](/LICENSE) +[![license](https://img.shields.io/github/license/lambdaclass/ethrex)](/LICENSE) [tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Frust_ethereum%2F&logo=telegram&label=chat&color=neon [tg-url]: https://t.me/rust_ethereum @@ -13,7 +13,7 @@ This client supports running in two different modes: - As a regular Ethereum execution client - As a ZK-Rollup, where block execution is proven and the proof sent to an L1 network for verification, thus inheriting the L1's security. -We call the first one Lambda Ethereum Rust L1 and the second one Lambda Ethereum Rust L2. +We call the first one ethrex L1 and the second one ethrex L2. ## Philosophy @@ -33,7 +33,7 @@ Read more about our engineering philosophy [here](https://blog.lambdaclass.com/l - Prioritize code readability and maintainability over premature optimizations. - Avoid concurrency split all over the codebase. Concurrency adds complexity. Only use where strictly necessary. -# Lambda Ethereum Rust L1 +# ethrex L1 ## Roadmap @@ -47,7 +47,7 @@ An Ethereum execution client consists roughly of the following parts: - The Ethereum Wire Protocol (`ETH`), used for state synchronization and block/transaction propagation, among other things. This runs on top of `RLPx`. - The `SNAP` protocol, used for exchanging state snapshots. Mainly needed for **snap sync**, a more optimized way of doing state sync than the old fast sync (you can read more about it [here](https://blog.ethereum.org/2021/03/03/geth-v1-10-0)). - Block building and Fork choice management (i.e. logic to both build blocks so a validator can propose them and set where the head of the chain is currently at, according to what the consensus layer determines). This is essentially what our `blockchain` crate contains. -- The block execution logic itself, i.e., an EVM implementation. We are finishing an implementation of our own called [levm](https://github.com/lambdaclass/ethereum_rust/tree/main/crates/vm/levm) (Lambda EVM). +- The block execution logic itself, i.e., an EVM implementation. We are finishing an implementation of our own called [levm](https://github.com/lambdaclass/ethrex/tree/main/crates/vm/levm) (Lambda EVM). Because most of the milestones below do not overlap much, we are currently working on them in parallel. @@ -60,61 +60,61 @@ Implement the bare minimum required to: In a bit more detail: -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Add `libmdbx` bindings and basic API, create tables for state (blocks, transactions, etc) | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Add `libmdbx` bindings and basic API, create tables for state (blocks, transactions, etc) | ✅ | EVM wrapper for block execution | ✅ | | JSON RPC API server setup | ✅ | | RPC State-serving endpoints | 🏗️ (almost done, a few endpoints are left) | -| Basic Engine API implementation. Set new chain head (`forkchoiceUpdated`) and new block (`newPayload`). | ✅ +| Basic Engine API implementation. Set new chain head (`forkchoiceUpdated`) and new block (`newPayload`). | ✅ -See detailed issues and progress for this milestone [here](https://github.com/lambdaclass/ethereum_rust/milestone/1). +See detailed issues and progress for this milestone [here](https://github.com/lambdaclass/ethrex/milestone/1). ### Milestone 2: History & Reorgs Implement support for block reorganizations and historical state queries. This milestone involves persisting the state trie to enable efficient access to historical states and implementing a tree structure for the blockchain to manage multiple chain branches. It also involves a real implementation of the `engine_forkchoiceUpdated` Engine API when we do not have to build the block ourselves (i.e. when `payloadAttributes` is null). -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Persist data on an on-disk Merkle Patricia Tree using `libmdbx` | ✅ -| Engine API `forkchoiceUpdated` implementation (without `payloadAttributes`) | 🏗️ -| Support for RPC historical queries, i.e. queries (`eth_call`, `eth_getBalance`, etc) at any block | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Persist data on an on-disk Merkle Patricia Tree using `libmdbx` | ✅ +| Engine API `forkchoiceUpdated` implementation (without `payloadAttributes`) | 🏗️ +| Support for RPC historical queries, i.e. queries (`eth_call`, `eth_getBalance`, etc) at any block | ✅ -Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/4). +Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/4). ### Milestone 3: Block building Add the ability to build new payloads (blocks), so the consensus client can propose new blocks based on transactions received from the RPC endpoints. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| `engine_forkchoiceUpdated` implementation with a non-null `payloadAttributes` | 🏗️ -| `engine_getPayload` endpoint implementation that builds blocks. | 🏗️ -| Implement a mempool and the `eth_sendRawTransaction` endpoint where users can send transactions | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| `engine_forkchoiceUpdated` implementation with a non-null `payloadAttributes` | 🏗️ +| `engine_getPayload` endpoint implementation that builds blocks. | 🏗️ +| Implement a mempool and the `eth_sendRawTransaction` endpoint where users can send transactions | ✅ -Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/5). +Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/5). ### Milestone 4: P2P Network Implement the peer to peer networking stack, i.e. the DevP2P protocol. This includes `discv4`, `RLPx` and the `eth` capability. This will let us get and retrieve blocks and transactions from other nodes. We'll add the transactions we receive to the mempool. We'll also download blocks from other nodes when we get payloads where the parent isn't in our local chain. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Implement `discv4` for peer discovery | ✅ -| Implement the `RLPx` transport protocol | 🏗️ -| Implement the `eth` capability | 🏗️ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Implement `discv4` for peer discovery | ✅ +| Implement the `RLPx` transport protocol | 🏗️ +| Implement the `eth` capability | 🏗️ -Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/2). +Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/2). ### Milestone 5: State Sync Add support for the `SNAP` protocol, which lets us get a recent copy of the blockchain state instead of going through all blocks from genesis. This is used for used for snap sync. Since we don't support older versions of the spec by design, this is a prerequisite to being able to sync the node with public networks, including mainnet. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Implement `SNAP` protocol for snap syncing | ❌ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Implement `SNAP` protocol for snap syncing | ❌ -Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/3). +Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/3). ## Quick Start (L1 localnet) @@ -160,12 +160,12 @@ make build ### Database Currently, the database is `libmdbx`, it will be set up when you start the client. The location of the db's files will depend on your OS: -- Mac: `~/Library/Application Support/ethereum_rust` -- Linux: `~/.config/ethereum_rust` +- Mac: `~/Library/Application Support/ethrex` +- Linux: `~/.config/ethrex` You can delete the db with: ```bash -cargo run --bin ethereum_rust -- removedb +cargo run --bin ethrex -- removedb ``` ### Test @@ -192,7 +192,7 @@ make test CRATE= ``` For example: ```bash -make test CRATE="ethereum_rust-blockchain" +make test CRATE="ethrex-blockchain" ``` ##### Hive Tests @@ -236,7 +236,7 @@ This example runs **every** test under rpc, with debug output Example run: ```bash -cargo run --bin ethereum_rust -- --network test_data/genesis-kurtosis.json +cargo run --bin ethrex -- --network test_data/genesis-kurtosis.json ``` The `network` argument is mandatory, as it defines the parameters of the chain. @@ -244,7 +244,7 @@ For more information about the different cli arguments check out the next sectio ### CLI Commands -Ethereum Rust supports the following command line arguments: +ethrex supports the following command line arguments: - `--network `: Receives a `Genesis` struct in json format. This is the only argument which is required. You can look at some example genesis files at `test_data/genesis*`. - `--datadir `: Receives the name of the directory where the Database is located. - `--import `: Receives an rlp encoded `Chain` object (aka a list of `Block`s). You can look at the example chain file at `test_data/chain.rlp`. @@ -260,11 +260,11 @@ Ethereum Rust supports the following command line arguments: - `--bootnodes `: Comma separated enode URLs for P2P discovery bootstrap. - `--log.level `: The verbosity level used for logs. Default value: info. possible values: info, debug, trace, warn, error -# Lambda Ethereum Rust L2 +# ethrex L2 -In this mode, the Ethereum Rust code is repurposed to run a rollup that settles on Ethereum as the L1. +In this mode, the ethrex code is repurposed to run a rollup that settles on Ethereum as the L1. -The main differences between this mode and regular Ethereum Rust are: +The main differences between this mode and regular ethrex are: - There is no consensus, the node is turned into a sequencer that proposes blocks for the network. - Block execution is proven using a RISC-V zkVM and its proofs are sent to L1 for verification. @@ -286,14 +286,15 @@ At a high level, the following new parts are added to the node: | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | | 0 | Users can deposit Eth in the L1 (Ethereum) and receive the corresponding funds on the L2. | ✅ | | 1 | The network supports basic L2 functionality, allowing users to deposit and withdraw funds to join and exit the network, while also interacting with the network as they do normally on the Ethereum network (deploying contracts, sending transactions, etc). | ✅ | -| 2 | The block execution is proven with a RISC-V zkVM and the proof is verified by the Verifier L1 contract. | 🏗️ | -| 3 | The network now commits to state diffs instead of the full state, lowering the commit transactions costs. These diffs are also submitted in compressed form, further reducing costs. It also supports EIP 4844 for L1 commit transactions, which means state diffs are sent as blob sidecars instead of calldata. | ❌ | -| 4 | The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. | ❌ | -| 5 | Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. | ❌ | -| 6 | The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. | ❌ | -| 7 | The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. | ❌ | -| 8 | The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. | ❌ | -| 9 | The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. | ❌ | +| 2 | The block execution is proven with a RISC-V zkVM and the proof is verified by the Verifier L1 contract. | ✅ | +| 3 | The network now commits to state diffs instead of the full state, lowering the commit transactions costs. These diffs are also submitted in compressed form, further reducing costs. It also supports EIP 4844 for L1 commit transactions, which means state diffs are sent as blob sidecars instead of calldata. | 🏗️ | +| 4 | Use our own EVM implementation | 🏗️ | +| 5 | The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. | ❌ | +| 6 | Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. | ❌ | +| 7 | The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. | ❌ | +| 8 | The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. | ❌ | +| 9 | The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. | ❌ | +| 10 | The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. | ❌ | ### Milestone 0 @@ -331,8 +332,8 @@ The L2's block execution is proven with a RISC-V zkVM and the proof is verified | Task Description | Status | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | | On the EVM, return all storage touched during block execution to pass to the prover as a witness | ✅ | -| Make the `onChainproposer` L1 contract verify the SNARK proof on the `verify` function. | 🏗️ | -| Add a `proverClient` binary that asks the sequencer for witness data to prove, generates proofs of execution and submits proofs to the `proverServer` component (see below) | 🏗️ | +| Make the `onChainproposer` L1 contract verify the SNARK proof on the `verify` function. | ✅ | +| Add a `proverClient` binary that asks the sequencer for witness data to prove, generates proofs of execution and submits proofs to the `proverServer` component (see below) | ✅ | | Add a `proverServer` component that feeds the `proverClient` with block witness data to be proven and delivers proofs to the `proposer` to send the L1 transaction for block verification | ✅ | ### Milestone 3: State diffs + Data compression + EIP 4844 (Blobs) @@ -345,13 +346,27 @@ It also supports EIP 4844 for L1 commit transactions, which means state diffs ar | Task Description | Status | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| The sequencer sends state diffs to the prover instead of full transaction data. | ❌ | +| The sequencer sends state diffs to the prover instead of full transaction data. | ✅ | | On the prover, prove the state diffs compression | ❌ | -| On the `proposer`, send the state diffs through a blob in a EIP 4844 transaction. | ❌ | +| On the `proposer`, send the state diffs through a blob in a EIP 4844 transaction. | ✅ | | Adapt the prover to prove a KZG commitment to the state diff and use the point evaluation precompile to show that the blob sent to the L1 is indeed the correct one through a proof of equivalence protocol | ❌ | | Add a command to the CLI to reconstructing the full L2 state from all the blob data on the L1. | ❌ | -### Milestone 4: Account Abstraction +### Milestone 4: Own EVM Implementation + +Finish our own EVM implementation (`levm`) to be able to add custom behaviour to the network (like account abstraction below). + +#### Status + +| Task Description | Status | +| ---------------------------------------------------- | ------ | +| Implement all opcodes | ✅ | +| Pass all execution (non-precompile) EF tests | 🏗️ | +| Implement all precompiles | 🏗️ | +| Pass all execution EF tests | 🏗️ | +| Full Ethereum Rust Integration (pass all Hive tests) | 🏗️ | + +### Milestone 5: Account Abstraction The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. @@ -362,7 +377,7 @@ The L2 supports native account abstraction following EIP 7702, allowing for cust | Add support for `SET_CODE_TX_TYPE` transactions (i.e. implement EIP 7702). | ❌ | | Add examples of WebAuthn signing and paymaster flows using EIP 7702 | ❌ | -### Milestone 5: L2s interoperability +### Milestone 6: L2s interoperability Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. @@ -375,7 +390,7 @@ Support multiple L2s sharing the same bridge contract on L1 for seamless interop TODO: Expand on tasks about proper interoperability between chains (seamlessly bridging between chains, etc). -### Milestone 6: Custom Native token +### Milestone 7: Custom Native token The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. @@ -387,7 +402,7 @@ The L2 can also be deployed using a custom native token, meaning that a certain | On the `commonBridge`, for custom native token deposits, `msg.value` should always be zero, and the amount of the native token to mint should be a new `valueToMintOnL2` argument. The amount should be deducted from the caller thorugh a `transferFrom`. | ❌ | | On the CLI, add support for custom native token deposits and withdrawals | ❌ | -### Milestone 7: Security (TEEs and Multi Prover support) +### Milestone 8: Security (TEEs and Multi Prover support) The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. @@ -399,7 +414,7 @@ The L2 has added security mechanisms in place, running on Trusted Execution Envi | Support verifying multiple different zkVM executions on the `onChainProposer` L1 contract. | ❌ | | Support running the operator on a TEE environment | ❌ | -### Milestone 8: Based Contestable Rollup +### Milestone 9: Based Contestable Rollup The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. @@ -411,7 +426,7 @@ The network can be run as a Based Rollup, meaning sequencing is done by the Ethe TODO: Expand on this. -### Milestone 9: Validium +### Milestone 10: Validium The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. @@ -443,7 +458,7 @@ The L2 can be initialized in Validium Mode, meaning the Data Availability layer make init ``` -This will setup a local Ethereum network as the L1, deploy all the needed contracts on it, then start an Ethereum Rust L2 node pointing to it. +This will setup a local Ethereum network as the L1, deploy all the needed contracts on it, then start an ethrex L2 node pointing to it. ### Restarting the network @@ -465,10 +480,10 @@ Most of them are [here](https://github.com/ethpandaops/ethereum-package/blob/mai } ``` -## Lambda Ethereum Rust L2 Docs +## ethrex L2 Docs -- [Ethereum Rust L2 Docs](./crates/l2/docs/README.md) -- [Ethereum Rust L2 CLI Docs](./cmd/ethereum_rust_l2/README.md) +- [ethrex L2 Docs](./crates/l2/docs/README.md) +- [ethrex L2 CLI Docs](./cmd/ethrex_l2/README.md) ## 📚 References and acknowledgements diff --git a/cmd/ef_tests/ethereum_rust/Cargo.toml b/cmd/ef_tests/ethrex/Cargo.toml similarity index 66% rename from cmd/ef_tests/ethereum_rust/Cargo.toml rename to cmd/ef_tests/ethrex/Cargo.toml index abd9c11c4..4c77e6c6f 100644 --- a/cmd/ef_tests/ethereum_rust/Cargo.toml +++ b/cmd/ef_tests/ethrex/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "ef_tests-ethereum_rust" +name = "ef_tests-ethrex" version.workspace = true edition.workspace = true [dependencies] -ethereum_rust-blockchain.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-rlp.workspace = true +ethrex-blockchain.workspace = true +ethrex-core.workspace = true +ethrex-storage.workspace = true +ethrex-rlp.workspace = true serde.workspace = true serde_json.workspace = true bytes.workspace = true diff --git a/cmd/ef_tests/ethereum_rust/ef_tests.rs b/cmd/ef_tests/ethrex/ef_tests.rs similarity index 100% rename from cmd/ef_tests/ethereum_rust/ef_tests.rs rename to cmd/ef_tests/ethrex/ef_tests.rs diff --git a/cmd/ef_tests/ethereum_rust/network.rs b/cmd/ef_tests/ethrex/network.rs similarity index 97% rename from cmd/ef_tests/ethereum_rust/network.rs rename to cmd/ef_tests/ethrex/network.rs index c69a5b2d5..300ab4ee0 100644 --- a/cmd/ef_tests/ethereum_rust/network.rs +++ b/cmd/ef_tests/ethrex/network.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::types::ChainConfig; +use ethrex_core::types::ChainConfig; use lazy_static::lazy_static; use serde::Deserialize; diff --git a/cmd/ef_tests/ethereum_rust/test_runner.rs b/cmd/ef_tests/ethrex/test_runner.rs similarity index 97% rename from cmd/ef_tests/ethereum_rust/test_runner.rs rename to cmd/ef_tests/ethrex/test_runner.rs index 2bfb66507..7d1d13f03 100644 --- a/cmd/ef_tests/ethereum_rust/test_runner.rs +++ b/cmd/ef_tests/ethrex/test_runner.rs @@ -1,12 +1,12 @@ use std::{collections::HashMap, path::Path}; use crate::types::{BlockWithRLP, TestUnit}; -use ethereum_rust_blockchain::{add_block, fork_choice::apply_fork_choice}; -use ethereum_rust_core::types::{ +use ethrex_blockchain::{add_block, fork_choice::apply_fork_choice}; +use ethrex_core::types::{ Account as CoreAccount, Block as CoreBlock, BlockHeader as CoreBlockHeader, }; -use ethereum_rust_rlp::decode::RLPDecode; -use ethereum_rust_storage::{EngineType, Store}; +use ethrex_rlp::decode::RLPDecode; +use ethrex_storage::{EngineType, Store}; pub fn run_ef_test(test_key: &str, test: &TestUnit) { // check that the decoded genesis block header matches the deserialized one diff --git a/cmd/ef_tests/ethereum_rust/tests/cancun.rs b/cmd/ef_tests/ethrex/tests/cancun.rs similarity index 91% rename from cmd/ef_tests/ethereum_rust/tests/cancun.rs rename to cmd/ef_tests/ethrex/tests/cancun.rs index 4ed4ad94d..13ade9dd2 100644 --- a/cmd/ef_tests/ethereum_rust/tests/cancun.rs +++ b/cmd/ef_tests/ethrex/tests/cancun.rs @@ -1,6 +1,6 @@ use std::path::Path; -use ef_tests_ethereum_rust::test_runner::{parse_test_file, run_ef_test}; +use ef_tests_ethrex::test_runner::{parse_test_file, run_ef_test}; fn parse_and_execute(path: &Path) -> datatest_stable::Result<()> { let tests = parse_test_file(path); diff --git a/cmd/ef_tests/ethereum_rust/tests/shanghai.rs b/cmd/ef_tests/ethrex/tests/shanghai.rs similarity index 88% rename from cmd/ef_tests/ethereum_rust/tests/shanghai.rs rename to cmd/ef_tests/ethrex/tests/shanghai.rs index d3689f939..666eff873 100644 --- a/cmd/ef_tests/ethereum_rust/tests/shanghai.rs +++ b/cmd/ef_tests/ethrex/tests/shanghai.rs @@ -1,6 +1,6 @@ use std::path::Path; -use ef_tests_ethereum_rust::test_runner::{parse_test_file, run_ef_test}; +use ef_tests_ethrex::test_runner::{parse_test_file, run_ef_test}; fn parse_and_execute(path: &Path) -> datatest_stable::Result<()> { let tests = parse_test_file(path); diff --git a/cmd/ef_tests/ethereum_rust/types.rs b/cmd/ef_tests/ethrex/types.rs similarity index 91% rename from cmd/ef_tests/ethereum_rust/types.rs rename to cmd/ef_tests/ethrex/types.rs index 05a525475..e05ecc031 100644 --- a/cmd/ef_tests/ethereum_rust/types.rs +++ b/cmd/ef_tests/ethrex/types.rs @@ -1,11 +1,11 @@ use bytes::Bytes; -use ethereum_rust_core::types::{ - code_hash, Account as ethereum_rustAccount, AccountInfo, Block as CoreBlock, BlockBody, +use ethrex_core::types::{ + code_hash, Account as ethrexAccount, AccountInfo, Block as CoreBlock, BlockBody, EIP1559Transaction, EIP2930Transaction, EIP4844Transaction, LegacyTransaction, - Transaction as ethereum_rustTransaction, TxKind, + Transaction as ethrexTransaction, TxKind, }; -use ethereum_rust_core::types::{Genesis, GenesisAccount, Withdrawal}; -use ethereum_rust_core::{types::BlockHeader, Address, Bloom, H256, H64, U256}; +use ethrex_core::types::{Genesis, GenesisAccount, Withdrawal}; +use ethrex_core::{types::BlockHeader, Address, Bloom, H256, H64, U256}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -18,7 +18,7 @@ pub struct TestUnit { pub info: Option, pub blocks: Vec, pub genesis_block_header: Header, - #[serde(rename = "genesisRLP", with = "ethereum_rust_core::serde_utils::bytes")] + #[serde(rename = "genesisRLP", with = "ethrex_core::serde_utils::bytes")] pub genesis_rlp: Bytes, pub lastblockhash: H256, pub network: Network, @@ -70,7 +70,7 @@ impl TestUnit { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct Account { pub balance: U256, - #[serde(with = "ethereum_rust_core::serde_utils::bytes")] + #[serde(with = "ethrex_core::serde_utils::bytes")] pub code: Bytes, pub nonce: U256, pub storage: HashMap, @@ -109,7 +109,7 @@ pub struct Header { pub bloom: Bloom, pub coinbase: Address, pub difficulty: U256, - #[serde(with = "ethereum_rust_core::serde_utils::bytes")] + #[serde(with = "ethrex_core::serde_utils::bytes")] pub extra_data: Bytes, pub gas_limit: U256, pub gas_used: U256, @@ -134,7 +134,7 @@ pub struct Header { #[derive(Debug, PartialEq, Eq, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct BlockWithRLP { - #[serde(with = "ethereum_rust_core::serde_utils::bytes")] + #[serde(with = "ethrex_core::serde_utils::bytes")] pub rlp: Bytes, #[serde(flatten)] inner: Option, @@ -191,7 +191,7 @@ impl From for CoreBlock { pub struct Transaction { #[serde(rename = "type")] pub transaction_type: Option, - #[serde(with = "ethereum_rust_core::serde_utils::bytes")] + #[serde(with = "ethrex_core::serde_utils::bytes")] pub data: Bytes, pub gas_limit: U256, pub gas_price: Option, @@ -211,7 +211,7 @@ pub struct Transaction { pub to: TxKind, } -// Conversions between EFtests & ethereum_rust types +// Conversions between EFtests & ethrex types impl From
for BlockHeader { fn from(val: Header) -> Self { BlockHeader { @@ -239,17 +239,17 @@ impl From
for BlockHeader { } } -impl From for ethereum_rustTransaction { +impl From for ethrexTransaction { fn from(val: Transaction) -> Self { match val.transaction_type { Some(tx_type) => match tx_type.as_u64() { - 0 => ethereum_rustTransaction::LegacyTransaction(val.into()), - 1 => ethereum_rustTransaction::EIP2930Transaction(val.into()), - 2 => ethereum_rustTransaction::EIP1559Transaction(val.into()), - 3 => ethereum_rustTransaction::EIP4844Transaction(val.into()), + 0 => ethrexTransaction::LegacyTransaction(val.into()), + 1 => ethrexTransaction::EIP2930Transaction(val.into()), + 2 => ethrexTransaction::EIP1559Transaction(val.into()), + 3 => ethrexTransaction::EIP4844Transaction(val.into()), _ => unimplemented!(), }, - None => ethereum_rustTransaction::LegacyTransaction(val.into()), + None => ethrexTransaction::LegacyTransaction(val.into()), } } } @@ -365,9 +365,9 @@ impl From for EIP2930Transaction { } } -impl From for ethereum_rustAccount { +impl From for ethrexAccount { fn from(val: Account) -> Self { - ethereum_rustAccount { + ethrexAccount { info: AccountInfo { code_hash: code_hash(&val.code), balance: val.balance, diff --git a/cmd/ef_tests/levm/Cargo.toml b/cmd/ef_tests/levm/Cargo.toml index 5e038b111..6a553046e 100644 --- a/cmd/ef_tests/levm/Cargo.toml +++ b/cmd/ef_tests/levm/Cargo.toml @@ -4,12 +4,12 @@ version.workspace = true edition.workspace = true [dependencies] -ethereum_rust-blockchain = { workspace = true, features = ["levm"] } -ethereum_rust-core.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-rlp.workspace = true -ethereum_rust-vm = { workspace = true, features = ["levm"] } -ethereum_rust-levm = { path = "../../../crates/vm/levm" } +ethrex-blockchain = { workspace = true, features = ["levm"] } +ethrex-core.workspace = true +ethrex-storage.workspace = true +ethrex-rlp.workspace = true +ethrex-vm = { workspace = true, features = ["levm"] } +ethrex-levm = { path = "../../../crates/vm/levm" } serde.workspace = true serde_json.workspace = true bytes.workspace = true diff --git a/cmd/ef_tests/levm/deserialize.rs b/cmd/ef_tests/levm/deserialize.rs index 81590178f..612e17045 100644 --- a/cmd/ef_tests/levm/deserialize.rs +++ b/cmd/ef_tests/levm/deserialize.rs @@ -1,6 +1,6 @@ use crate::types::{EFTest, EFTests}; use bytes::Bytes; -use ethereum_rust_core::U256; +use ethrex_core::U256; use serde::Deserialize; use std::{collections::HashMap, str::FromStr}; diff --git a/cmd/ef_tests/levm/report.rs b/cmd/ef_tests/levm/report.rs index 513c2c363..7c0a3d9e4 100644 --- a/cmd/ef_tests/levm/report.rs +++ b/cmd/ef_tests/levm/report.rs @@ -1,9 +1,9 @@ use crate::runner::EFTestRunnerError; use colored::Colorize; -use ethereum_rust_core::Address; -use ethereum_rust_levm::errors::{TransactionReport, TxResult, VMError}; -use ethereum_rust_storage::AccountUpdate; -use ethereum_rust_vm::SpecId; +use ethrex_core::Address; +use ethrex_levm::errors::{TransactionReport, TxResult, VMError}; +use ethrex_storage::AccountUpdate; +use ethrex_vm::SpecId; use revm::primitives::{ExecutionResult as RevmExecutionResult, HashSet}; use std::{ collections::HashMap, diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 39b43127a..46d003d3e 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -4,18 +4,18 @@ use crate::{ types::EFTest, utils, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{code_hash, AccountInfo}, H256, U256, }; -use ethereum_rust_levm::{ +use ethrex_levm::{ db::Cache, errors::{TransactionReport, VMError}, vm::VM, Environment, }; -use ethereum_rust_storage::AccountUpdate; -use ethereum_rust_vm::db::StoreWrapper; +use ethrex_storage::AccountUpdate; +use ethrex_vm::db::StoreWrapper; use keccak_hash::keccak; use std::{collections::HashMap, sync::Arc}; diff --git a/cmd/ef_tests/levm/runner/mod.rs b/cmd/ef_tests/levm/runner/mod.rs index 63217ea9f..c496ff9d5 100644 --- a/cmd/ef_tests/levm/runner/mod.rs +++ b/cmd/ef_tests/levm/runner/mod.rs @@ -4,8 +4,8 @@ use crate::{ }; use clap::Parser; use colored::Colorize; -use ethereum_rust_levm::errors::{TransactionReport, VMError}; -use ethereum_rust_vm::SpecId; +use ethrex_levm::errors::{TransactionReport, VMError}; +use ethrex_vm::SpecId; use spinoff::{spinners::Dots, Color, Spinner}; pub mod levm_runner; diff --git a/cmd/ef_tests/levm/runner/revm_runner.rs b/cmd/ef_tests/levm/runner/revm_runner.rs index dc09a372c..ea59ed28f 100644 --- a/cmd/ef_tests/levm/runner/revm_runner.rs +++ b/cmd/ef_tests/levm/runner/revm_runner.rs @@ -4,10 +4,10 @@ use crate::{ types::EFTest, utils::load_initial_state, }; -use ethereum_rust_core::{types::TxKind, Address}; -use ethereum_rust_levm::errors::{TransactionReport, TxResult}; -use ethereum_rust_storage::{error::StoreError, AccountUpdate}; -use ethereum_rust_vm::{db::StoreWrapper, spec_id, EvmState, RevmAddress, RevmU256}; +use ethrex_core::{types::TxKind, Address}; +use ethrex_levm::errors::{TransactionReport, TxResult}; +use ethrex_storage::{error::StoreError, AccountUpdate}; +use ethrex_vm::{db::StoreWrapper, spec_id, EvmState, RevmAddress, RevmU256}; use revm::{ db::State, inspectors::TracerEip3155 as RevmTracerEip3155, @@ -230,7 +230,7 @@ pub fn ensure_post_state( // We only want to compare account updates when no exception is expected. None => { let levm_account_updates = levm_runner::get_state_transitions(levm_execution_report); - let revm_account_updates = ethereum_rust_vm::get_state_transitions(revm_state); + let revm_account_updates = ethrex_vm::get_state_transitions(revm_state); let account_updates_report = compare_levm_revm_account_updates(&levm_account_updates, &revm_account_updates); re_run_report.register_account_updates_report(*vector, account_updates_report); diff --git a/cmd/ef_tests/levm/types.rs b/cmd/ef_tests/levm/types.rs index ebca565db..be399ca5e 100644 --- a/cmd/ef_tests/levm/types.rs +++ b/cmd/ef_tests/levm/types.rs @@ -7,11 +7,11 @@ use crate::{ report::TestVector, }; use bytes::Bytes; -use ethereum_rust_core::{ +use ethrex_core::{ types::{Genesis, GenesisAccount, TxKind}, Address, H256, U256, }; -use ethereum_rust_vm::SpecId; +use ethrex_vm::SpecId; use serde::Deserialize; use std::collections::HashMap; diff --git a/cmd/ef_tests/levm/utils.rs b/cmd/ef_tests/levm/utils.rs index 3e4dbe329..b09944b93 100644 --- a/cmd/ef_tests/levm/utils.rs +++ b/cmd/ef_tests/levm/utils.rs @@ -1,7 +1,7 @@ use crate::types::EFTest; -use ethereum_rust_core::{types::Genesis, H256}; -use ethereum_rust_storage::{EngineType, Store}; -use ethereum_rust_vm::{evm_state, EvmState}; +use ethrex_core::{types::Genesis, H256}; +use ethrex_storage::{EngineType, Store}; +use ethrex_vm::{evm_state, EvmState}; pub fn load_initial_state(test: &EFTest) -> (EvmState, H256) { let genesis = Genesis::from(test); diff --git a/cmd/ethereum_rust/Cargo.toml b/cmd/ethereum_rust/Cargo.toml deleted file mode 100644 index 69c87285e..000000000 --- a/cmd/ethereum_rust/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "ethereum_rust" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ethereum_rust-blockchain.workspace = true -ethereum_rust-rpc.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-net.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-vm.workspace = true -ethereum_rust-rlp.workspace = true -ethereum_rust-l2.workspace = true - -bytes.workspace = true -hex.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true -clap = { version = "4.5.4", features = ["cargo"] } -directories = "5.0.1" -serde_json.workspace = true -tokio = { version = "1.38.0", features = ["full"] } -anyhow = "1.0.86" -rand = "0.8.5" -k256 = { version = "0.13.3", features = ["ecdh"] } -local-ip-address = "0.6" -tokio-util.workspace = true - -cfg-if = "1.0.0" - -ethereum_rust-dev = { path = "../../crates/blockchain/dev", optional = true } - -[[bin]] -name = "ethereum_rust" -path = "./ethereum_rust.rs" - -[features] -default = [] -dev = ["dep:ethereum_rust-dev"] -l2 = ["ethereum_rust-vm/l2"] -levm = ["ethereum_rust-vm/levm", "ethereum_rust-blockchain/levm"] diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml new file mode 100644 index 000000000..eeec636c1 --- /dev/null +++ b/cmd/ethrex/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "ethrex" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethrex-blockchain.workspace = true +ethrex-rpc.workspace = true +ethrex-core.workspace = true +ethrex-net.workspace = true +ethrex-storage.workspace = true +ethrex-vm.workspace = true +ethrex-rlp.workspace = true +ethrex-l2.workspace = true + +bytes.workspace = true +hex.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +k256.workspace = true +clap = { version = "4.5.4", features = ["cargo"] } +directories = "5.0.1" +serde_json.workspace = true +tokio = { version = "1.38.0", features = ["full"] } +anyhow = "1.0.86" +rand = "0.8.5" +local-ip-address = "0.6" +tokio-util.workspace = true + +cfg-if = "1.0.0" + +ethrex-dev = { path = "../../crates/blockchain/dev", optional = true } + +[[bin]] +name = "ethrex" +path = "./ethrex.rs" + +[features] +default = [] +dev = ["dep:ethrex-dev"] +l2 = ["ethrex-vm/l2"] +levm = ["ethrex-vm/levm", "ethrex-blockchain/levm"] diff --git a/cmd/ethereum_rust/cli.rs b/cmd/ethrex/cli.rs similarity index 92% rename from cmd/ethereum_rust/cli.rs rename to cmd/ethrex/cli.rs index 727de4f38..b85e48548 100644 --- a/cmd/ethereum_rust/cli.rs +++ b/cmd/ethrex/cli.rs @@ -1,10 +1,10 @@ use clap::{Arg, ArgAction, Command}; -use ethereum_rust_net::bootnode::BootNode; +use ethrex_net::bootnode::BootNode; use tracing::Level; pub fn cli() -> Command { - Command::new("ethereum_rust") - .about("Ethereum Rust Execution client") + Command::new("ethrex") + .about("ethrex Execution client") .author("Lambdaclass") .arg( Arg::new("http.addr") @@ -104,6 +104,12 @@ pub fn cli() -> Command { .required(false) .value_name("CHAIN_RLP_PATH"), ) + .arg( + Arg::new("import_dir") + .long("import_dir") + .required(false) + .value_name("BLOCKS_DIR_PATH"), + ) .subcommand( Command::new("removedb").about("Remove the database").arg( Arg::new("datadir") diff --git a/cmd/ethereum_rust/decode.rs b/cmd/ethrex/decode.rs similarity index 94% rename from cmd/ethereum_rust/decode.rs rename to cmd/ethrex/decode.rs index a3a01a48d..01bb32647 100644 --- a/cmd/ethereum_rust/decode.rs +++ b/cmd/ethrex/decode.rs @@ -1,7 +1,7 @@ use anyhow::Error; use bytes::Bytes; -use ethereum_rust_core::types::{Block, Genesis}; -use ethereum_rust_rlp::decode::RLPDecode as _; +use ethrex_core::types::{Block, Genesis}; +use ethrex_rlp::decode::RLPDecode as _; use std::{ fs::File, io::{BufReader, Read as _}, @@ -38,7 +38,7 @@ pub fn genesis_file(file: File) -> Result { #[cfg(test)] mod tests { use crate::decode::chain_file; - use ethereum_rust_core::H256; + use ethrex_core::H256; use std::{fs::File, str::FromStr as _}; #[test] diff --git a/cmd/ethereum_rust/ethereum_rust.rs b/cmd/ethrex/ethrex.rs similarity index 72% rename from cmd/ethereum_rust/ethereum_rust.rs rename to cmd/ethrex/ethrex.rs index 67e208dba..0d905fc8e 100644 --- a/cmd/ethereum_rust/ethereum_rust.rs +++ b/cmd/ethrex/ethrex.rs @@ -1,15 +1,18 @@ use bytes::Bytes; use directories::ProjectDirs; -use ethereum_rust_blockchain::add_block; -use ethereum_rust_blockchain::fork_choice::apply_fork_choice; -use ethereum_rust_core::types::{Block, Genesis}; -use ethereum_rust_core::H256; -use ethereum_rust_net::bootnode::BootNode; -use ethereum_rust_net::node_id_from_signing_key; -use ethereum_rust_net::types::Node; -use ethereum_rust_storage::{EngineType, Store}; + +use ethrex_blockchain::add_block; +use ethrex_blockchain::fork_choice::apply_fork_choice; +use ethrex_core::types::{Block, Genesis}; +use ethrex_core::H256; +use ethrex_net::bootnode::BootNode; +use ethrex_net::node_id_from_signing_key; +use ethrex_net::types::Node; +use ethrex_rlp::decode::RLPDecode; +use ethrex_storage::{EngineType, Store}; use k256::ecdsa::SigningKey; use local_ip_address::local_ip; +use std::fs; use std::future::IntoFuture; use std::path::Path; use std::str::FromStr as _; @@ -26,7 +29,7 @@ use tracing_subscriber::{EnvFilter, FmtSubscriber}; mod cli; mod decode; -const DEFAULT_DATADIR: &str = "ethereum_rust"; +const DEFAULT_DATADIR: &str = "ethrex"; #[tokio::main] async fn main() { let matches = cli::cli().get_matches(); @@ -122,46 +125,34 @@ async fn main() { .expect("Failed to create genesis block"); if let Some(chain_rlp_path) = matches.get_one::("import") { + info!("Importing blocks from chain file: {}", chain_rlp_path); let blocks = read_chain_file(chain_rlp_path); - let size = blocks.len(); - for block in &blocks { - let hash = block.hash(); - info!( - "Adding block {} with hash {:#x}.", - block.header.number, hash - ); - let result = add_block(block, &store); - if let Some(error) = result.err() { - warn!( - "Failed to add block {} with hash {:#x}: {}.", - block.header.number, hash, error - ); - } - if store - .update_latest_block_number(block.header.number) - .is_err() - { - error!("Fatal: added block {} but could not update the block number -- aborting block import", block.header.number); - break; - }; - if store - .set_canonical_block(block.header.number, hash) - .is_err() - { - error!("Fatal: added block {} but could not set it as canonical -- aborting block import", block.header.number); - break; - }; - } - if let Some(last_block) = blocks.last() { - let hash = last_block.hash(); - apply_fork_choice(&store, hash, hash, hash).unwrap(); + import_blocks(&store, &blocks); + } + + if let Some(blocks_path) = matches.get_one::("import_dir") { + info!( + "Importing blocks from individual block files in directory: {}", + blocks_path + ); + let mut blocks = vec![]; + let dir_reader = fs::read_dir(blocks_path).expect("Failed to read blocks directory"); + for file_res in dir_reader { + let file = file_res.expect("Failed to open file in directory"); + let path = file.path(); + let s = path + .to_str() + .expect("Path could not be converted into string"); + blocks.push(read_block_file(s)); } - info!("Added {} blocks to blockchain", size); + + import_blocks(&store, &blocks); } + let jwt_secret = read_jwtsecret_file(authrpc_jwtsecret); // TODO Learn how should the key be created - // https://github.com/lambdaclass/lambda_ethereum_rust/issues/836 + // https://github.com/lambdaclass/lambda_ethrex/issues/836 //let signer = SigningKey::random(&mut OsRng); let key_bytes = H256::from_str("577d8278cc7748fad214b5378669b420f8221afb45ce930b7f22da49cbc545f3").unwrap(); @@ -185,7 +176,7 @@ async fn main() { // TODO: Check every module starts properly. let tracker = TaskTracker::new(); - let rpc_api = ethereum_rust_rpc::start_api( + let rpc_api = ethrex_rpc::start_api( http_socket_addr, authrpc_socket_addr, store.clone(), @@ -195,7 +186,7 @@ async fn main() { .into_future(); // TODO Find a proper place to show node information - // https://github.com/lambdaclass/lambda_ethereum_rust/issues/836 + // https://github.com/lambdaclass/lambda_ethrex/issues/836 let enode = local_p2p_node.enode_url(); info!("Node: {enode}"); @@ -204,10 +195,10 @@ async fn main() { // We do not want to start the networking module if the l2 feature is enabled. cfg_if::cfg_if! { if #[cfg(feature = "l2")] { - let l2_proposer = ethereum_rust_l2::start_proposer(store).into_future(); + let l2_proposer = ethrex_l2::start_proposer(store).into_future(); tracker.spawn(l2_proposer); } else if #[cfg(feature = "dev")] { - use ethereum_rust_dev; + use ethrex_dev; let authrpc_jwtsecret = std::fs::read(authrpc_jwtsecret).expect("Failed to read JWT secret"); let head_block_hash = { @@ -216,10 +207,10 @@ async fn main() { }; let max_tries = 3; let url = format!("http://{authrpc_socket_addr}"); - let block_producer_engine = ethereum_rust_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethereum_rust_core::Address::default()); + let block_producer_engine = ethrex_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethrex_core::Address::default()); tracker.spawn(block_producer_engine); } else { - let networking = ethereum_rust_net::start_network( + let networking = ethrex_net::start_network( udp_socket_addr, tcp_socket_addr, bootnodes, @@ -268,6 +259,13 @@ fn read_chain_file(chain_rlp_path: &str) -> Vec { decode::chain_file(chain_file).expect("Failed to decode chain rlp file") } +fn read_block_file(block_file_path: &str) -> Block { + let encoded_block = std::fs::read(block_file_path) + .unwrap_or_else(|_| panic!("Failed to read block file with path {}", block_file_path)); + Block::decode(&encoded_block) + .unwrap_or_else(|_| panic!("Failed to decode block file {}", block_file_path)) +} + fn read_genesis_file(genesis_file_path: &str) -> Genesis { let genesis_file = std::fs::File::open(genesis_file_path).expect("Failed to open genesis file"); decode::genesis_file(genesis_file).expect("Failed to decode genesis file") @@ -292,3 +290,43 @@ fn set_datadir(datadir: &str) -> String { .expect("invalid data directory") .to_owned() } + +fn import_blocks(store: &Store, blocks: &Vec) { + let size = blocks.len(); + for block in blocks { + let hash = block.hash(); + info!( + "Adding block {} with hash {:#x}.", + block.header.number, hash + ); + let result = add_block(block, store); + if let Some(error) = result.err() { + warn!( + "Failed to add block {} with hash {:#x}: {}.", + block.header.number, hash, error + ); + } + if store + .update_latest_block_number(block.header.number) + .is_err() + { + error!("Fatal: added block {} but could not update the block number -- aborting block import", block.header.number); + break; + }; + if store + .set_canonical_block(block.header.number, hash) + .is_err() + { + error!( + "Fatal: added block {} but could not set it as canonical -- aborting block import", + block.header.number + ); + break; + }; + } + if let Some(last_block) = blocks.last() { + let hash = last_block.hash(); + apply_fork_choice(store, hash, hash, hash).unwrap(); + } + info!("Added {} blocks to blockchain", size); +} diff --git a/cmd/ethereum_rust_l2/Cargo.toml b/cmd/ethrex_l2/Cargo.toml similarity index 71% rename from cmd/ethereum_rust_l2/Cargo.toml rename to cmd/ethrex_l2/Cargo.toml index 0cbcd346e..5b969aa03 100644 --- a/cmd/ethereum_rust_l2/Cargo.toml +++ b/cmd/ethrex_l2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust_l2_cli" +name = "ethrex_l2_cli" version.workspace = true edition.workspace = true @@ -26,12 +26,12 @@ strum = "0.26.3" secp256k1.workspace = true keccak-hash = "0.10.0" -ethereum_rust-l2.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-blockchain.workspace = true -ethereum_rust-prover.workspace = true -ethereum_rust-rlp.workspace = true -ethereum_rust-rpc.workspace = true +ethrex-l2.workspace = true +ethrex-core.workspace = true +ethrex-blockchain.workspace = true +ethrex-prover.workspace = true +ethrex-rlp.workspace = true +ethrex-rpc.workspace = true [[bin]] name = "l2" diff --git a/cmd/ethereum_rust_l2/README.md b/cmd/ethrex_l2/README.md similarity index 74% rename from cmd/ethereum_rust_l2/README.md rename to cmd/ethrex_l2/README.md index 6aefbbb92..7441580c1 100644 --- a/cmd/ethereum_rust_l2/README.md +++ b/cmd/ethrex_l2/README.md @@ -1,27 +1,28 @@ -# Ethereum Rust L2 CLI +# ethrex L2 CLI ## Table of Contents -- [Introduction](#introduction) -- [How to install](#how-to-install) -- [Commands](#commands) - - [`config`](#config) - - [`stack`](#stack) - - [`wallet`](#wallet) - - [`autocomplete`](#autocomplete) -- [Examples](#examples) +- [ethrex L2 CLI](#ethrex-l2-cli) + - [Table of Contents](#table-of-contents) + - [How to install](#how-to-install) + - [Commands](#commands) - [`config`](#config) - - [Adding a configuration](#adding-a-configuration) - - [Editing exiting configuration interactively](#editing-exiting-configuration-interactively) - - [Deleting existing configuration interactively](#deleting-existing-configuration-interactively) - - [Setting a configuration interactively](#setting-a-configuration-interactively) - [`stack`](#stack) - - [Initializing the stack](#initializing-the-stack) - - [Restarting the stack](#restarting-the-stack) + - [`wallet`](#wallet) + - [`autocomplete`](#autocomplete) + - [Examples](#examples) + - [`config`](#config-1) + - [Adding a configuration](#adding-a-configuration) + - [Editing exiting configuration interactively](#editing-exiting-configuration-interactively) + - [Deleting existing configuration interactively](#deleting-existing-configuration-interactively) + - [Setting a configuration interactively](#setting-a-configuration-interactively) + - [`stack`](#stack-1) + - [Initializing the stack](#initializing-the-stack) + - [Restarting the stack](#restarting-the-stack) ## How to install -Running the command below will install the `ethereum_rust_l2` binary in your system. +Running the command below will install the `ethrex_l2` binary in your system. ``` cargo install --path . @@ -30,7 +31,7 @@ cargo install --path . ## Commands ``` -Usage: ethereum_rust_l2 +Usage: ethrex_l2 Commands: stack Stack related commands. @@ -44,7 +45,7 @@ Options: -V, --version Print version ``` -> [!IMPORTANT] +> [!IMPORTANT] > Most of the CLI interaction needs a configuration to be set. You can set a configuration with the `config` command. ### `config` @@ -52,7 +53,7 @@ Options: ``` CLI config commands. -Usage: ethereum_rust_l2 config +Usage: ethrex_l2 config Commands: edit Edit an existing config. @@ -72,7 +73,7 @@ Options: ``` Stack related commands. -Usage: ethereum_rust_l2 stack +Usage: ethrex_l2 stack Commands: init Initializes the L2 network in the provided L1. [aliases: i] @@ -94,7 +95,7 @@ Options: ``` Wallet interaction commands. The configured wallet could operate both with the L1 and L2 networks. -Usage: ethereum_rust_l2 wallet +Usage: ethrex_l2 wallet Commands: balance Get the balance of the wallet. @@ -115,7 +116,7 @@ Options: ``` Generate shell completion scripts. -Usage: ethereum_rust_l2 autocomplete +Usage: ethrex_l2 autocomplete Commands: generate Generate autocomplete shell script. diff --git a/cmd/ethereum_rust_l2/assets/config_create.cast.gif b/cmd/ethrex_l2/assets/config_create.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/config_create.cast.gif rename to cmd/ethrex_l2/assets/config_create.cast.gif diff --git a/cmd/ethereum_rust_l2/assets/config_delete.cast.gif b/cmd/ethrex_l2/assets/config_delete.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/config_delete.cast.gif rename to cmd/ethrex_l2/assets/config_delete.cast.gif diff --git a/cmd/ethereum_rust_l2/assets/config_edit.cast.gif b/cmd/ethrex_l2/assets/config_edit.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/config_edit.cast.gif rename to cmd/ethrex_l2/assets/config_edit.cast.gif diff --git a/cmd/ethereum_rust_l2/assets/config_set.cast.gif b/cmd/ethrex_l2/assets/config_set.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/config_set.cast.gif rename to cmd/ethrex_l2/assets/config_set.cast.gif diff --git a/cmd/ethereum_rust_l2/assets/stack_init.cast.gif b/cmd/ethrex_l2/assets/stack_init.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/stack_init.cast.gif rename to cmd/ethrex_l2/assets/stack_init.cast.gif diff --git a/cmd/ethereum_rust_l2/assets/stack_restart.cast.gif b/cmd/ethrex_l2/assets/stack_restart.cast.gif similarity index 100% rename from cmd/ethereum_rust_l2/assets/stack_restart.cast.gif rename to cmd/ethrex_l2/assets/stack_restart.cast.gif diff --git a/cmd/ethereum_rust_l2/src/cli.rs b/cmd/ethrex_l2/src/cli.rs similarity index 62% rename from cmd/ethereum_rust_l2/src/cli.rs rename to cmd/ethrex_l2/src/cli.rs index 6933a1dfc..e4d7570a7 100644 --- a/cmd/ethereum_rust_l2/src/cli.rs +++ b/cmd/ethrex_l2/src/cli.rs @@ -7,14 +7,14 @@ use clap::{Parser, Subcommand}; pub const VERSION_STRING: &str = env!("CARGO_PKG_VERSION"); #[derive(Parser)] -#[command(name="ethereum_rust_l2_cli", author, version=VERSION_STRING, about, long_about = None)] -pub struct EthereumRustL2CLI { +#[command(name="Ethrex_l2_cli", author, version=VERSION_STRING, about, long_about = None)] +pub struct EthrexL2CLI { #[command(subcommand)] - command: EthereumRustL2Command, + command: EthrexL2Command, } #[derive(Subcommand)] -enum EthereumRustL2Command { +enum EthrexL2Command { #[clap(subcommand, about = "Stack related commands.")] Stack(stack::Command), #[clap( @@ -38,18 +38,18 @@ enum EthereumRustL2Command { } pub async fn start() -> eyre::Result<()> { - let EthereumRustL2CLI { command } = EthereumRustL2CLI::parse(); - if let EthereumRustL2Command::Config(cmd) = command { + let EthrexL2CLI { command } = EthrexL2CLI::parse(); + if let EthrexL2Command::Config(cmd) = command { return cmd.run().await; } let cfg = load_selected_config().await?; match command { - EthereumRustL2Command::Stack(cmd) => cmd.run(cfg).await?, - EthereumRustL2Command::Wallet(cmd) => cmd.run(cfg).await?, - EthereumRustL2Command::Utils(cmd) => cmd.run().await?, - EthereumRustL2Command::Autocomplete(cmd) => cmd.run()?, - EthereumRustL2Command::Config(_) => unreachable!(), - EthereumRustL2Command::Test(cmd) => cmd.run(cfg).await?, + EthrexL2Command::Stack(cmd) => cmd.run(cfg).await?, + EthrexL2Command::Wallet(cmd) => cmd.run(cfg).await?, + EthrexL2Command::Utils(cmd) => cmd.run().await?, + EthrexL2Command::Autocomplete(cmd) => cmd.run()?, + EthrexL2Command::Config(_) => unreachable!(), + EthrexL2Command::Test(cmd) => cmd.run(cfg).await?, }; Ok(()) } diff --git a/cmd/ethereum_rust_l2/src/commands/autocomplete.rs b/cmd/ethrex_l2/src/commands/autocomplete.rs similarity index 85% rename from cmd/ethereum_rust_l2/src/commands/autocomplete.rs rename to cmd/ethrex_l2/src/commands/autocomplete.rs index a227d75f7..c98fbc1c7 100644 --- a/cmd/ethereum_rust_l2/src/commands/autocomplete.rs +++ b/cmd/ethrex_l2/src/commands/autocomplete.rs @@ -1,4 +1,4 @@ -use crate::cli::EthereumRustL2CLI; +use crate::cli::EthrexL2CLI; use clap::{CommandFactory, Subcommand, ValueEnum}; use clap_complete::{aot::Shell, generate}; use std::fs::{File, OpenOptions}; @@ -58,8 +58,8 @@ fn generate_bash_script(shell_arg: Option) -> eyre::Result<()> { let shell = get_shell(shell_arg)?; generate( shell, - &mut EthereumRustL2CLI::command(), - "ethereum_rust_l2", + &mut EthrexL2CLI::command(), + "ethrex_l2", &mut io::stdout(), ); Ok(()) @@ -67,9 +67,9 @@ fn generate_bash_script(shell_arg: Option) -> eyre::Result<()> { fn shellrc_command_exists(shellrc_path: &std::path::Path, shell: Shell) -> eyre::Result { let expected_string = if shell == Shell::Elvish { - "-source $HOME/.ethereum-rust-l2-completion" + "-source $HOME/.ethrex-l2-completion" } else { - ". $HOME/.ethereum-rust-l2-completion" + ". $HOME/.ethrex-l2-completion" }; let file = File::open(shellrc_path)?; @@ -90,14 +90,9 @@ fn install_bash_script(shell_arg: Option) -> eyre::Result<()> { let file_path = dirs::home_dir() .ok_or(eyre::eyre!("Cannot find home directory."))? - .join(".ethereum-rust-l2-completion"); + .join(".ethrex-l2-completion"); let mut file = File::create(file_path)?; - generate( - shell, - &mut EthereumRustL2CLI::command(), - "ethereum_rust_l2", - &mut file, - ); + generate(shell, &mut EthrexL2CLI::command(), "ethrex_l2", &mut file); file.flush()?; let shellrc_path = dirs::home_dir() @@ -107,9 +102,9 @@ fn install_bash_script(shell_arg: Option) -> eyre::Result<()> { if !shellrc_command_exists(&shellrc_path, shell)? { let mut file = OpenOptions::new().append(true).open(shellrc_path)?; if shell == Shell::Elvish { - file.write_all(b"\n-source $HOME/.ethereum-rust-l2-completion\n")?; + file.write_all(b"\n-source $HOME/.ethrex-l2-completion\n")?; } else { - file.write_all(b"\n. $HOME/.ethereum-rust-l2-completion\n")?; + file.write_all(b"\n. $HOME/.ethrex-l2-completion\n")?; } file.flush()?; } diff --git a/cmd/ethereum_rust_l2/src/commands/config.rs b/cmd/ethrex_l2/src/commands/config.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/commands/config.rs rename to cmd/ethrex_l2/src/commands/config.rs diff --git a/cmd/ethereum_rust_l2/src/commands/mod.rs b/cmd/ethrex_l2/src/commands/mod.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/commands/mod.rs rename to cmd/ethrex_l2/src/commands/mod.rs diff --git a/cmd/ethereum_rust_l2/src/commands/stack.rs b/cmd/ethrex_l2/src/commands/stack.rs similarity index 89% rename from cmd/ethereum_rust_l2/src/commands/stack.rs rename to cmd/ethrex_l2/src/commands/stack.rs index 13285c474..567708e02 100644 --- a/cmd/ethereum_rust_l2/src/commands/stack.rs +++ b/cmd/ethrex_l2/src/commands/stack.rs @@ -1,4 +1,4 @@ -use crate::{config::EthereumRustL2Config, utils::config::confirm}; +use crate::{config::EthrexL2Config, utils::config::confirm}; use clap::Subcommand; use eyre::ContextCompat; use secp256k1::SecretKey; @@ -68,13 +68,13 @@ pub(crate) enum Command { } impl Command { - pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { let root = std::path::Path::new(CARGO_MANIFEST_DIR) .parent() .map(std::path::Path::parent) .context("Failed to get parent")? .context("Failed to get grandparent")?; - let ethereum_rust_dev_path = root.join("crates/blockchain/dev"); + let ethrex_dev_path = root.join("crates/blockchain/dev"); let l2_crate_path = root.join("crates/l2"); let contracts_path = l2_crate_path.join("contracts"); @@ -90,7 +90,7 @@ impl Command { // or in a testnet. If the L1 RPC URL is localhost, then it is // a local environment and the local node needs to be started. if l1_rpc_url.contains("localhost") { - start_l1(&l2_crate_path, ðereum_rust_dev_path).await?; + start_l1(&l2_crate_path, ðrex_dev_path).await?; tokio::time::sleep(std::time::Duration::from_secs(1)).await; } if !skip_l1_deployment { @@ -101,7 +101,7 @@ impl Command { Command::Shutdown { l1, l2, force } => { if force || (l1 && confirm("Are you sure you want to shutdown the local L1 node?")?) { - shutdown_l1(ðereum_rust_dev_path)?; + shutdown_l1(ðrex_dev_path)?; } if force || (l2 && confirm("Are you sure you want to shutdown the L2 node?")?) { shutdown_l2()?; @@ -114,7 +114,7 @@ impl Command { start_prover, } => { if force || l1 { - start_l1(&l2_crate_path, ðereum_rust_dev_path).await?; + start_l1(&l2_crate_path, ðrex_dev_path).await?; } if force || l2 { start_l2(root.to_path_buf(), &l2_rpc_url, start_prover).await?; @@ -200,14 +200,14 @@ fn deploy_l1( Ok(()) } -fn shutdown_l1(ethereum_rust_dev_path: &Path) -> eyre::Result<()> { - let local_l1_docker_compose_path = ethereum_rust_dev_path.join("docker-compose-dev.yaml"); +fn shutdown_l1(ethrex_dev_path: &Path) -> eyre::Result<()> { + let local_l1_docker_compose_path = ethrex_dev_path.join("docker-compose-dev.yaml"); let cmd = std::process::Command::new("docker") .arg("compose") .arg("-f") .arg(local_l1_docker_compose_path) .arg("down") - .current_dir(ethereum_rust_dev_path) + .current_dir(ethrex_dev_path) .spawn()? .wait()?; if !cmd.success() { @@ -219,15 +219,15 @@ fn shutdown_l1(ethereum_rust_dev_path: &Path) -> eyre::Result<()> { fn shutdown_l2() -> eyre::Result<()> { std::process::Command::new("pkill") .arg("-f") - .arg("ethereum_rust") + .arg("ethrex") .spawn()? .wait()?; Ok(()) } -async fn start_l1(l2_crate_path: &Path, ethereum_rust_dev_path: &Path) -> eyre::Result<()> { +async fn start_l1(l2_crate_path: &Path, ethrex_dev_path: &Path) -> eyre::Result<()> { create_volumes(l2_crate_path)?; - docker_compose_l2_up(ethereum_rust_dev_path)?; + docker_compose_l2_up(ethrex_dev_path)?; Ok(()) } @@ -237,15 +237,15 @@ fn create_volumes(l2_crate_path: &Path) -> eyre::Result<()> { Ok(()) } -fn docker_compose_l2_up(ethereum_rust_dev_path: &Path) -> eyre::Result<()> { - let local_l1_docker_compose_path = ethereum_rust_dev_path.join("docker-compose-dev.yaml"); +fn docker_compose_l2_up(ethrex_dev_path: &Path) -> eyre::Result<()> { + let local_l1_docker_compose_path = ethrex_dev_path.join("docker-compose-dev.yaml"); let cmd = std::process::Command::new("docker") .arg("compose") .arg("-f") .arg(local_l1_docker_compose_path) .arg("up") .arg("-d") - .current_dir(ethereum_rust_dev_path) + .current_dir(ethrex_dev_path) .spawn()? .wait()?; if !cmd.success() { @@ -264,7 +264,7 @@ async fn start_l2(root: PathBuf, l2_rpc_url: &str, start_prover: bool) -> eyre:: .arg("run") .arg("--release") .arg("--bin") - .arg("ethereum_rust") + .arg("ethrex") .arg("--features") .arg("l2") .arg("--") @@ -293,7 +293,7 @@ async fn start_l2(root: PathBuf, l2_rpc_url: &str, start_prover: bool) -> eyre:: .arg("--features") .arg("build_zkvm") .arg("--bin") - .arg("ethereum_rust_prover") + .arg("ethrex_prover") .current_dir(root_clone) .status(); diff --git a/cmd/ethereum_rust_l2/src/commands/test.rs b/cmd/ethrex_l2/src/commands/test.rs similarity index 93% rename from cmd/ethereum_rust_l2/src/commands/test.rs rename to cmd/ethrex_l2/src/commands/test.rs index 3e78b1631..ae8e90ef0 100644 --- a/cmd/ethereum_rust_l2/src/commands/test.rs +++ b/cmd/ethrex_l2/src/commands/test.rs @@ -1,9 +1,9 @@ -use crate::config::EthereumRustL2Config; +use crate::config::EthrexL2Config; use bytes::Bytes; use clap::Subcommand; -use ethereum_rust_blockchain::constants::TX_GAS_COST; -use ethereum_rust_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use ethereum_types::{Address, H160, H256, U256}; +use ethrex_blockchain::constants::TX_GAS_COST; +use ethrex_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; use secp256k1::SecretKey; use std::{ @@ -67,7 +67,7 @@ async fn transfer_from( value: U256, iterations: u64, verbose: bool, - cfg: EthereumRustL2Config, + cfg: EthrexL2Config, ) -> u64 { let client = EthClient::new(&cfg.network.l2_rpc_url); let private_key = SecretKey::from_slice(pk.parse::().unwrap().as_bytes()).unwrap(); @@ -117,7 +117,7 @@ async fn transfer_from( } impl Command { - pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { match self { Command::Load { path, diff --git a/cmd/ethereum_rust_l2/src/commands/utils.rs b/cmd/ethrex_l2/src/commands/utils.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/commands/utils.rs rename to cmd/ethrex_l2/src/commands/utils.rs diff --git a/cmd/ethereum_rust_l2/src/commands/wallet.rs b/cmd/ethrex_l2/src/commands/wallet.rs similarity index 98% rename from cmd/ethereum_rust_l2/src/commands/wallet.rs rename to cmd/ethrex_l2/src/commands/wallet.rs index d9de3f43e..fdd6f0c6d 100644 --- a/cmd/ethereum_rust_l2/src/commands/wallet.rs +++ b/cmd/ethrex_l2/src/commands/wallet.rs @@ -1,13 +1,13 @@ -use crate::{commands::utils::encode_calldata, config::EthereumRustL2Config}; +use crate::{commands::utils::encode_calldata, config::EthrexL2Config}; use bytes::Bytes; use clap::Subcommand; -use ethereum_rust_core::types::{PrivilegedTxType, Transaction}; -use ethereum_rust_l2::utils::{ +use ethereum_types::{Address, H256, U256}; +use ethrex_core::types::{PrivilegedTxType, Transaction}; +use ethrex_l2::utils::{ eth_client::{eth_sender::Overrides, EthClient}, merkle_tree::merkle_proof, }; -use ethereum_rust_rpc::types::block::BlockBodyWrapper; -use ethereum_types::{Address, H256, U256}; +use ethrex_rpc::types::block::BlockBodyWrapper; use eyre::OptionExt; use hex::FromHexError; use itertools::Itertools; @@ -251,7 +251,7 @@ async fn get_withdraw_merkle_proof( } impl Command { - pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { let eth_client = EthClient::new(&cfg.network.l1_rpc_url); let rollup_client = EthClient::new(&cfg.network.l2_rpc_url); let from = cfg.wallet.address; diff --git a/cmd/ethereum_rust_l2/src/config.rs b/cmd/ethrex_l2/src/config.rs similarity index 87% rename from cmd/ethereum_rust_l2/src/config.rs rename to cmd/ethrex_l2/src/config.rs index 523febdcf..c568c2877 100644 --- a/cmd/ethereum_rust_l2/src/config.rs +++ b/cmd/ethrex_l2/src/config.rs @@ -11,7 +11,7 @@ use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone)] -pub struct EthereumRustL2Config { +pub struct EthrexL2Config { pub network: NetworkConfig, pub wallet: WalletConfig, pub contracts: ContractsConfig, @@ -31,8 +31,8 @@ pub struct NetworkConfig { pub struct WalletConfig { pub address: Address, #[serde( - serialize_with = "ethereum_rust_l2::utils::secret_key_serializer", - deserialize_with = "ethereum_rust_l2::utils::secret_key_deserializer" + serialize_with = "ethrex_l2::utils::secret_key_serializer", + deserialize_with = "ethrex_l2::utils::secret_key_deserializer" )] pub private_key: SecretKey, } @@ -42,7 +42,7 @@ pub struct ContractsConfig { pub common_bridge: Address, } -pub async fn try_load_selected_config() -> eyre::Result> { +pub async fn try_load_selected_config() -> eyre::Result> { let config_path = selected_config_path()?; if !config_path.exists() { return Ok(None); @@ -53,7 +53,7 @@ pub async fn try_load_selected_config() -> eyre::Result eyre::Result { +pub async fn load_selected_config() -> eyre::Result { let config_path = selected_config_path()?; if !config_path.exists() { println!("No config set, please select a config to set"); diff --git a/cmd/ethereum_rust_l2/src/lib.rs b/cmd/ethrex_l2/src/lib.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/lib.rs rename to cmd/ethrex_l2/src/lib.rs diff --git a/cmd/ethereum_rust_l2/src/main.rs b/cmd/ethrex_l2/src/main.rs similarity index 90% rename from cmd/ethereum_rust_l2/src/main.rs rename to cmd/ethrex_l2/src/main.rs index c8ea06604..bc50c9ae8 100644 --- a/cmd/ethereum_rust_l2/src/main.rs +++ b/cmd/ethrex_l2/src/main.rs @@ -1,4 +1,4 @@ -use ethereum_rust_l2_cli::cli; +use ethrex_l2_cli::cli; #[tokio::main] async fn main() { diff --git a/cmd/ethereum_rust_l2/src/utils/config/default_values.rs b/cmd/ethrex_l2/src/utils/config/default_values.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/utils/config/default_values.rs rename to cmd/ethrex_l2/src/utils/config/default_values.rs diff --git a/cmd/ethereum_rust_l2/src/utils/config/mod.rs b/cmd/ethrex_l2/src/utils/config/mod.rs similarity index 90% rename from cmd/ethereum_rust_l2/src/utils/config/mod.rs rename to cmd/ethrex_l2/src/utils/config/mod.rs index 9177ee225..e02e1ea1b 100644 --- a/cmd/ethereum_rust_l2/src/utils/config/mod.rs +++ b/cmd/ethrex_l2/src/utils/config/mod.rs @@ -1,6 +1,6 @@ use crate::{ commands::{self, config::EditConfigOpts}, - config::{ContractsConfig, EthereumRustL2Config, NetworkConfig, WalletConfig}, + config::{ContractsConfig, EthrexL2Config, NetworkConfig, WalletConfig}, utils::messages::{ ADDRESS_PROMPT_MSG, CONFIG_CREATE_PROMPT_MSG, CONFIG_EDIT_PROMPT_MSG, L1_CHAIN_ID_PROMPT_MSG, L1_EXPLORER_URL_PROMPT_MSG, L1_RPC_URL_PROMPT_MSG, @@ -28,7 +28,7 @@ pub const SELECTED_CONFIG_FILE_NAME: &str = ".selected"; pub fn configs_dir_path() -> eyre::Result { let configs_dir_path = dirs::config_dir() .ok_or_else(|| eyre::eyre!("Could not find user's config directory"))? - .join("ethereum-rust-l2-cli") + .join("Ethrex-l2-cli") .join("configs"); if !configs_dir_path.exists() { std::fs::create_dir_all(&configs_dir_path)?; @@ -95,8 +95,8 @@ pub fn config_path_interactive_selection(prompt: &str) -> eyre::Result config_path(configs.get(selection).context("No config selected")?) } -pub fn prompt_config() -> eyre::Result { - let prompted_config = EthereumRustL2Config { +pub fn prompt_config() -> eyre::Result { + let prompted_config = EthrexL2Config { network: NetworkConfig { l1_rpc_url: prompt(L1_RPC_URL_PROMPT_MSG, DEFAULT_L1_RPC_URL.into())?, l1_chain_id: prompt(L1_CHAIN_ID_PROMPT_MSG, DEFAULT_L1_CHAIN_ID)?, @@ -149,11 +149,8 @@ pub fn selected_config_path() -> eyre::Result { Ok(configs_dir_path()?.join(SELECTED_CONFIG_FILE_NAME)) } -pub fn edit_config_by_name_interactively( - config_path: &PathBuf, -) -> eyre::Result { - let existing_config: EthereumRustL2Config = - toml::from_str(&std::fs::read_to_string(config_path)?)?; +pub fn edit_config_by_name_interactively(config_path: &PathBuf) -> eyre::Result { + let existing_config: EthrexL2Config = toml::from_str(&std::fs::read_to_string(config_path)?)?; let new_config = edit_existing_config_interactively(existing_config)?; Ok(new_config) } @@ -161,16 +158,15 @@ pub fn edit_config_by_name_interactively( pub fn edit_config_by_name_with_args( config_path: &PathBuf, opts: EditConfigOpts, -) -> eyre::Result { - let existing_config: EthereumRustL2Config = - toml::from_str(&std::fs::read_to_string(config_path)?)?; +) -> eyre::Result { + let existing_config: EthrexL2Config = toml::from_str(&std::fs::read_to_string(config_path)?)?; let new_config = edit_existing_config_non_interactively(existing_config, opts)?; Ok(new_config) } -pub fn edit_config_interactively() -> eyre::Result<(EthereumRustL2Config, PathBuf)> { +pub fn edit_config_interactively() -> eyre::Result<(EthrexL2Config, PathBuf)> { let config_path = config_path_interactive_selection(CONFIG_EDIT_PROMPT_MSG)?; - let existing_config: EthereumRustL2Config = + let existing_config: EthrexL2Config = toml::from_str(&std::fs::read_to_string(config_path.clone())?)?; let new_config = edit_existing_config_interactively(existing_config)?; Ok((new_config, config_path)) @@ -195,9 +191,9 @@ pub async fn set_new_config(config_path: PathBuf) -> eyre::Result<()> { } pub fn edit_existing_config_interactively( - existing_config: EthereumRustL2Config, -) -> eyre::Result { - let config = EthereumRustL2Config { + existing_config: EthrexL2Config, +) -> eyre::Result { + let config = EthrexL2Config { network: NetworkConfig { l1_rpc_url: prompt(L1_RPC_URL_PROMPT_MSG, existing_config.network.l1_rpc_url)?, l1_chain_id: prompt(L1_CHAIN_ID_PROMPT_MSG, existing_config.network.l1_chain_id)?, @@ -236,10 +232,10 @@ pub fn edit_existing_config_interactively( } pub fn edit_existing_config_non_interactively( - existing_config: EthereumRustL2Config, + existing_config: EthrexL2Config, opts: EditConfigOpts, -) -> eyre::Result { - let config = EthereumRustL2Config { +) -> eyre::Result { + let config = EthrexL2Config { network: NetworkConfig { l1_rpc_url: opts .l1_rpc_url diff --git a/cmd/ethereum_rust_l2/src/utils/messages.rs b/cmd/ethrex_l2/src/utils/messages.rs similarity index 97% rename from cmd/ethereum_rust_l2/src/utils/messages.rs rename to cmd/ethrex_l2/src/utils/messages.rs index 5893a69a0..ee86c3653 100644 --- a/cmd/ethereum_rust_l2/src/utils/messages.rs +++ b/cmd/ethrex_l2/src/utils/messages.rs @@ -1,4 +1,4 @@ -// `ethereum_rust_l2 config` messages +// `ethrex_l2 config` messages pub const CONFIG_OVERRIDE_PROMPT_MSG: &str = "Config already exists. Do you want to overwrite it?"; pub const CONFIG_CREATE_PROMPT_MSG: &str = "This config does not exist. Do you want to create it?"; pub const CONFIG_CREATE_NAME_PROMPT_MSG: &str = "What do you want to name the new config?"; diff --git a/cmd/ethereum_rust_l2/src/utils/mod.rs b/cmd/ethrex_l2/src/utils/mod.rs similarity index 100% rename from cmd/ethereum_rust_l2/src/utils/mod.rs rename to cmd/ethrex_l2/src/utils/mod.rs diff --git a/cmd/hive_report/Cargo.toml b/cmd/hive_report/Cargo.toml new file mode 100644 index 000000000..30ac15e18 --- /dev/null +++ b/cmd/hive_report/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "hive_report" +version.workspace = true +edition.workspace = true + +[dependencies] +serde_json.workspace = true +serde.workspace = true diff --git a/cmd/hive_report/src/main.rs b/cmd/hive_report/src/main.rs new file mode 100644 index 000000000..b367aa597 --- /dev/null +++ b/cmd/hive_report/src/main.rs @@ -0,0 +1,69 @@ +use serde::Deserialize; +use std::fs::{self, File}; +use std::io::BufReader; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct TestCase { + summary_result: SummaryResult, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct SummaryResult { + pass: bool, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct JsonFile { + name: String, + test_cases: std::collections::HashMap, +} + +fn main() -> Result<(), Box> { + let mut results = Vec::new(); + + for entry in fs::read_dir("hive/workspace/logs")? { + let entry = entry?; + let path = entry.path(); + + if path.is_file() + && path.extension().and_then(|s| s.to_str()) == Some("json") + && path.file_name().and_then(|s| s.to_str()) != Some("hive.json") + { + let file_name = path + .file_name() + .and_then(|s| s.to_str()) + .expect("Path should be a valid string"); + let file = File::open(&path)?; + let reader = BufReader::new(file); + + let json_data: JsonFile = match serde_json::from_reader(reader) { + Ok(data) => data, + Err(_) => { + eprintln!("Error processing file: {}", file_name); + continue; + } + }; + + let total_tests = json_data.test_cases.len(); + let passed_tests = json_data + .test_cases + .values() + .filter(|test_case| test_case.summary_result.pass) + .count(); + + results.push((json_data.name, passed_tests, total_tests)); + } + } + + // Sort by file name. + results.sort_by(|a, b| a.0.cmp(&b.0)); + + for (file_name, passed, total) in results { + println!("{}: {}/{}", file_name, passed, total); + } + + Ok(()) +} diff --git a/cmd/loc/Cargo.toml b/cmd/loc/Cargo.toml new file mode 100644 index 000000000..55addc9d9 --- /dev/null +++ b/cmd/loc/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "loc" +version.workspace = true +edition.workspace = true + +[dependencies] +tokei = "12.1.2" +colored = "2.1.0" diff --git a/cmd/loc/src/main.rs b/cmd/loc/src/main.rs new file mode 100644 index 000000000..c2a8eb30f --- /dev/null +++ b/cmd/loc/src/main.rs @@ -0,0 +1,36 @@ +use colored::Colorize; +use std::path::PathBuf; +use tokei::{Config, LanguageType, Languages}; + +const CARGO_MANIFEST_DIR: &str = std::env!("CARGO_MANIFEST_DIR"); + +fn main() { + let ethrex = PathBuf::from(CARGO_MANIFEST_DIR).join("../../"); + let levm = PathBuf::from(CARGO_MANIFEST_DIR).join("../../crates/vm"); + let ethrex_l2 = PathBuf::from(CARGO_MANIFEST_DIR).join("../../crates/l2"); + + let config = Config::default(); + + let mut languages = Languages::new(); + languages.get_statistics(&[ethrex.clone()], &[], &config); + let ethrex_loc = &languages.get(&LanguageType::Rust).unwrap(); + + let mut languages = Languages::new(); + languages.get_statistics(&[levm], &[], &config); + let levm_loc = &languages.get(&LanguageType::Rust).unwrap(); + + let mut languages = Languages::new(); + languages.get_statistics(&[ethrex_l2], &[], &config); + let ethrex_l2_loc = &languages.get(&LanguageType::Rust).unwrap(); + + println!("{}", "ethrex loc summary".bold()); + println!("{}", "====================".bold()); + println!( + "{}: {:?}", + "ethrex L1".bold(), + ethrex_loc.code - ethrex_l2_loc.code - levm_loc.code + ); + println!("{}: {:?}", "ethrex L2".bold(), ethrex_l2_loc.code); + println!("{}: {:?}", "levm".bold(), levm_loc.code); + println!("{}: {:?}", "ethrex (total)".bold(), ethrex_loc.code); +} diff --git a/crates/blockchain/Cargo.toml b/crates/blockchain/Cargo.toml index 952472caa..1c81c4533 100644 --- a/crates/blockchain/Cargo.toml +++ b/crates/blockchain/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust-blockchain" +name = "ethrex-blockchain" version = "0.1.0" edition = "2021" @@ -12,10 +12,10 @@ tracing.workspace = true bytes.workspace = true cfg-if = "1.0.0" -ethereum_rust-rlp.workspace = true -ethereum_rust-core = { path = "../common", default-features = false } -ethereum_rust-storage = { path = "../storage/store", default-features = false } -ethereum_rust-vm = { path = "../vm", default-features = false } +ethrex-rlp.workspace = true +ethrex-core = { path = "../common", default-features = false } +ethrex-storage = { path = "../storage/store", default-features = false } +ethrex-vm = { path = "../vm", default-features = false } k256 = { version = "0.13.3", features = ["ecdh"] } @@ -27,10 +27,11 @@ hex = "0.4.3" path = "./blockchain.rs" [features] -default = ["libmdbx"] +default = ["libmdbx", "c-kzg"] libmdbx = [ - "ethereum_rust-core/libmdbx", - "ethereum_rust-storage/default", - "ethereum_rust-vm/libmdbx", + "ethrex-core/libmdbx", + "ethrex-storage/default", + "ethrex-vm/libmdbx", ] -levm = ["ethereum_rust-vm/levm"] +levm = ["ethrex-vm/levm"] +c-kzg =["ethrex-core/c-kzg"] diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 6f85420a1..6b1aab8f6 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -7,15 +7,15 @@ mod smoke_test; use constants::{GAS_PER_BLOB, MAX_BLOB_GAS_PER_BLOCK, MAX_BLOB_NUMBER_PER_BLOCK}; use error::{ChainError, InvalidBlockError}; -use ethereum_rust_core::types::{ +use ethrex_core::types::{ validate_block_header, validate_cancun_header_fields, validate_no_cancun_header_fields, Block, BlockHash, BlockHeader, BlockNumber, EIP4844Transaction, Receipt, Transaction, }; -use ethereum_rust_core::H256; +use ethrex_core::H256; -use ethereum_rust_storage::error::StoreError; -use ethereum_rust_storage::Store; -use ethereum_rust_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; +use ethrex_storage::error::StoreError; +use ethrex_storage::Store; +use ethrex_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; //TODO: Implement a struct Chain or BlockChain to encapsulate //functionality and canonical chain state and config @@ -27,7 +27,7 @@ use ethereum_rust_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; /// Performs pre and post execution validation, and updates the database with the post state. #[cfg(not(feature = "levm"))] pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> { - use ethereum_rust_vm::get_state_transitions; + use ethrex_vm::get_state_transitions; let block_hash = block.header.compute_block_hash(); diff --git a/crates/blockchain/constants.rs b/crates/blockchain/constants.rs index 0b19af442..651c0b038 100644 --- a/crates/blockchain/constants.rs +++ b/crates/blockchain/constants.rs @@ -59,5 +59,3 @@ pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; pub const GAS_LIMIT_BOUND_DIVISOR: u64 = 1024; pub const MIN_GAS_LIMIT: u64 = 5000; - -pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; diff --git a/crates/blockchain/dev/Cargo.toml b/crates/blockchain/dev/Cargo.toml index a4601cd16..919488b9e 100644 --- a/crates/blockchain/dev/Cargo.toml +++ b/crates/blockchain/dev/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "ethereum_rust-dev" +name = "ethrex-dev" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum_rust-rpc.workspace = true +ethrex-rpc.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/crates/blockchain/dev/Dockerfile b/crates/blockchain/dev/Dockerfile index af091f266..246c958a3 100644 --- a/crates/blockchain/dev/Dockerfile +++ b/crates/blockchain/dev/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethereum_rust +WORKDIR /ethrex FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethereum_rust/recipe.json recipe.json +COPY --from=planner /ethrex/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,6 +27,6 @@ RUN cargo build --release --features dev FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethereum_rust/target/release/ethereum_rust . +COPY --from=builder ethrex/target/release/ethrex . EXPOSE 8545 -ENTRYPOINT [ "./ethereum_rust" ] +ENTRYPOINT [ "./ethrex" ] diff --git a/crates/blockchain/dev/block_producer.rs b/crates/blockchain/dev/block_producer.rs index f9ef46853..7cbe818e7 100644 --- a/crates/blockchain/dev/block_producer.rs +++ b/crates/blockchain/dev/block_producer.rs @@ -1,7 +1,7 @@ use crate::utils::engine_client::{errors::EngineClientError, EngineClient}; use bytes::Bytes; -use ethereum_rust_rpc::types::fork_choice::{ForkChoiceState, PayloadAttributesV3}; use ethereum_types::{Address, H256}; +use ethrex_rpc::types::fork_choice::{ForkChoiceState, PayloadAttributesV3}; use sha2::{Digest, Sha256}; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/crates/blockchain/dev/docker-compose-dev.yaml b/crates/blockchain/dev/docker-compose-dev.yaml index de952a73e..b95dce07c 100644 --- a/crates/blockchain/dev/docker-compose-dev.yaml +++ b/crates/blockchain/dev/docker-compose-dev.yaml @@ -1,8 +1,8 @@ services: - ethereum_rust: + ethrex: restart: always - container_name: ethereum_rust_l1 - image: "ethereum_rust_dev" + container_name: ethrex_l1 + image: "ethrex_dev" build: context: ../../../ dockerfile: ./crates/blockchain/dev/Dockerfile diff --git a/crates/blockchain/dev/utils/engine_client/mod.rs b/crates/blockchain/dev/utils/engine_client/mod.rs index 551bdb286..47dd69f55 100644 --- a/crates/blockchain/dev/utils/engine_client/mod.rs +++ b/crates/blockchain/dev/utils/engine_client/mod.rs @@ -3,7 +3,8 @@ use errors::{ EngineClientError, ExchangeCapabilitiesError, ForkChoiceUpdateError, GetPayloadError, NewPayloadError, }; -use ethereum_rust_rpc::{ +use ethereum_types::H256; +use ethrex_rpc::{ engine::{ fork_choice::ForkChoiceUpdatedV3, payload::{GetPayloadV3Request, NewPayloadV3Request}, @@ -15,7 +16,6 @@ use ethereum_rust_rpc::{ }, utils::{RpcErrorResponse, RpcRequest, RpcSuccessResponse}, }; -use ethereum_types::H256; use reqwest::Client; use serde::Deserialize; use serde_json::json; diff --git a/crates/blockchain/error.rs b/crates/blockchain/error.rs index 050064e25..8a16ab224 100644 --- a/crates/blockchain/error.rs +++ b/crates/blockchain/error.rs @@ -1,6 +1,6 @@ -use ethereum_rust_core::types::InvalidBlockHeaderError; -use ethereum_rust_storage::error::StoreError; -use ethereum_rust_vm::EvmError; +use ethrex_core::types::{BlobsBundleError, InvalidBlockHeaderError}; +use ethrex_storage::error::StoreError; +use ethrex_vm::EvmError; #[derive(Debug, thiserror::Error)] pub enum ChainError { @@ -42,6 +42,8 @@ pub enum MempoolError { NoBlockHeaderError, #[error("DB error: {0}")] StoreError(#[from] StoreError), + #[error("BlobsBundle error: {0}")] + BlobsBundleError(#[from] BlobsBundleError), #[error("Transaction max init code size exceeded")] TxMaxInitCodeSizeError, #[error("Transaction gas limit exceeded")] @@ -56,8 +58,6 @@ pub enum MempoolError { TxBlobBaseFeeTooLowError, #[error("Blob transaction submited without blobs bundle")] BlobTxNoBlobsBundle, - #[error("Mismatch between blob versioned hashes and blobs bundle content length")] - BlobsBundleWrongLen, #[error("Nonce for account too low")] InvalidNonce, #[error("Transaction chain id mismatch, expected chain id: {0}")] @@ -66,8 +66,6 @@ pub enum MempoolError { NotEnoughBalance, #[error("Transaction gas fields are invalid")] InvalidTxGasvalues, - #[error("Blob versioned hashes do not match the blob commitments")] - BlobVersionedHashesIncorrectError, } #[derive(Debug)] diff --git a/crates/blockchain/fork_choice.rs b/crates/blockchain/fork_choice.rs index 3514d35cb..46ba0ed95 100644 --- a/crates/blockchain/fork_choice.rs +++ b/crates/blockchain/fork_choice.rs @@ -1,8 +1,8 @@ -use ethereum_rust_core::{ +use ethrex_core::{ types::{Block, BlockHash, BlockHeader, BlockNumber}, H256, }; -use ethereum_rust_storage::{error::StoreError, Store}; +use ethrex_storage::{error::StoreError, Store}; use crate::{ error::{self, InvalidForkChoice}, diff --git a/crates/blockchain/mempool.rs b/crates/blockchain/mempool.rs index 485b9b283..aa566f239 100644 --- a/crates/blockchain/mempool.rs +++ b/crates/blockchain/mempool.rs @@ -5,26 +5,27 @@ use crate::{ MAX_INITCODE_SIZE, MIN_BASE_FEE_PER_BLOB_GAS, TX_ACCESS_LIST_ADDRESS_GAS, TX_ACCESS_LIST_STORAGE_KEY_GAS, TX_CREATE_GAS_COST, TX_DATA_NON_ZERO_GAS, TX_DATA_NON_ZERO_GAS_EIP2028, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, - TX_INIT_CODE_WORD_GAS_COST, VERSIONED_HASH_VERSION_KZG, + TX_INIT_CODE_WORD_GAS_COST, }, error::MempoolError, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{ BlobsBundle, BlockHeader, ChainConfig, EIP4844Transaction, MempoolTransaction, Transaction, }, Address, H256, U256, }; -use ethereum_rust_storage::{error::StoreError, Store}; +use ethrex_storage::{error::StoreError, Store}; /// Add a blob transaction and its blobs bundle to the mempool +#[cfg(feature = "c-kzg")] pub fn add_blob_transaction( transaction: EIP4844Transaction, blobs_bundle: BlobsBundle, store: Store, ) -> Result { // Validate blobs bundle - validate_blobs_bundle(&transaction, &blobs_bundle)?; + blobs_bundle.validate(&transaction)?; // Validate transaction let transaction = Transaction::EIP4844Transaction(transaction); @@ -81,7 +82,7 @@ pub fn filter_transactions( return false; } // This is a temporary fix to avoid invalid transactions to be included. - // This should be removed once https://github.com/lambdaclass/ethereum_rust/issues/680 + // This should be removed once https://github.com/lambdaclass/ethrex/issues/680 // is addressed. } else if tx.effective_gas_tip(filter.base_fee).is_none() { return false; @@ -141,16 +142,16 @@ Stateless validations 3. Transaction's encoded size is smaller than maximum allowed -> I think that this is not in the spec, but it may be a good idea 4. Make sure the transaction is signed properly -5. Ensure a Blob Transaction comes with its sidecar (Done!): - 1. Validate number of BlobHashes is positive +5. Ensure a Blob Transaction comes with its sidecar (Done! - All blob validations have been moved to `common/types/blobs_bundle.rs`): + 1. Validate number of BlobHashes is positive (Done!) 2. Validate number of BlobHashes is less than the maximum allowed per block, which may be computed as `maxBlobGasPerBlock / blobTxBlobGasPerBlob` 3. Ensure number of BlobHashes is equal to: - The number of blobs (Done!) - The number of commitments (Done!) - The number of proofs (Done!) - 4. Validate that the hashes matches with the commitments, performing a `kzg4844` hash. - 5. Verify the blob proofs with the `kzg4844` + 4. Validate that the hashes matches with the commitments, performing a `kzg4844` hash. (Done!) + 5. Verify the blob proofs with the `kzg4844` (Done!) Stateful validations 1. Ensure transaction nonce is higher than the `from` address stored nonce 2. Certain pools do not allow for nonce gaps. Ensure a gap is not produced (that is, the transaction nonce is exactly the following of the stored one) @@ -200,11 +201,11 @@ fn validate_transaction(tx: &Transaction, store: Store) -> Result<(), MempoolErr // Check that the specified blob gas fee is above the minimum value if let Some(fee) = tx.max_fee_per_blob_gas() { - // Blob tx + // Blob tx fee checks if fee < MIN_BASE_FEE_PER_BLOB_GAS.into() { return Err(MempoolError::TxBlobBaseFeeTooLowError); } - } + }; let maybe_sender_acc_info = store.get_account_info(header_no, tx.sender())?; @@ -234,40 +235,6 @@ fn validate_transaction(tx: &Transaction, store: Store) -> Result<(), MempoolErr Ok(()) } -fn validate_blobs_bundle( - tx: &EIP4844Transaction, - blobs_bundle: &BlobsBundle, -) -> Result<(), MempoolError> { - let tx_blob_count = tx.blob_versioned_hashes.len(); - - if tx_blob_count != blobs_bundle.blobs.len() - || tx_blob_count != blobs_bundle.commitments.len() - || tx_blob_count != blobs_bundle.proofs.len() - { - return Err(MempoolError::BlobsBundleWrongLen); - }; - - // return error early if any commitment doesn't match it's blob versioned hash - for (commitment, blob_versioned_hash) in blobs_bundle - .commitments - .iter() - .zip(tx.blob_versioned_hashes.iter()) - { - if *blob_versioned_hash != kzg_to_versioned_hash(commitment) { - return Err(MempoolError::BlobVersionedHashesIncorrectError); - } - } - - Ok(()) -} - -pub fn kzg_to_versioned_hash(data: &[u8]) -> H256 { - use k256::sha2::Digest; - let mut versioned_hash: [u8; 32] = k256::sha2::Sha256::digest(data).into(); - versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; - versioned_hash.into() -} - fn transaction_intrinsic_gas( tx: &Transaction, header: &BlockHeader, @@ -337,15 +304,13 @@ mod tests { TX_DATA_ZERO_GAS_COST, TX_GAS_COST, TX_INIT_CODE_WORD_GAS_COST, }; - use super::{transaction_intrinsic_gas, validate_blobs_bundle, validate_transaction}; - use ethereum_rust_core::types::{ - BlobsBundle, BlockHeader, ChainConfig, EIP1559Transaction, EIP4844Transaction, Transaction, - TxKind, BYTES_PER_BLOB, + use super::{transaction_intrinsic_gas, validate_transaction}; + use ethrex_core::types::{ + BlockHeader, ChainConfig, EIP1559Transaction, EIP4844Transaction, Transaction, TxKind, }; - use ethereum_rust_core::{Address, Bytes, H256, U256}; - use ethereum_rust_storage::EngineType; - use ethereum_rust_storage::{error::StoreError, Store}; - use hex; + use ethrex_core::{Address, Bytes, H256, U256}; + use ethrex_storage::EngineType; + use ethrex_storage::{error::StoreError, Store}; fn setup_storage(config: ChainConfig, header: BlockHeader) -> Result { let store = Store::new("test", EngineType::InMemory)?; @@ -687,116 +652,4 @@ mod tests { Err(MempoolError::TxBlobBaseFeeTooLowError) )); } - - #[test] - fn transaction_with_correct_blobs_should_pass() { - let convert_str_to_bytes48 = |s| { - let bytes = hex::decode(s).expect("Invalid hex string"); - let mut array = [0u8; 48]; - array.copy_from_slice(&bytes[..48]); - array - }; - - // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs - - let blobs_bundle = BlobsBundle { - blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], - commitments: vec!["b90289aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", - "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] - .into_iter() - .map(|s| { - convert_str_to_bytes48(s) - }) - .collect(), - proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", - "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] - .into_iter() - .map(|s| { - convert_str_to_bytes48(s) - }) - .collect() - }; - - let tx = EIP4844Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - max_fee_per_blob_gas: 0.into(), - gas: 15_000_000, - to: Address::from_low_u64_be(1), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::default(), // No data - access_list: Default::default(), // No access list - blob_versioned_hashes: vec![ - "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", - "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", - ] - .into_iter() - .map(|b| { - let bytes = hex::decode(b).expect("Invalid hex string"); - H256::from_slice(&bytes) - }) - .collect::>(), - ..Default::default() - }; - - assert!(matches!(validate_blobs_bundle(&tx, &blobs_bundle), Ok(()))); - } - - #[test] - fn transaction_with_incorrect_blobs_should_fail() { - let convert_str_to_bytes48 = |s| { - let bytes = hex::decode(s).expect("Invalid hex string"); - let mut array = [0u8; 48]; - array.copy_from_slice(&bytes[..48]); - array - }; - - // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs - let blobs_bundle = BlobsBundle { - blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], - commitments: vec!["dead89aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", - "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] - .into_iter() - .map(|s| { - convert_str_to_bytes48(s) - }) - .collect(), - proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", - "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] - .into_iter() - .map(|s| { - convert_str_to_bytes48(s) - }) - .collect() - }; - - let tx = EIP4844Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - max_fee_per_blob_gas: 0.into(), - gas: 15_000_000, - to: Address::from_low_u64_be(1), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::default(), // No data - access_list: Default::default(), // No access list - blob_versioned_hashes: vec![ - "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", - "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", - ] - .into_iter() - .map(|b| { - let bytes = hex::decode(b).expect("Invalid hex string"); - H256::from_slice(&bytes) - }) - .collect::>(), - ..Default::default() - }; - - assert!(matches!( - validate_blobs_bundle(&tx, &blobs_bundle), - Err(MempoolError::BlobVersionedHashesIncorrectError) - )); - } } diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index e6fc48a9c..a9e841fb8 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -3,7 +3,7 @@ use std::{ collections::HashMap, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{ calculate_base_fee_per_blob_gas, calculate_base_fee_per_gas, compute_receipts_root, compute_transactions_root, compute_withdrawals_root, BlobsBundle, Block, BlockBody, @@ -12,9 +12,9 @@ use ethereum_rust_core::{ }, Address, Bloom, Bytes, H256, U256, }; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_storage::{error::StoreError, Store}; -use ethereum_rust_vm::{ +use ethrex_rlp::encode::RLPEncode; +use ethrex_storage::{error::StoreError, Store}; +use ethrex_vm::{ beacon_root_contract_call, evm_state, execute_tx, get_state_transitions, process_withdrawals, spec_id, EvmError, EvmState, SpecId, }; @@ -224,7 +224,7 @@ fn fetch_mempool_transactions( context: &mut PayloadBuildContext, ) -> Result<(TransactionQueue, TransactionQueue), ChainError> { let tx_filter = PendingTxFilter { - /*TODO(https://github.com/lambdaclass/ethereum_rust/issues/680): add tip filter */ + /*TODO(https://github.com/lambdaclass/ethrex/issues/680): add tip filter */ base_fee: context.base_fee_per_gas(), blob_fee: Some(context.base_fee_per_blob_gas), ..Default::default() diff --git a/crates/blockchain/smoke_test.rs b/crates/blockchain/smoke_test.rs index f2897b7ff..c9fbe7498 100644 --- a/crates/blockchain/smoke_test.rs +++ b/crates/blockchain/smoke_test.rs @@ -10,11 +10,11 @@ mod blockchain_integration_test { payload::{build_payload, create_payload, BuildPayloadArgs}, }; - use ethereum_rust_core::{ + use ethrex_core::{ types::{Block, BlockHeader}, H160, H256, }; - use ethereum_rust_storage::{EngineType, Store}; + use ethrex_storage::{EngineType, Store}; #[test] fn test_small_to_long_reorg() { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 5aa9a95cb..57523b005 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -1,19 +1,22 @@ [package] -name = "ethereum_rust-core" +name = "ethrex-core" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum_rust-rlp.workspace = true -ethereum_rust-trie = { path = "../storage/trie", default-features = false } +ethrex-rlp.workspace = true +ethrex-trie = { path = "../storage/trie", default-features = false } tinyvec = "1.6.0" ethereum-types.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true +k256.workspace = true +# TODO(#1102): Move to Lambdaworks in the future +c-kzg = { version = "^1.0.3", optional = true } keccak-hash = "0.10.0" sha3.workspace = true secp256k1.workspace = true @@ -27,8 +30,9 @@ lazy_static.workspace = true hex-literal.workspace = true [features] -default = ["libmdbx"] -libmdbx = ["ethereum_rust-trie/libmdbx"] +default = ["libmdbx", "c-kzg"] +libmdbx = ["ethrex-trie/libmdbx"] +c-kzg = ["dep:c-kzg"] [lib] path = "./core.rs" diff --git a/crates/common/rlp/Cargo.toml b/crates/common/rlp/Cargo.toml index b3737055b..34c6a9680 100644 --- a/crates/common/rlp/Cargo.toml +++ b/crates/common/rlp/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust-rlp" +name = "ethrex-rlp" version.workspace = true edition.workspace = true diff --git a/crates/common/rlp/decode.rs b/crates/common/rlp/decode.rs index 0b04ed67d..6418e693a 100644 --- a/crates/common/rlp/decode.rs +++ b/crates/common/rlp/decode.rs @@ -522,6 +522,9 @@ pub(crate) fn static_left_pad(data: &[u8]) -> Result<[u8; N], RL if data[0] == 0 { return Err(RLPDecodeError::MalformedData); } + if data.len() > N { + return Err(RLPDecodeError::InvalidLength); + } let data_start_index = N.saturating_sub(data.len()); result .get_mut(data_start_index..) diff --git a/crates/common/rlp/structs.rs b/crates/common/rlp/structs.rs index e497cb26b..161e443fd 100644 --- a/crates/common/rlp/structs.rs +++ b/crates/common/rlp/structs.rs @@ -16,9 +16,9 @@ use bytes::Bytes; /// # Examples /// /// ``` -/// # use ethereum_rust_rlp::structs::Decoder; -/// # use ethereum_rust_rlp::error::RLPDecodeError; -/// # use ethereum_rust_rlp::decode::RLPDecode; +/// # use ethrex_rlp::structs::Decoder; +/// # use ethrex_rlp::error::RLPDecodeError; +/// # use ethrex_rlp::decode::RLPDecode; /// #[derive(Debug, PartialEq, Eq)] /// struct Simple { /// pub a: u8, @@ -131,8 +131,8 @@ fn field_decode_error(field_name: &str, err: RLPDecodeError) -> RLPDecodeErro /// # Examples /// /// ``` -/// # use ethereum_rust_rlp::structs::Encoder; -/// # use ethereum_rust_rlp::encode::RLPEncode; +/// # use ethrex_rlp::structs::Encoder; +/// # use ethrex_rlp::encode::RLPEncode; /// # use bytes::BufMut; /// #[derive(Debug, PartialEq, Eq)] /// struct Simple { diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index 5b4910835..378419199 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; use bytes::Bytes; -use ethereum_rust_trie::Trie; use ethereum_types::{H256, U256}; +use ethrex_trie::Trie; use serde::{Deserialize, Serialize}; use sha3::{Digest as _, Keccak256}; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, diff --git a/crates/common/types/blobs_bundle.rs b/crates/common/types/blobs_bundle.rs index 8df27665b..974892476 100644 --- a/crates/common/types/blobs_bundle.rs +++ b/crates/common/types/blobs_bundle.rs @@ -1,7 +1,17 @@ +#[cfg(feature = "c-kzg")] +use lazy_static::lazy_static; use std::ops::AddAssign; use crate::serde_utils; -use ethereum_rust_rlp::{ +use crate::{ + types::{constants::VERSIONED_HASH_VERSION_KZG, transaction::EIP4844Transaction}, + Bytes, H256, +}; + +#[cfg(feature = "c-kzg")] +use c_kzg::{ethereum_kzg_settings, KzgCommitment, KzgProof, KzgSettings}; + +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -16,6 +26,11 @@ pub type Blob = [u8; BYTES_PER_BLOB]; pub type Commitment = Bytes48; pub type Proof = Bytes48; +#[cfg(feature = "c-kzg")] +lazy_static! { + static ref KZG_SETTINGS: &'static KzgSettings = ethereum_kzg_settings(); +} + #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] /// Struct containing all the blobs for a blob transaction, along with the corresponding commitments and proofs @@ -28,6 +43,134 @@ pub struct BlobsBundle { pub proofs: Vec, } +pub fn blob_from_bytes(bytes: Bytes) -> Result { + // This functions moved from `l2/utils/eth_client/transaction.rs` + // We set the first byte of every 32-bytes chunk to 0x00 + // so it's always under the field module. + if bytes.len() > BYTES_PER_BLOB * 31 / 32 { + return Err(BlobsBundleError::BlobDataInvalidBytesLength); + } + + let mut buf = [0u8; BYTES_PER_BLOB]; + buf[..(bytes.len() * 32).div_ceil(31)].copy_from_slice( + &bytes + .chunks(31) + .map(|x| [&[0x00], x].concat()) + .collect::>() + .concat(), + ); + + Ok(buf) +} + +fn kzg_commitment_to_versioned_hash(data: &Commitment) -> H256 { + use k256::sha2::Digest; + let mut versioned_hash: [u8; 32] = k256::sha2::Sha256::digest(data).into(); + versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; + versioned_hash.into() +} + +#[cfg(feature = "c-kzg")] +fn blob_to_kzg_commitment_and_proof(blob: &Blob) -> Result<(Commitment, Proof), BlobsBundleError> { + let blob: c_kzg::Blob = (*blob).into(); + + let commitment = KzgCommitment::blob_to_kzg_commitment(&blob, &KZG_SETTINGS) + .or(Err(BlobsBundleError::BlobToCommitmentAndProofError))?; + + let commitment_bytes = commitment.to_bytes(); + + let proof = KzgProof::compute_blob_kzg_proof(&blob, &commitment_bytes, &KZG_SETTINGS) + .or(Err(BlobsBundleError::BlobToCommitmentAndProofError))?; + + let proof_bytes = proof.to_bytes(); + + Ok((commitment_bytes.into_inner(), proof_bytes.into_inner())) +} + +#[cfg(feature = "c-kzg")] +fn verify_blob_kzg_proof( + blob: Blob, + commitment: Commitment, + proof: Proof, +) -> Result { + let blob: c_kzg::Blob = blob.into(); + let commitment: c_kzg::Bytes48 = commitment.into(); + let proof: c_kzg::Bytes48 = proof.into(); + + KzgProof::verify_blob_kzg_proof(&blob, &commitment, &proof, &KZG_SETTINGS) + .or(Err(BlobsBundleError::BlobToCommitmentAndProofError)) +} + +impl BlobsBundle { + // In the future we might want to provide a new method that calculates the commitments and proofs using the following. + #[cfg(feature = "c-kzg")] + pub fn create_from_blobs(blobs: &Vec) -> Result { + let mut commitments = Vec::new(); + let mut proofs = Vec::new(); + + // Populate the commitments and proofs + for blob in blobs { + let (commitment, proof) = blob_to_kzg_commitment_and_proof(blob)?; + commitments.push(commitment); + proofs.push(proof); + } + + Ok(Self { + blobs: blobs.clone(), + commitments, + proofs, + }) + } + + pub fn generate_versioned_hashes(&self) -> Vec { + self.commitments + .iter() + .map(kzg_commitment_to_versioned_hash) + .collect() + } + + #[cfg(feature = "c-kzg")] + pub fn validate(&self, tx: &EIP4844Transaction) -> Result<(), BlobsBundleError> { + let blob_count = self.blobs.len(); + + // Check if the blob bundle is empty + if blob_count == 0 { + return Err(BlobsBundleError::BlobBundleEmptyError); + } + + // Check if the blob versioned hashes and blobs bundle content length mismatch + if blob_count != self.commitments.len() + || blob_count != self.proofs.len() + || blob_count != tx.blob_versioned_hashes.len() + { + return Err(BlobsBundleError::BlobsBundleWrongLen); + }; + + // Check versioned hashes match the tx + for (commitment, blob_versioned_hash) in + self.commitments.iter().zip(tx.blob_versioned_hashes.iter()) + { + if *blob_versioned_hash != kzg_commitment_to_versioned_hash(commitment) { + return Err(BlobsBundleError::BlobVersionedHashesError); + } + } + + // Validate the blobs with the commitments and proofs + for ((blob, commitment), proof) in self + .blobs + .iter() + .zip(self.commitments.iter()) + .zip(self.proofs.iter()) + { + if !verify_blob_kzg_proof(*blob, *commitment, *proof)? { + return Err(BlobsBundleError::BlobToCommitmentAndProofError); + } + } + + Ok(()) + } +} + impl RLPEncode for BlobsBundle { fn encode(&self, buf: &mut dyn bytes::BufMut) { let encoder = Encoder::new(buf); @@ -63,3 +206,163 @@ impl AddAssign for BlobsBundle { self.proofs.extend_from_slice(&rhs.proofs); } } + +#[derive(Debug, thiserror::Error)] +pub enum BlobsBundleError { + #[error("Blob data has an invalid length")] + BlobDataInvalidBytesLength, + #[error("Blob bundle is empty")] + BlobBundleEmptyError, + #[error("Blob versioned hashes and blobs bundle content length mismatch")] + BlobsBundleWrongLen, + #[error("Blob versioned hashes are incorrect")] + BlobVersionedHashesError, + #[error("Blob to commitment and proof generation error")] + BlobToCommitmentAndProofError, +} + +#[cfg(test)] + +mod tests { + use super::*; + use crate::{ + types::{blobs_bundle, transaction::EIP4844Transaction}, + Address, Bytes, U256, + }; + mod shared { + pub fn convert_str_to_bytes48(s: &str) -> [u8; 48] { + let bytes = hex::decode(s).expect("Invalid hex string"); + let mut array = [0u8; 48]; + array.copy_from_slice(&bytes[..48]); + array + } + } + + #[test] + fn transaction_with_valid_blobs_should_pass() { + let blobs = vec!["Hello, world!".as_bytes(), "Goodbye, world!".as_bytes()] + .into_iter() + .map(|data| blobs_bundle::blob_from_bytes(data.into()).expect("Failed to create blob")) + .collect(); + + let blobs_bundle = + BlobsBundle::create_from_blobs(&blobs).expect("Failed to create blobs bundle"); + + let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); + + let tx = EIP4844Transaction { + nonce: 3, + max_priority_fee_per_gas: 0, + max_fee_per_gas: 0, + max_fee_per_blob_gas: 0.into(), + gas: 15_000_000, + to: Address::from_low_u64_be(1), // Normal tx + value: U256::zero(), // Value zero + data: Bytes::default(), // No data + access_list: Default::default(), // No access list + blob_versioned_hashes, + ..Default::default() + }; + + assert!(matches!(blobs_bundle.validate(&tx), Ok(()))); + } + #[test] + fn transaction_with_invalid_proofs_should_fail() { + // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs, but with 0 size blobs + let blobs_bundle = BlobsBundle { + blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], + commitments: vec!["b90289aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", + "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] + .into_iter() + .map(|s| { + shared::convert_str_to_bytes48(s) + }) + .collect(), + proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", + "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] + .into_iter() + .map(|s| { + shared::convert_str_to_bytes48(s) + }) + .collect() + }; + + let tx = EIP4844Transaction { + nonce: 3, + max_priority_fee_per_gas: 0, + max_fee_per_gas: 0, + max_fee_per_blob_gas: 0.into(), + gas: 15_000_000, + to: Address::from_low_u64_be(1), // Normal tx + value: U256::zero(), // Value zero + data: Bytes::default(), // No data + access_list: Default::default(), // No access list + blob_versioned_hashes: vec![ + "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", + "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", + ] + .into_iter() + .map(|b| { + let bytes = hex::decode(b).expect("Invalid hex string"); + H256::from_slice(&bytes) + }) + .collect::>(), + ..Default::default() + }; + + assert!(matches!( + blobs_bundle.validate(&tx), + Err(BlobsBundleError::BlobToCommitmentAndProofError) + )); + } + + #[test] + fn transaction_with_incorrect_blobs_should_fail() { + // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs + let blobs_bundle = BlobsBundle { + blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], + commitments: vec!["dead89aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", + "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] + .into_iter() + .map(|s| { + shared::convert_str_to_bytes48(s) + }) + .collect(), + proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", + "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] + .into_iter() + .map(|s| { + shared::convert_str_to_bytes48(s) + }) + .collect() + }; + + let tx = EIP4844Transaction { + nonce: 3, + max_priority_fee_per_gas: 0, + max_fee_per_gas: 0, + max_fee_per_blob_gas: 0.into(), + gas: 15_000_000, + to: Address::from_low_u64_be(1), // Normal tx + value: U256::zero(), // Value zero + data: Bytes::default(), // No data + access_list: Default::default(), // No access list + blob_versioned_hashes: vec![ + "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", + "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", + ] + .into_iter() + .map(|b| { + let bytes = hex::decode(b).expect("Invalid hex string"); + H256::from_slice(&bytes) + }) + .collect::>(), + ..Default::default() + }; + + assert!(matches!( + blobs_bundle.validate(&tx), + Err(BlobsBundleError::BlobVersionedHashesError) + )); + } +} diff --git a/crates/common/types/block.rs b/crates/common/types/block.rs index ba6261314..bbd2acd3b 100644 --- a/crates/common/types/block.rs +++ b/crates/common/types/block.rs @@ -7,14 +7,14 @@ use crate::{ Address, H256, U256, }; use bytes::Bytes; -use ethereum_rust_rlp::{ +use ethereum_types::Bloom; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, structs::{Decoder, Encoder}, }; -use ethereum_rust_trie::Trie; -use ethereum_types::Bloom; +use ethrex_trie::Trie; use keccak_hash::keccak; use serde::{Deserialize, Serialize}; diff --git a/crates/common/types/constants.rs b/crates/common/types/constants.rs index 52e5034ee..0e195cb38 100644 --- a/crates/common/types/constants.rs +++ b/crates/common/types/constants.rs @@ -7,6 +7,7 @@ pub const GWEI_TO_WEI: u64 = 1_000_000_000; pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; //Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) pub const BLOB_BASE_FEE_UPDATE_FRACTION: u64 = 3338477; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) // Blob size related // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) diff --git a/crates/common/types/fork_id.rs b/crates/common/types/fork_id.rs index a44795b35..ec3945612 100644 --- a/crates/common/types/fork_id.rs +++ b/crates/common/types/fork_id.rs @@ -1,5 +1,5 @@ use crc32fast::Hasher; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -10,7 +10,7 @@ use ethereum_types::H32; use super::{BlockHash, BlockNumber, ChainConfig}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub struct ForkId { fork_hash: H32, fork_next: BlockNumber, diff --git a/crates/common/types/genesis.rs b/crates/common/types/genesis.rs index f729f63b1..21ed59b0e 100644 --- a/crates/common/types/genesis.rs +++ b/crates/common/types/genesis.rs @@ -1,11 +1,11 @@ use bytes::Bytes; -use ethereum_rust_trie::Trie; use ethereum_types::{Address, Bloom, H256, U256}; +use ethrex_trie::Trie; use serde::{Deserialize, Serialize}; use sha3::{Digest, Keccak256}; use std::collections::HashMap; -use ethereum_rust_rlp::encode::RLPEncode; +use ethrex_rlp::encode::RLPEncode; use super::{ compute_receipts_root, compute_transactions_root, compute_withdrawals_root, AccountState, diff --git a/crates/common/types/mod.rs b/crates/common/types/mod.rs index e244999d6..736de7a16 100644 --- a/crates/common/types/mod.rs +++ b/crates/common/types/mod.rs @@ -1,5 +1,5 @@ mod account; -mod blobs_bundle; +pub mod blobs_bundle; mod block; mod constants; mod fork_id; diff --git a/crates/common/types/receipt.rs b/crates/common/types/receipt.rs index d70d5bad9..e0e888244 100644 --- a/crates/common/types/receipt.rs +++ b/crates/common/types/receipt.rs @@ -1,11 +1,11 @@ use bytes::Bytes; -use ethereum_rust_rlp::{ +use ethereum_types::{Address, Bloom, BloomInput, H256}; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, structs::{Decoder, Encoder}, }; -use ethereum_types::{Address, Bloom, BloomInput, H256}; use serde::{Deserialize, Serialize}; use super::TxType; diff --git a/crates/common/types/transaction.rs b/crates/common/types/transaction.rs index e1ea3fefb..95fea476f 100644 --- a/crates/common/types/transaction.rs +++ b/crates/common/types/transaction.rs @@ -9,7 +9,7 @@ use serde::{ser::SerializeStruct, Deserialize, Serialize}; pub use serde_impl::{AccessListEntry, GenericTransaction}; use sha3::{Digest, Keccak256}; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ constants::RLP_NULL, decode::{get_rlp_bytes_item_payload, is_encoded_as_bytes, RLPDecode}, encode::{PayloadRLPEncode, RLPEncode}, diff --git a/crates/l2/.env.example b/crates/l2/.env.example index 54b5f9ba4..865056bcb 100644 --- a/crates/l2/.env.example +++ b/crates/l2/.env.example @@ -13,7 +13,6 @@ L1_WATCHER_TOPICS=0x6f65d68a35457dd88c1f8641be5da191aa122bc76de22ab0789dcc71929d L1_WATCHER_CHECK_INTERVAL_MS=1000 L1_WATCHER_MAX_BLOCK_STEP=5000 L1_WATCHER_L2_PROPOSER_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 -L1_WATCHER_L2_PROPOSER_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b ENGINE_API_RPC_URL=http://localhost:8551 ENGINE_API_JWT_PATH=./jwt.hex PROVER_SERVER_LISTEN_IP=127.0.0.1 diff --git a/crates/l2/Cargo.toml b/crates/l2/Cargo.toml index 2a08fea5e..408198c35 100644 --- a/crates/l2/Cargo.toml +++ b/crates/l2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust-l2" +name = "ethrex-l2" version = "0.1.0" edition = "2021" @@ -13,13 +13,13 @@ tracing.workspace = true serde.workspace = true serde_json.workspace = true ethereum-types.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-rlp.workspace = true -ethereum_rust-rpc.workspace = true -ethereum_rust-blockchain.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-vm.workspace = true -ethereum_rust-dev = { path = "../../crates/blockchain/dev" } +ethrex-core.workspace = true +ethrex-rlp.workspace = true +ethrex-rpc.workspace = true +ethrex-blockchain.workspace = true +ethrex-storage.workspace = true +ethrex-vm.workspace = true +ethrex-dev = { path = "../../crates/blockchain/dev" } hex.workspace = true bytes.workspace = true jsonwebtoken.workspace = true @@ -27,14 +27,12 @@ secp256k1.workspace = true keccak-hash = "0.10.0" envy = "0.4.2" thiserror.workspace = true -sha2 = "0.10.8" -c-kzg = "^1.0.3" # risc0 risc0-zkvm = { version = "1.1.2" } [dev-dependencies] -ethereum_rust-sdk = { path = "./sdk" } +ethrex-sdk = { path = "./sdk" } rand = "0.8.5" diff --git a/crates/l2/Dockerfile b/crates/l2/Dockerfile index 6aae7e37c..52da2edad 100644 --- a/crates/l2/Dockerfile +++ b/crates/l2/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethereum_rust +WORKDIR /ethrex FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethereum_rust/recipe.json recipe.json +COPY --from=planner /ethrex/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,6 +27,6 @@ RUN cargo build --release --features l2 FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethereum_rust/target/release/ethereum_rust . +COPY --from=builder ethrex/target/release/ethrex . EXPOSE 1729 -ENTRYPOINT [ "./ethereum_rust" ] +ENTRYPOINT [ "./ethrex" ] diff --git a/crates/l2/Makefile b/crates/l2/Makefile index 3542fd899..54f011bd1 100644 --- a/crates/l2/Makefile +++ b/crates/l2/Makefile @@ -8,7 +8,7 @@ L1_GENESIS_FILE_PATH=../../test_data/genesis-l1.json help: ## 📚 Show help for each of the Makefile recipes @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -init: init-local-l1 deploy-l1 init-l2 ## 🚀 Initializes a localnet with Lambda Ethereum Rust client as both L1 and L2 +init: init-local-l1 deploy-l1 init-l2 ## 🚀 Initializes a localnet with Lambda ethrex client as both L1 and L2 down: down-local-l1 down-l2 ## 🛑 Shuts down the localnet @@ -16,22 +16,22 @@ clean: clean-contract-deps ## 🧹 Cleans the localnet restart: restart-local-l1 deploy-l1 restart-l2 ## 🔄 Restarts the localnet -cli: ## 🛠️ Installs the L2 Lambda Ethereum Rust CLI - cargo install --path ${ETHEREUM_RUST_PATH}/cmd/ethereum_rust_l2/ --force +cli: ## 🛠️ Installs the L2 Lambda ethrex CLI + cargo install --path ${ethrex_PATH}/cmd/ethrex_l2/ --force # Variables -ETHEREUM_RUST_PATH=$(shell pwd)/../.. -ETHEREUM_RUST_BIN_PATH=$(ETHEREUM_RUST_PATH)/target/release/ethereum_rust -ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH=$(ETHEREUM_RUST_PATH)/crates/blockchain/dev/docker-compose-dev.yaml -ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml +ethrex_PATH=$(shell pwd)/../.. +ethrex_BIN_PATH=$(ethrex_PATH)/target/release/ethrex +ethrex_DEV_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/dev/docker-compose-dev.yaml +ethrex_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml -ETHEREUM_RUST_L2_CONTRACTS_PATH=./contracts +ethrex_L2_CONTRACTS_PATH=./contracts L1_RPC_URL=http://localhost:8545 L1_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 -ETHEREUM_RUST_L2_DEV_LIBMDBX=dev_ethereum_rust_l2 -ETHEREUM_RUST_L1_DEV_LIBMDBX=dev_ethereum_rust_l1 +ethrex_L2_DEV_LIBMDBX=dev_ethrex_l2 +ethrex_L1_DEV_LIBMDBX=dev_ethrex_l1 L1_PORT=8545 L2_PORT=1729 L1_AUTH_PORT=8551 @@ -40,25 +40,25 @@ L2_AUTH_PORT=8552 # Local L1 -init-local-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client with Docker (Used with make init) - docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} up -d - -init-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features dev -- \ +init-local-l1: ## 🚀 Initializes an L1 Lambda ethrex Client with Docker (Used with make init) + docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} up -d + +init-l1: ## 🚀 Initializes an L1 Lambda ethrex Client + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features dev -- \ --network ${L1_GENESIS_FILE_PATH} \ --http.port ${L1_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L1_AUTH_PORT} \ - --datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX} + --datadir ${ethrex_L1_DEV_LIBMDBX} -down-local-l1: ## 🛑 Shuts down the L1 Lambda Ethereum Rust Client - docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} down +down-local-l1: ## 🛑 Shuts down the L1 Lambda ethrex Client + docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} down docker compose -f docker-compose-l2.yaml down -restart-local-l1: down-local-l1 init-local-l1 ## 🔄 Restarts the L1 Lambda Ethereum Rust Client +restart-local-l1: down-local-l1 init-local-l1 ## 🔄 Restarts the L1 Lambda ethrex Client rm_dev_libmdbx_l1: ## 🛑 Removes the Libmdbx DB used by the L1 - cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX} + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex -- removedb --datadir ${ethrex_L1_DEV_LIBMDBX} # Contracts @@ -69,37 +69,37 @@ clean-contract-deps: ## 🧹 Cleans the dependencies for the L1 contracts. restart-contract-deps: clean-contract-deps ## 🔄 Restarts the dependencies for the L1 contracts. deploy-l1: ## 📜 Deploys the L1 contracts - DEPLOYER_CONTRACTS_PATH=contracts cargo run --release --bin ethereum_rust_l2_l1_deployer --manifest-path ${ETHEREUM_RUST_L2_CONTRACTS_PATH}/Cargo.toml + DEPLOYER_CONTRACTS_PATH=contracts cargo run --release --bin ethrex_l2_l1_deployer --manifest-path ${ethrex_L2_CONTRACTS_PATH}/Cargo.toml # L2 -init-l2: ## 🚀 Initializes an L2 Lambda Ethereum Rust Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features l2 -- \ +init-l2: ## 🚀 Initializes an L2 Lambda ethrex Client + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features l2 -- \ --network ${L2_GENESIS_FILE_PATH} \ --http.port ${L2_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L2_AUTH_PORT} \ - --datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX} + --datadir ${ethrex_L2_DEV_LIBMDBX} -down-l2: ## 🛑 Shuts down the L2 Lambda Ethereum Rust Client - pkill -f ethereum_rust || exit 0 +down-l2: ## 🛑 Shuts down the L2 Lambda ethrex Client + pkill -f ethrex || exit 0 -restart-l2: down-l2 init-l2 ## 🔄 Restarts the L2 Lambda Ethereum Rust Client +restart-l2: down-l2 init-l2 ## 🔄 Restarts the L2 Lambda ethrex Client init-l2-prover: ## 🚀 Initializes the Prover - cargo run --release --features build_zkvm --manifest-path ../../Cargo.toml --bin ethereum_rust_prover + cargo run --release --features build_zkvm --manifest-path ../../Cargo.toml --bin ethrex_prover init-l2-prover-gpu: ## 🚀 Initializes the Prover with GPU support - cargo run --release --features "build_zkvm,gpu" --manifest-path ../../Cargo.toml --bin ethereum_rust_prover + cargo run --release --features "build_zkvm,gpu" --manifest-path ../../Cargo.toml --bin ethrex_prover rm_dev_libmdbx_l2: ## 🛑 Removes the Libmdbx DB used by the L2 - cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX} + cargo run --release --manifest-path ../../Cargo.toml --bin ethrex -- removedb --datadir ${ethrex_L2_DEV_LIBMDBX} # CI Testing test: - docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} down - docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} up -d --build + docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down + docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} up -d --build BRIDGE_ADDRESS=$$(grep 'L1_WATCHER_BRIDGE_ADDRESS' .env | cut -d= -f2) ON_CHAIN_PROPOSER_ADDRESS=$$(grep 'COMMITTER_ON_CHAIN_PROPOSER_ADDRESS' .env | cut -d= -f2) cargo test --release testito -- --nocapture - docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} down + docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down diff --git a/crates/l2/contracts/Cargo.toml b/crates/l2/contracts/Cargo.toml index 76df9717f..ea5a65510 100644 --- a/crates/l2/contracts/Cargo.toml +++ b/crates/l2/contracts/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust_l2-deployer" +name = "ethrex_l2-deployer" version = "0.1.0" edition = "2021" @@ -17,9 +17,9 @@ colored = "2.1.0" lazy_static = "1.5.0" tracing.workspace = true -ethereum_rust-l2 = { path = "../../l2" } -ethereum_rust-core = { path = "../../common" } +ethrex-l2 = { path = "../../l2" } +ethrex-core = { path = "../../common" } [[bin]] -name = "ethereum_rust_l2_l1_deployer" +name = "ethrex_l2_l1_deployer" path = "./deployer.rs" diff --git a/crates/l2/contracts/Dockerfile b/crates/l2/contracts/Dockerfile index 2334fd52b..d743f0d2e 100644 --- a/crates/l2/contracts/Dockerfile +++ b/crates/l2/contracts/Dockerfile @@ -9,18 +9,18 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethereum_rust +WORKDIR /ethrex FROM chef AS planner COPY . . # Determine the crates that need to be built from dependencies -RUN cargo chef prepare --bin ethereum_rust_l2_l1_deployer --recipe-path recipe.json +RUN cargo chef prepare --bin ethrex_l2_l1_deployer --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethereum_rust/recipe.json recipe.json +COPY --from=planner /ethrex/recipe.json recipe.json # Build dependencies only, these remained cached -RUN cargo chef cook --release --recipe-path recipe.json --manifest-path crates/l2/contracts/Cargo.toml --bin ethereum_rust_l2_l1_deployer +RUN cargo chef cook --release --recipe-path recipe.json --manifest-path crates/l2/contracts/Cargo.toml --bin ethrex_l2_l1_deployer COPY . . RUN cargo build --release --manifest-path crates/l2/contracts/Cargo.toml @@ -32,7 +32,7 @@ RUN apt-get update && apt-get -y install git gnupg software-properties-common RUN add-apt-repository ppa:ethereum/ethereum RUN apt-get update && apt-get -y install solc -COPY --from=builder ethereum_rust/target/release/ethereum_rust_l2_l1_deployer . +COPY --from=builder ethrex/target/release/ethrex_l2_l1_deployer . EXPOSE 1729 -ENTRYPOINT [ "./ethereum_rust_l2_l1_deployer" ] +ENTRYPOINT [ "./ethrex_l2_l1_deployer" ] diff --git a/crates/l2/contracts/deployer.rs b/crates/l2/contracts/deployer.rs index 602bd21b3..66de5adf3 100644 --- a/crates/l2/contracts/deployer.rs +++ b/crates/l2/contracts/deployer.rs @@ -1,11 +1,11 @@ use bytes::Bytes; use colored::Colorize; -use ethereum_rust_core::types::{GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM}; -use ethereum_rust_l2::utils::{ +use ethereum_types::{Address, H160, H256}; +use ethrex_core::types::{GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM}; +use ethrex_l2::utils::{ config::{read_env_as_lines, read_env_file, write_env}, eth_client::{eth_sender::Overrides, EthClient}, }; -use ethereum_types::{Address, H160, H256}; use keccak_hash::keccak; use secp256k1::SecretKey; use spinoff::{spinner, spinners, Color, Spinner}; diff --git a/crates/l2/contracts/src/l1/CommonBridge.sol b/crates/l2/contracts/src/l1/CommonBridge.sol index 93afb58fb..59ef19e5f 100644 --- a/crates/l2/contracts/src/l1/CommonBridge.sol +++ b/crates/l2/contracts/src/l1/CommonBridge.sol @@ -55,6 +55,11 @@ contract CommonBridge is ICommonBridge, Ownable, ReentrancyGuard { ON_CHAIN_PROPOSER = onChainProposer; } + /// @inheritdoc ICommonBridge + function getDepositLogs() public view returns (bytes32[] memory) { + return depositLogs; + } + /// @inheritdoc ICommonBridge function deposit(address to) public payable { require(msg.value > 0, "CommonBridge: amount to deposit is zero"); diff --git a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol index 92f6f6721..4958eafaa 100644 --- a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol +++ b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol @@ -41,6 +41,11 @@ interface ICommonBridge { uint256 indexed claimedAmount ); + /// @notice Method to retrieve all the deposit logs hashes. + /// @dev This method is used by the L2 L1_Watcher to get the remaining + /// deposit logs to be processed. + function getDepositLogs() external view returns (bytes32[] memory); + /// @notice Initializes the contract. /// @dev This method is called only once after the contract is deployed. /// @dev It sets the OnChainProposer address. diff --git a/crates/l2/docker-compose-l2.yaml b/crates/l2/docker-compose-l2.yaml index a434e3ab3..a0871823c 100644 --- a/crates/l2/docker-compose-l2.yaml +++ b/crates/l2/docker-compose-l2.yaml @@ -5,7 +5,7 @@ services: contract_deployer: platform: linux/amd64 container_name: contract_deployer - image: ethereum_rust_l2_contract_deployer + image: ethrex_l2_contract_deployer build: context: ../../ args: @@ -17,17 +17,17 @@ services: env_file: - .env environment: - - ETH_RPC_URL=http://ethereum_rust_l1:8545 + - ETH_RPC_URL=http://ethrex_l1:8545 - DEPLOYER_CONTRACTS_PATH=/contracts - ENV_FILE=/.env depends_on: - - ethereum_rust + - ethrex restart: on-failure:3 - ethereum_rust_l2: + ethrex_l2: restart: always - container_name: ethereum_rust_l2 - image: "ethereum_rust_l2" + container_name: ethrex_l2 + image: "ethrex_l2" build: context: ../../ dockerfile: ./crates/l2/Dockerfile @@ -35,7 +35,7 @@ services: - 127.0.0.1:1729:1729 - 3000:3000 environment: - - ETH_RPC_URL=http://ethereum_rust_l1:8545 + - ETH_RPC_URL=http://ethrex_l1:8545 - ENV_FILE=/.env volumes: - ../../test_data/genesis-l2.json:/genesis-l2.json diff --git a/crates/l2/docs/README.md b/crates/l2/docs/README.md index e1ae9f833..d92f09df6 100644 --- a/crates/l2/docs/README.md +++ b/crates/l2/docs/README.md @@ -1,4 +1,4 @@ -# Ethereum Rust L2 Docs +# ethrex L2 Docs For a high level overview of the L2: @@ -19,7 +19,7 @@ Configuration is done through env vars. A detailed list is available in each par Load tests are available via L2 CLI. The test take a list of private keys and send a bunch of transactions from each of them to some address. To run them, use the following command: ```bash -cargo run --bin ethereum_rust_l2 -- test load --path +cargo run --bin ethrex_l2 -- test load --path ``` The path should point to a plain text file containing a list of private keys, one per line. Those account must be funded on the L2 network. Use `--help` to see more available options. diff --git a/crates/l2/docs/contracts.md b/crates/l2/docs/contracts.md index 1301e427b..1bcc926c0 100644 --- a/crates/l2/docs/contracts.md +++ b/crates/l2/docs/contracts.md @@ -1,14 +1,15 @@ -# Ethereum Rust L2 Contracts +# ethrex L2 Contracts ## ToC -- [ToC](#toc) -- [L1 side](#l1-side) - - [`CommonBridge`](#commonbridge) - - [`OnChainOperator`](#onchainoperator) - - [`Verifier`](#verifier) -- [L2 side](#l2-side) - - [`L1MessageSender`](#l1messagesender) +- [ethrex L2 Contracts](#ethrex-l2-contracts) + - [ToC](#toc) + - [L1 side](#l1-side) + - [`CommonBridge`](#commonbridge) + - [`OnChainOperator`](#onchainoperator) + - [`Verifier`](#verifier) + - [L2 side](#l2-side) + - [`L1MessageSender`](#l1messagesender) ## L1 side diff --git a/crates/l2/docs/overview.md b/crates/l2/docs/overview.md index 7fd5cf705..47bb2bdf0 100644 --- a/crates/l2/docs/overview.md +++ b/crates/l2/docs/overview.md @@ -1,6 +1,6 @@ # High Level Docs -This document aims to explain how the Lambda Ethereum Rust L2 and all its moving parts work. +This document aims to explain how the Lambda ethrex L2 and all its moving parts work. ## Intro @@ -23,7 +23,7 @@ Below some answers to these questions, along with an overview of all the moving Now that general purpose `zkVM`s exist, most people have little trouble with the idea that you can prove execution. Just take the usual EVM code you wrote in Rust, compile to some `zkVM` target instead and you're mostly done. You can now prove it. -What's usually less clear is how you prove state. Let's say we want to prove a new L2 block that was just built. Running the `ethereum_rust` `execute_block` function on a Rust `zkVM` does the trick, but that only proves that you ran the VM correctly on **some** previous state/block. How do you know it was the actual previous state of the L2 and not some other, modified one? +What's usually less clear is how you prove state. Let's say we want to prove a new L2 block that was just built. Running the `ethrex` `execute_block` function on a Rust `zkVM` does the trick, but that only proves that you ran the VM correctly on **some** previous state/block. How do you know it was the actual previous state of the L2 and not some other, modified one? In other words, how do you ensure that: @@ -36,7 +36,7 @@ To solve this, we do what we always do: instead of having the actual previous st The flow for the prover is then roughly as follows: - Take as public input the previous block commitment and the next (output) block commitment. -- Execute the current block to prove its execution is valid. Here "execution" means more than just transaction execution; there's also header validation, transaction validation, etc. (essentially all the logic `ethereum_rust` needs to follow when executing and adding a new block to the chain). +- Execute the current block to prove its execution is valid. Here "execution" means more than just transaction execution; there's also header validation, transaction validation, etc. (essentially all the logic `ethrex` needs to follow when executing and adding a new block to the chain). - For every storage slot read, present and verify a merkle path from it to the previous state root (i.e. previous block commitment). - For every storage slot written, present and verify a merkle path from it to the next state root (i.e. next block commitment). @@ -65,7 +65,7 @@ Detailed documentation on the state diffs spec [here](./state_diffs.md). ### How do we prevent the sequencer from publishing the wrong state diffs? -Once again, state diffs have to be part of the public input. With them, the prover can show that they are equal to the ones returned by the VM after executing the block. As always, the actual state diffs are not part of the public input, but **their hash** is, so the size is a fixed 32 bytes. This hash is then part of the block commitment. The prover then assures us that the given state diff hash is correct (i.e. it exactly corresponds to the changes in state of the executed block). +Once again, state diffs have to be part of the public input. With them, the prover can show that they are equal to the ones returned by the VM after executing the block. As always, the actual state diffs are not part of the public input, but **their hash** is, so the size is a fixed 32 bytes. This hash is then part of the block commitment. The prover then assures us that the given state diff hash is correct (i.e. it exactly corresponds to the changes in state of the executed block). There's still a problem however: the L1 contract needs to have the actual state diff for data availability, not just the hash. This is sent as part of calldata of the `commit` transaction (actually later as a blob, we'll get to that), so the sequencer could in theory send the wrong state diff. To make sure this can't happen, the L1 contract hashes it to make sure that it matches the actual state diff hash that is included as part of the public input. @@ -85,7 +85,7 @@ This is important. If you recall, the way the L1 ensured that the state diff pub The solution is through a [proof of equivalence](https://ethresear.ch/t/easy-proof-of-equivalence-between-multiple-polynomial-commitment-schemes-to-the-same-data/8188) between polynomial commitment schemes. The idea is as follows: proofs of equivalence allow you to show that two (polynomial) commitments point to the same underlying data. In our case, we have two commitments: -- The state diff commitment calculated by the sequencer/prover. +- The state diff commitment calculated by the sequencer/prover. - The KZG commitment of the blob sent on the commit transaction (recall that the blob should just be the state diff). If we turn the first one into a polynomial commitment, we can take a random evaluation point through Fiat Shamir and prove that it evaluates to the same value as the KZG blob commitment at that point. The `commit` transaction then sends the blob commitment and, through the point evaluation precompile, verifies that the given blob evaluates to that same value. If it does, the underlying blob is indeed the correct state diff. diff --git a/crates/l2/docs/proposer.md b/crates/l2/docs/proposer.md index 6e6635a5e..64e12362e 100644 --- a/crates/l2/docs/proposer.md +++ b/crates/l2/docs/proposer.md @@ -1,13 +1,14 @@ -# Ethereum Rust L2 Proposer +# ethrex L2 Proposer ## ToC -- [ToC](#toc) -- [Components](#components) - - [L1 Watcher](#l1-watcher) - - [L1 Transaction Sender](#l1-transaction-sender) - - [Prover Server](#prover-server) -- [Configuration](#configuration) +- [ethrex L2 Proposer](#ethrex-l2-proposer) + - [ToC](#toc) + - [Components](#components) + - [L1 Watcher](#l1-watcher) + - [L1 Transaction Sender](#l1-transaction-sender) + - [Prover Server](#prover-server) + - [Configuration](#configuration) ## Components diff --git a/crates/l2/docs/prover.md b/crates/l2/docs/prover.md index 9726c88a3..35d70a117 100644 --- a/crates/l2/docs/prover.md +++ b/crates/l2/docs/prover.md @@ -1,25 +1,26 @@ -# Ethereum Rust L2 Prover +# ethrex L2 Prover ## ToC -- [ToC](#toc) -- [What](#what) -- [Workflow](#workflow) -- [How](#how) - - [Dev Mode](#dev-mode) - - [Quick Test](#quick-test) - - [Run the whole system with the prover](#run-the-whole-system-with-the-prover) - - [GPU mode](#gpu-mode) - - [Proving Process Test](#proving-process-test) - - [Run the whole system with the prover in Sepolia](#run-the-whole-system-with-the-prover-in-sepolia) -- [Configuration](#configuration) +- [ethrex L2 Prover](#ethrex-l2-prover) + - [ToC](#toc) + - [What](#what) + - [Workflow](#workflow) + - [How](#how) + - [Dev Mode](#dev-mode) + - [Quick Test](#quick-test) + - [Run the whole system with the prover](#run-the-whole-system-with-the-prover) + - [GPU mode](#gpu-mode) + - [Proving Process Test](#proving-process-test) + - [Run the whole system with the prover in Sepolia](#run-the-whole-system-with-the-prover-in-sepolia) + - [Configuration](#configuration) >[!NOTE] > The shipping/deploying process and the `Prover` itself are under development. ## What -The prover consists of two main components: handling incoming proving data from the `L2 proposer`, specifically the `prover_server` component, and the `zkVM`. The `prover_client` is responsible for this first part, while the `zkVM` serves as a RISC-V emulator executing code specified in `crates/l2/prover/zkvm/interface/guest/src`. +The prover consists of two main components: handling incoming proving data from the `L2 proposer`, specifically the `prover_server` component, and the `zkVM`. The `prover_client` is responsible for this first part, while the `zkVM` serves as a RISC-V emulator executing code specified in `crates/l2/prover/zkvm/interface/guest/src`. Before the `zkVM` code (or guest), there is a directory called `interface`, which indicates that we access the `zkVM` through the "interface" crate. In summary, the `prover_client` manages the inputs from the `prover_server` and then "calls" the `zkVM` to perform the proving process and generate the `groth16` ZK proof. @@ -115,7 +116,7 @@ make perf_gpu Two servers are required: one for the `prover` and another for the `proposer`. If you run both components on the same machine, the `prover` may consume all available resources, leading to potential stuttering or performance issues for the `proposer`/`node`. 1. `prover`/`zkvm` → prover with gpu, make sure to have all the required dependencies described at the beginning of [Gpu Mode](#gpu-mode) section. - 1. `cd lambda_ethereum_rust/crates/l2` + 1. `cd lambda_ethrex/crates/l2` 2. `cp .example.env` and change the `PROVER_CLIENT_PROVER_SERVER_ENDPOINT` with the ip of the other server. The env variables needed are: @@ -130,7 +131,7 @@ Finally, to start the `prover_client`/`zkvm`, run: - `make init-l2-prover-gpu` 2.  `proposer` → this server just needs rust installed. - 1. `cd lambda_ethereum_rust/crates/l2` + 1. `cd lambda_ethrex/crates/l2` 2. `cp .example.env` and change the addresses and the following fields: - `PROVER_SERVER_LISTEN_IP=0.0.0.0` → used to handle the tcp communication with the other server. - The `COMMITTER` and `PROVER_SERVER_VERIFIER` must be different accounts, the `DEPLOYER_ADDRESS` as well as the `L1_WATCHER` may be the same account used by the `COMMITTER` diff --git a/crates/l2/proposer/errors.rs b/crates/l2/proposer/errors.rs index df37f122c..bf469f701 100644 --- a/crates/l2/proposer/errors.rs +++ b/crates/l2/proposer/errors.rs @@ -1,8 +1,9 @@ use crate::utils::{config::errors::ConfigError, eth_client::errors::EthClientError}; -use ethereum_rust_dev::utils::engine_client::errors::EngineClientError; -use ethereum_rust_storage::error::StoreError; -use ethereum_rust_vm::EvmError; use ethereum_types::FromStrRadixErr; +use ethrex_core::types::BlobsBundleError; +use ethrex_dev::utils::engine_client::errors::EngineClientError; +use ethrex_storage::error::StoreError; +use ethrex_vm::EvmError; #[derive(Debug, thiserror::Error)] pub enum L1WatcherError { @@ -48,6 +49,8 @@ pub enum CommitterError { FailedToParseLastCommittedBlock(#[from] FromStrRadixErr), #[error("Committer failed retrieve block from storage: {0}")] FailedToRetrieveBlockFromStorage(#[from] StoreError), + #[error("Committer failed to generate blobs bundle: {0}")] + FailedToGenerateBlobsBundle(#[from] BlobsBundleError), #[error("Committer failed to get information from storage")] FailedToGetInformationFromStorage(String), #[error("Committer failed to encode state diff: {0}")] @@ -56,8 +59,6 @@ pub enum CommitterError { FailedToOpenPointsFile(#[from] std::io::Error), #[error("Committer failed to re-execute block: {0}")] FailedToReExecuteBlock(#[from] EvmError), - #[error("Committer failed to make KZG operations: {0}")] - KZGError(#[from] c_kzg::Error), #[error("Committer failed to send transaction: {0}")] FailedToSendCommitment(String), } diff --git a/crates/l2/proposer/l1_committer.rs b/crates/l2/proposer/l1_committer.rs index 1a08733b3..384cecda9 100644 --- a/crates/l2/proposer/l1_committer.rs +++ b/crates/l2/proposer/l1_committer.rs @@ -5,28 +5,24 @@ use crate::{ }, utils::{ config::{committer::CommitterConfig, eth::EthConfig}, - eth_client::{ - errors::EthClientError, eth_sender::Overrides, transaction::blob_from_bytes, EthClient, - }, + eth_client::{errors::EthClientError, eth_sender::Overrides, EthClient}, merkle_tree::merkelize, }, }; use bytes::Bytes; -use c_kzg::{Bytes48, KzgSettings}; -use ethereum_rust_blockchain::constants::TX_GAS_COST; -use ethereum_rust_core::{ +use ethrex_blockchain::constants::TX_GAS_COST; +use ethrex_core::{ types::{ - BlobsBundle, Block, EIP1559Transaction, GenericTransaction, PrivilegedL2Transaction, - PrivilegedTxType, Transaction, TxKind, BYTES_PER_BLOB, + blobs_bundle, BlobsBundle, Block, EIP1559Transaction, GenericTransaction, + PrivilegedL2Transaction, PrivilegedTxType, Transaction, TxKind, }, Address, H256, U256, }; -use ethereum_rust_rpc::types::transaction::WrappedEIP4844Transaction; -use ethereum_rust_storage::Store; -use ethereum_rust_vm::{evm_state, execute_block, get_state_transitions}; +use ethrex_rpc::types::transaction::WrappedEIP4844Transaction; +use ethrex_storage::Store; +use ethrex_vm::{evm_state, execute_block, get_state_transitions}; use keccak_hash::keccak; use secp256k1::SecretKey; -use sha2::{Digest, Sha256}; use std::ops::Div; use std::{collections::HashMap, time::Duration}; use tokio::time::sleep; @@ -41,7 +37,6 @@ pub struct Committer { l1_address: Address, l1_private_key: SecretKey, interval_ms: u64, - kzg_settings: &'static KzgSettings, } pub async fn start_l1_commiter(store: Store) { @@ -64,7 +59,6 @@ impl Committer { l1_address: committer_config.l1_address, l1_private_key: committer_config.l1_private_key, interval_ms: committer_config.interval_ms, - kzg_settings: c_kzg::ethereum_kzg_settings(), } } @@ -123,8 +117,7 @@ impl Committer { deposits, )?; - let (blob_commitment, blob_proof) = - self.prepare_blob_commitment(state_diff.clone())?; + let blobs_bundle = self.generate_blobs_bundle(state_diff.clone())?; let head_block_hash = block_to_commit.hash(); match self @@ -132,9 +125,7 @@ impl Committer { block_to_commit.header.number, withdrawal_logs_merkle_root, deposit_logs_hash, - blob_commitment, - blob_proof, - state_diff.encode()?, + blobs_bundle, ) .await { @@ -282,30 +273,16 @@ impl Committer { Ok(state_diff) } - /// Generate the KZG commitment and proof for the blob. This commitment can then be used - /// to calculate the blob versioned hash, necessary for the EIP-4844 transaction. - pub fn prepare_blob_commitment( + /// Generate the blob bundle necessary for the EIP-4844 transaction. + pub fn generate_blobs_bundle( &self, state_diff: StateDiff, - ) -> Result<([u8; 48], [u8; 48]), CommitterError> { + ) -> Result { let blob_data = state_diff.encode().map_err(CommitterError::from)?; - let blob = blob_from_bytes(blob_data).map_err(CommitterError::from)?; + let blob = blobs_bundle::blob_from_bytes(blob_data).map_err(CommitterError::from)?; - let commitment = c_kzg::KzgCommitment::blob_to_kzg_commitment(&blob, self.kzg_settings) - .map_err(CommitterError::from)?; - let commitment_bytes = - Bytes48::from_bytes(commitment.as_slice()).map_err(CommitterError::from)?; - let proof = - c_kzg::KzgProof::compute_blob_kzg_proof(&blob, &commitment_bytes, self.kzg_settings) - .map_err(CommitterError::from)?; - - let mut commitment_bytes = [0u8; 48]; - commitment_bytes.copy_from_slice(commitment.as_slice()); - let mut proof_bytes = [0u8; 48]; - proof_bytes.copy_from_slice(proof.as_slice()); - - Ok((commitment_bytes, proof_bytes)) + BlobsBundle::create_from_blobs(&vec![blob]).map_err(CommitterError::from) } pub async fn send_commitment( @@ -313,39 +290,26 @@ impl Committer { block_number: u64, withdrawal_logs_merkle_root: H256, deposit_logs_hash: H256, - commitment: [u8; 48], - proof: [u8; 48], - blob_data: Bytes, + blobs_bundle: BlobsBundle, ) -> Result { info!("Sending commitment for block {block_number}"); - let mut hasher = Sha256::new(); - hasher.update(commitment); - let mut blob_versioned_hash = hasher.finalize(); - blob_versioned_hash[0] = 0x01; // EIP-4844 versioning - let mut calldata = Vec::with_capacity(132); calldata.extend(COMMIT_FUNCTION_SELECTOR); let mut block_number_bytes = [0_u8; 32]; U256::from(block_number).to_big_endian(&mut block_number_bytes); calldata.extend(block_number_bytes); - calldata.extend(blob_versioned_hash); + + let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); + // We only actually support one versioned hash on the onChainProposer for now, + // but eventually this should work if we start sending multiple blobs per commit operation. + for blob_versioned_hash in blob_versioned_hashes { + let blob_versioned_hash_bytes = blob_versioned_hash.to_fixed_bytes(); + calldata.extend(blob_versioned_hash_bytes); + } calldata.extend(withdrawal_logs_merkle_root.0); calldata.extend(deposit_logs_hash.0); - let mut buf = [0u8; BYTES_PER_BLOB]; - buf.copy_from_slice( - blob_from_bytes(blob_data) - .map_err(CommitterError::from)? - .iter() - .as_slice(), - ); - - let blobs_bundle = BlobsBundle { - blobs: vec![buf], - commitments: vec![commitment], - proofs: vec![proof], - }; let wrapped_tx = self .eth_client .build_eip4844_transaction( diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index ed3aaa8fe..73d7290e8 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -6,12 +6,13 @@ use crate::{ }, }; use bytes::Bytes; -use ethereum_rust_blockchain::{constants::TX_GAS_COST, mempool}; -use ethereum_rust_core::types::PrivilegedTxType; -use ethereum_rust_core::types::{Signable, Transaction}; -use ethereum_rust_rpc::types::receipt::RpcLog; -use ethereum_rust_storage::Store; use ethereum_types::{Address, BigEndianHash, H256, U256}; +use ethrex_blockchain::{constants::TX_GAS_COST, mempool}; +use ethrex_core::types::PrivilegedTxType; +use ethrex_core::types::{Signable, Transaction}; +use ethrex_rpc::types::receipt::RpcLog; +use ethrex_storage::Store; +use keccak_hash::keccak; use secp256k1::SecretKey; use std::{cmp::min, ops::Mul, time::Duration}; use tokio::time::sleep; @@ -20,11 +21,31 @@ use tracing::{debug, info, warn}; pub async fn start_l1_watcher(store: Store) { let eth_config = EthConfig::from_env().expect("EthConfig::from_env()"); let watcher_config = L1WatcherConfig::from_env().expect("L1WatcherConfig::from_env()"); + let sleep_duration = Duration::from_millis(watcher_config.check_interval_ms); let mut l1_watcher = L1Watcher::new_from_config(watcher_config, eth_config); loop { - let logs = l1_watcher.get_logs().await.expect("l1_watcher.get_logs()"); + sleep(sleep_duration).await; + + let logs = match l1_watcher.get_logs().await { + Ok(logs) => logs, + Err(error) => { + warn!("Error when getting logs from L1: {}", error); + continue; + } + }; + if logs.is_empty() { + continue; + } + + let pending_deposits_logs = match l1_watcher.get_pending_deposit_logs().await { + Ok(logs) => logs, + Err(error) => { + warn!("Error when getting L1 pending deposit logs: {}", error); + continue; + } + }; let _deposit_txs = l1_watcher - .process_logs(logs, &store) + .process_logs(logs, &pending_deposits_logs, &store) .await .expect("l1_watcher.process_logs()"); } @@ -34,11 +55,9 @@ pub struct L1Watcher { eth_client: EthClient, address: Address, topics: Vec, - check_interval: Duration, max_block_step: U256, last_block_fetched: U256, l2_proposer_pk: SecretKey, - l2_proposer_address: Address, } impl L1Watcher { @@ -47,14 +66,32 @@ impl L1Watcher { eth_client: EthClient::new_from_config(eth_config), address: watcher_config.bridge_address, topics: watcher_config.topics, - check_interval: Duration::from_millis(watcher_config.check_interval_ms), max_block_step: watcher_config.max_block_step, last_block_fetched: U256::zero(), l2_proposer_pk: watcher_config.l2_proposer_private_key, - l2_proposer_address: watcher_config.l2_proposer_address, } } + pub async fn get_pending_deposit_logs(&self) -> Result, L1WatcherError> { + Ok(hex::decode( + &self + .eth_client + .call( + self.address, + Bytes::copy_from_slice(&[0x35, 0x6d, 0xa2, 0x49]), + Overrides::default(), + ) + .await?[2..], + ) + .map_err(|_| L1WatcherError::FailedToDeserializeLog("Not a valid hex string".to_string()))? + .chunks(32) + .map(H256::from_slice) + .collect::>() + .split_at(2) // Two first words are index and length abi encode + .1 + .to_vec()) + } + pub async fn get_logs(&mut self) -> Result, L1WatcherError> { let current_block = self.eth_client.get_block_number().await?; @@ -91,25 +128,25 @@ impl L1Watcher { self.last_block_fetched = new_last_block; - sleep(self.check_interval).await; - Ok(logs) } pub async fn process_logs( &self, logs: Vec, + l1_deposit_logs: &[H256], store: &Store, ) -> Result, L1WatcherError> { - if logs.is_empty() { - return Ok(Vec::new()); - } - let mut deposit_txs = Vec::new(); let mut operator_nonce = store .get_account_info( - self.eth_client.get_block_number().await?.as_u64(), - self.l2_proposer_address, + store + .get_latest_block_number() + .map_err(|e| L1WatcherError::FailedToRetrieveChainConfig(e.to_string()))? + .ok_or(L1WatcherError::FailedToRetrieveChainConfig( + "Last block is None".to_string(), + ))?, + Address::zero(), ) .map_err(|e| L1WatcherError::FailedToRetrieveDepositorAccountInfo(e.to_string()))? .map(|info| info.nonce) @@ -131,6 +168,13 @@ impl L1Watcher { )) })?; + let mut value_bytes = [0u8; 32]; + mint_value.to_big_endian(&mut value_bytes); + if !l1_deposit_logs.contains(&keccak([beneficiary.as_bytes(), &value_bytes].concat())) { + warn!("Deposit already processed (to: {beneficiary:#x}, value: {mint_value}), skipping."); + continue; + } + info!("Initiating mint transaction for {beneficiary:#x} with value {mint_value:#x}",); let mut mint_transaction = self diff --git a/crates/l2/proposer/mod.rs b/crates/l2/proposer/mod.rs index e146eaeb7..5c6c435c9 100644 --- a/crates/l2/proposer/mod.rs +++ b/crates/l2/proposer/mod.rs @@ -1,8 +1,8 @@ use crate::utils::config::{proposer::ProposerConfig, read_env_file}; use errors::ProposerError; -use ethereum_rust_dev::utils::engine_client::{config::EngineApiConfig, errors::EngineClientError}; -use ethereum_rust_storage::Store; use ethereum_types::{Address, H256}; +use ethrex_dev::utils::engine_client::{config::EngineApiConfig, errors::EngineClientError}; +use ethrex_storage::Store; use tracing::{info, warn}; pub mod l1_committer; @@ -64,7 +64,7 @@ impl Proposer { } pub async fn start(&self, head_block_hash: H256) -> Result<(), ProposerError> { - ethereum_rust_dev::block_producer::start_block_producer( + ethrex_dev::block_producer::start_block_producer( self.engine_config.rpc_url.clone(), std::fs::read(&self.engine_config.jwt_path).unwrap().into(), head_block_hash, diff --git a/crates/l2/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index 0190dd7ef..ffc2a18ed 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -1,5 +1,5 @@ -use ethereum_rust_storage::Store; -use ethereum_rust_vm::execution_db::ExecutionDB; +use ethrex_storage::Store; +use ethrex_vm::execution_db::ExecutionDB; use keccak_hash::keccak; use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; @@ -16,7 +16,7 @@ use tokio::{ }; use tracing::{debug, error, info, warn}; -use ethereum_rust_core::{ +use ethrex_core::{ types::{Block, BlockHeader, EIP1559Transaction}, Address, H256, }; @@ -105,6 +105,9 @@ pub async fn start_prover_server(store: Store) { .await .unwrap(); + info!("Sending verify transaction with tx hash: {tx_hash:#x}"); + + let mut retries = 1; while eth_client .get_transaction_receipt(tx_hash) .await @@ -112,9 +115,17 @@ pub async fn start_prover_server(store: Store) { .is_none() { thread::sleep(Duration::from_secs(1)); + retries += 1; + if retries > 10 { + error!("Couldn't find receipt for transaction {tx_hash:#x}"); + panic!("Couldn't find receipt for transaction {tx_hash:#x}"); + } } - info!("Mocked verify transaction sent"); + info!( + "Mocked verify transaction sent for block {}", + last_verified_block + 1 + ); } } else { let mut prover_server = ProverServer::new_from_config( diff --git a/crates/l2/prover/Cargo.toml b/crates/l2/prover/Cargo.toml index 3c93e46b5..e3274f200 100644 --- a/crates/l2/prover/Cargo.toml +++ b/crates/l2/prover/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust-prover" +name = "ethrex-prover" version.workspace = true edition.workspace = true @@ -14,19 +14,19 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing.workspace = true hex.workspace = true -# ethereum_rust -ethereum_rust-core.workspace = true -ethereum_rust-vm.workspace = true -ethereum_rust-rlp.workspace = true +# ethrex +ethrex-core.workspace = true +ethrex-vm.workspace = true +ethrex-rlp.workspace = true # l2 -ethereum_rust-l2.workspace = true +ethrex-l2.workspace = true # risc0 zkvm_interface = { path = "./zkvm/interface", default-features = false } risc0-zkvm = { version = "1.1.2" } -# revm (this dep is temporary, should be replaced with ethereum_rust-vm after ExecutionDB is completely integrated into the L1) +# revm (this dep is temporary, should be replaced with ethrex-vm after ExecutionDB is completely integrated into the L1) revm = { version = "14.0.3", features = [ "std", "serde", @@ -34,16 +34,16 @@ revm = { version = "14.0.3", features = [ ], default-features = false } [dev-dependencies] -ethereum_rust-vm.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-blockchain.workspace = true +ethrex-vm.workspace = true +ethrex-storage.workspace = true +ethrex-blockchain.workspace = true [lib] -name = "ethereum_rust_prover_lib" +name = "ethrex_prover_lib" path = "src/lib.rs" [[bin]] -name = "ethereum_rust_prover" +name = "ethrex_prover" path = "src/main.rs" [features] diff --git a/crates/l2/prover/src/lib.rs b/crates/l2/prover/src/lib.rs index 8e81668f6..ee528391a 100644 --- a/crates/l2/prover/src/lib.rs +++ b/crates/l2/prover/src/lib.rs @@ -1,7 +1,7 @@ pub mod prover; pub mod prover_client; -use ethereum_rust_l2::utils::config::prover_client::ProverClientConfig; +use ethrex_l2::utils::config::prover_client::ProverClientConfig; use tracing::warn; pub async fn init_client(config: ProverClientConfig) { diff --git a/crates/l2/prover/src/main.rs b/crates/l2/prover/src/main.rs index 403c65d04..7c99f88bb 100644 --- a/crates/l2/prover/src/main.rs +++ b/crates/l2/prover/src/main.rs @@ -1,5 +1,5 @@ -use ethereum_rust_l2::utils::config::{prover_client::ProverClientConfig, read_env_file}; -use ethereum_rust_prover_lib::init_client; +use ethrex_l2::utils::config::{prover_client::ProverClientConfig, read_env_file}; +use ethrex_prover_lib::init_client; use tracing::{self, debug, warn, Level}; diff --git a/crates/l2/prover/src/prover.rs b/crates/l2/prover/src/prover.rs index 05e8fc90b..45328188d 100644 --- a/crates/l2/prover/src/prover.rs +++ b/crates/l2/prover/src/prover.rs @@ -6,12 +6,12 @@ use zkvm_interface::methods::{ZKVM_PROGRAM_ELF, ZKVM_PROGRAM_ID}; use risc0_zkvm::{default_prover, ExecutorEnv, ExecutorEnvBuilder, ProverOpts}; -use ethereum_rust_core::types::Receipt; -use ethereum_rust_l2::{ +use ethrex_core::types::Receipt; +use ethrex_l2::{ proposer::prover_server::ProverInputData, utils::config::prover_client::ProverClientConfig, }; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_vm::execution_db::ExecutionDB; +use ethrex_rlp::encode::RLPEncode; +use ethrex_vm::execution_db::ExecutionDB; // The order of variables in this structure should match the order in which they were // committed in the zkVM, with each variable represented by a field. diff --git a/crates/l2/prover/src/prover_client.rs b/crates/l2/prover/src/prover_client.rs index 21626b9ac..8c297109c 100644 --- a/crates/l2/prover/src/prover_client.rs +++ b/crates/l2/prover/src/prover_client.rs @@ -7,7 +7,7 @@ use std::{ use tokio::time::sleep; use tracing::{debug, error, info, warn}; -use ethereum_rust_l2::{ +use ethrex_l2::{ proposer::prover_server::{ProofData, ProverInputData}, utils::config::prover_client::ProverClientConfig, }; diff --git a/crates/l2/prover/tests/perf_zkvm.rs b/crates/l2/prover/tests/perf_zkvm.rs index 7fdec57d3..c4ac54cd1 100644 --- a/crates/l2/prover/tests/perf_zkvm.rs +++ b/crates/l2/prover/tests/perf_zkvm.rs @@ -1,37 +1,30 @@ -use std::path::PathBuf; +use std::path::Path; use tracing::info; -use ethereum_rust_blockchain::add_block; -use ethereum_rust_l2::proposer::prover_server::ProverInputData; -use ethereum_rust_prover_lib::prover::Prover; -use ethereum_rust_storage::{EngineType, Store}; -use ethereum_rust_vm::execution_db::ExecutionDB; +use ethrex_blockchain::add_block; +use ethrex_l2::proposer::prover_server::ProverInputData; +use ethrex_prover_lib::prover::Prover; +use ethrex_storage::{EngineType, Store}; +use ethrex_vm::execution_db::ExecutionDB; #[tokio::test] async fn test_performance_zkvm() { tracing_subscriber::fmt::init(); - let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); - // Go back 3 levels (Go to the root of the project) - for _ in 0..3 { - path.pop(); - } - path.push("test_data"); + let path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../../../test_data")); // Another use is genesis-execution-api.json in conjunction with chain.rlp(20 blocks not too loaded). - let genesis_file_path = path.join("genesis-l2.json"); + let genesis_file_path = path.join("genesis-l2-old.json"); // l2-loadtest.rlp has blocks with many txs. let chain_file_path = path.join("l2-loadtest.rlp"); let store = Store::new("memory", EngineType::InMemory).expect("Failed to create Store"); - let genesis = ethereum_rust_l2::utils::test_data_io::read_genesis_file( - genesis_file_path.to_str().unwrap(), - ); + let genesis = + ethrex_l2::utils::test_data_io::read_genesis_file(genesis_file_path.to_str().unwrap()); store.add_initial_state(genesis.clone()).unwrap(); - let blocks = - ethereum_rust_l2::utils::test_data_io::read_chain_file(chain_file_path.to_str().unwrap()); + let blocks = ethrex_l2::utils::test_data_io::read_chain_file(chain_file_path.to_str().unwrap()); info!("Number of blocks to insert: {}", blocks.len()); for block in &blocks { diff --git a/crates/l2/prover/zkvm/interface/Cargo.toml b/crates/l2/prover/zkvm/interface/Cargo.toml index 2516c1005..3e89bdf33 100644 --- a/crates/l2/prover/zkvm/interface/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" serde = { version = "1.0", default-features = false, features = ["derive"] } thiserror = "1.0.64" -ethereum_rust-storage = { path = "../../../../storage/store" } +ethrex-storage = { path = "../../../../storage/store" } # revm revm = { version = "14.0.3", features = [ diff --git a/crates/l2/prover/zkvm/interface/guest/Cargo.toml b/crates/l2/prover/zkvm/interface/guest/Cargo.toml index 1d1b5616f..19a0c440a 100644 --- a/crates/l2/prover/zkvm/interface/guest/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/guest/Cargo.toml @@ -8,10 +8,10 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "1.1.2", default-features = false, features = ["std"] } -ethereum_rust-core = { path = "../../../../../common", default-features = false } -ethereum_rust-rlp = { path = "../../../../../common/rlp" } -ethereum_rust-vm = { path = "../../../../../vm", default-features = false } -ethereum_rust-blockchain = { path = "../../../../../blockchain", default-features = false } +ethrex-core = { path = "../../../../../common", default-features = false } +ethrex-rlp = { path = "../../../../../common/rlp" } +ethrex-vm = { path = "../../../../../vm", default-features = false } +ethrex-blockchain = { path = "../../../../../blockchain", default-features = false } [build-dependencies] ## cc version ^1.1.31 breaks the compilation. diff --git a/crates/l2/prover/zkvm/interface/guest/src/main.rs b/crates/l2/prover/zkvm/interface/guest/src/main.rs index 12f608134..425729ff7 100644 --- a/crates/l2/prover/zkvm/interface/guest/src/main.rs +++ b/crates/l2/prover/zkvm/interface/guest/src/main.rs @@ -1,9 +1,9 @@ -use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError}; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError}; use risc0_zkvm::guest::env; -use ethereum_rust_blockchain::{validate_block, validate_gas_used}; -use ethereum_rust_core::types::{Block, BlockHeader}; -use ethereum_rust_vm::{execute_block, execution_db::ExecutionDB, get_state_transitions, EvmState}; +use ethrex_blockchain::{validate_block, validate_gas_used}; +use ethrex_core::types::{Block, BlockHeader}; +use ethrex_vm::{execute_block, execution_db::ExecutionDB, get_state_transitions, EvmState}; fn main() { let (block, execution_db, parent_header) = read_inputs().expect("failed to read inputs"); diff --git a/crates/l2/sdk/Cargo.toml b/crates/l2/sdk/Cargo.toml index 76f61c4a8..c4c937e09 100644 --- a/crates/l2/sdk/Cargo.toml +++ b/crates/l2/sdk/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "ethereum_rust-sdk" +name = "ethrex-sdk" version.workspace = true edition.workspace = true [dependencies] -ethereum_rust-l2.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-rpc.workspace = true +ethrex-l2.workspace = true +ethrex-core.workspace = true +ethrex-rpc.workspace = true ethereum-types.workspace = true tokio.workspace = true @@ -16,5 +16,5 @@ secp256k1.workspace = true itertools = "0.13.0" [lib] -name = "ethereum_rust_l2_sdk" +name = "ethrex_l2_sdk" path = "src/sdk.rs" diff --git a/crates/l2/sdk/src/sdk.rs b/crates/l2/sdk/src/sdk.rs index 145a9cf47..ee7d472a5 100644 --- a/crates/l2/sdk/src/sdk.rs +++ b/crates/l2/sdk/src/sdk.rs @@ -1,5 +1,6 @@ -use ethereum_rust_core::types::{PrivilegedTxType, Transaction}; -use ethereum_rust_l2::utils::{ +use ethereum_types::{Address, H160, H256, U256}; +use ethrex_core::types::{PrivilegedTxType, Transaction}; +use ethrex_l2::utils::{ eth_client::{ errors::{EthClientError, GetTransactionReceiptError}, eth_sender::Overrides, @@ -7,8 +8,7 @@ use ethereum_rust_l2::utils::{ }, merkle_tree::merkle_proof, }; -use ethereum_rust_rpc::types::{block::BlockBodyWrapper, receipt::RpcReceipt}; -use ethereum_types::{Address, H160, H256, U256}; +use ethrex_rpc::types::{block::BlockBodyWrapper, receipt::RpcReceipt}; use itertools::Itertools; use keccak_hash::keccak; use secp256k1::SecretKey; diff --git a/crates/l2/tests/tests.rs b/crates/l2/tests/tests.rs index 081f73b39..a4b53931b 100644 --- a/crates/l2/tests/tests.rs +++ b/crates/l2/tests/tests.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethereum_rust_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use ethereum_types::{Address, H160, U256}; +use ethrex_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use keccak_hash::H256; use secp256k1::SecretKey; use std::{str::FromStr, time::Duration}; @@ -59,7 +59,7 @@ async fn testito() { println!("Depositing funds from L1 to L2"); let deposit_value = U256::from(1000000000000000000000u128); - let deposit_tx = ethereum_rust_l2_sdk::deposit( + let deposit_tx = ethrex_l2_sdk::deposit( deposit_value, l1_rich_wallet_address(), l1_rich_wallet_private_key(), @@ -71,7 +71,7 @@ async fn testito() { println!("Waiting for deposit transaction receipt"); let _deposit_tx_receipt = - ethereum_rust_l2_sdk::wait_for_transaction_receipt(deposit_tx, ð_client, 5).await; + ethrex_l2_sdk::wait_for_transaction_receipt(deposit_tx, ð_client, 5).await; // 3. Check balances on L1 and L2 @@ -131,7 +131,7 @@ async fn testito() { .unwrap(); assert!(l2_random_account_initial_balance.is_zero()); let transfer_value = U256::from(10000000000u128); - let transfer_tx = ethereum_rust_l2_sdk::transfer( + let transfer_tx = ethrex_l2_sdk::transfer( transfer_value, l1_rich_wallet_address(), random_account_address, @@ -141,7 +141,7 @@ async fn testito() { .await .unwrap(); let _transfer_tx_receipt = - ethereum_rust_l2_sdk::wait_for_transaction_receipt(transfer_tx, &proposer_client, 30).await; + ethrex_l2_sdk::wait_for_transaction_receipt(transfer_tx, &proposer_client, 30).await; // 5. Check balances on L2 @@ -176,7 +176,7 @@ async fn testito() { println!("Withdrawing funds from L2 to L1"); let withdraw_value = U256::from(100000000000000000000u128); - let withdraw_tx = ethereum_rust_l2_sdk::withdraw( + let withdraw_tx = ethrex_l2_sdk::withdraw( withdraw_value, l1_rich_wallet_address(), l1_rich_wallet_private_key(), @@ -185,7 +185,7 @@ async fn testito() { .await .unwrap(); let withdraw_tx_receipt = - ethereum_rust_l2_sdk::wait_for_transaction_receipt(withdraw_tx, &proposer_client, 30).await; + ethrex_l2_sdk::wait_for_transaction_receipt(withdraw_tx, &proposer_client, 30).await; // 7. Check balances on L1 and L2 @@ -240,7 +240,7 @@ async fn testito() { std::thread::sleep(Duration::from_secs(2)); } - let claim_tx = ethereum_rust_l2_sdk::claim_withdraw( + let claim_tx = ethrex_l2_sdk::claim_withdraw( withdraw_tx, withdraw_value, l1_rich_wallet_address(), @@ -252,7 +252,7 @@ async fn testito() { .unwrap(); let _claim_tx_receipt = - ethereum_rust_l2_sdk::wait_for_transaction_receipt(claim_tx, ð_client, 15).await; + ethrex_l2_sdk::wait_for_transaction_receipt(claim_tx, ð_client, 15).await; // 9. Check balances on L1 and L2 diff --git a/crates/l2/utils/config/l1_watcher.rs b/crates/l2/utils/config/l1_watcher.rs index 00a34a3f5..0b679aedd 100644 --- a/crates/l2/utils/config/l1_watcher.rs +++ b/crates/l2/utils/config/l1_watcher.rs @@ -13,7 +13,6 @@ pub struct L1WatcherConfig { pub max_block_step: U256, #[serde(deserialize_with = "secret_key_deserializer")] pub l2_proposer_private_key: SecretKey, - pub l2_proposer_address: Address, } impl L1WatcherConfig { diff --git a/crates/l2/utils/eth_client/errors.rs b/crates/l2/utils/eth_client/errors.rs index baa885332..99287d03f 100644 --- a/crates/l2/utils/eth_client/errors.rs +++ b/crates/l2/utils/eth_client/errors.rs @@ -1,4 +1,4 @@ -use ethereum_rust_rpc::utils::RpcRequest; +use ethrex_rpc::utils::RpcRequest; #[derive(Debug, thiserror::Error)] pub enum EthClientError { diff --git a/crates/l2/utils/eth_client/eth_sender.rs b/crates/l2/utils/eth_client/eth_sender.rs index 16969aeb6..e8c38e5d7 100644 --- a/crates/l2/utils/eth_client/eth_sender.rs +++ b/crates/l2/utils/eth_client/eth_sender.rs @@ -3,10 +3,10 @@ use crate::utils::eth_client::{ EthClient, RpcResponse, }; use bytes::Bytes; -use ethereum_rust_core::types::{GenericTransaction, TxKind}; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_rpc::utils::{RpcRequest, RpcRequestId}; use ethereum_types::{Address, U256}; +use ethrex_core::types::{GenericTransaction, TxKind}; +use ethrex_rlp::encode::RLPEncode; +use ethrex_rpc::utils::{RpcRequest, RpcRequestId}; use keccak_hash::{keccak, H256}; use secp256k1::SecretKey; use serde_json::json; diff --git a/crates/l2/utils/eth_client/mod.rs b/crates/l2/utils/eth_client/mod.rs index 467a61f21..6d504f3e0 100644 --- a/crates/l2/utils/eth_client/mod.rs +++ b/crates/l2/utils/eth_client/mod.rs @@ -6,12 +6,13 @@ use errors::{ GetTransactionReceiptError, SendRawTransactionError, }; use eth_sender::Overrides; -use ethereum_rust_core::types::{ +use ethereum_types::{Address, H256, U256}; +use ethrex_core::types::{ BlobsBundle, EIP1559Transaction, EIP4844Transaction, GenericTransaction, PrivilegedL2Transaction, PrivilegedTxType, Signable, TxKind, TxType, }; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_rpc::{ +use ethrex_rlp::encode::RLPEncode; +use ethrex_rpc::{ types::{ block::RpcBlock, receipt::{RpcLog, RpcReceipt}, @@ -19,17 +20,14 @@ use ethereum_rust_rpc::{ }, utils::{RpcErrorResponse, RpcRequest, RpcRequestId, RpcSuccessResponse}, }; -use ethereum_types::{Address, H256, U256}; use keccak_hash::keccak; use reqwest::Client; use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; use serde_json::json; -use sha2::{Digest, Sha256}; pub mod errors; pub mod eth_sender; -pub mod transaction; #[derive(Deserialize, Debug)] #[serde(untagged)] @@ -447,17 +445,7 @@ impl EthClient { overrides: Overrides, blobs_bundle: BlobsBundle, ) -> Result { - let blob_versioned_hashes = blobs_bundle - .commitments - .iter() - .map(|commitment| { - let mut hasher = Sha256::new(); - hasher.update(commitment); - let mut blob_versioned_hash = hasher.finalize(); - blob_versioned_hash[0] = 0x01; // EIP-4844 versioning - H256::from_slice(blob_versioned_hash.as_slice()) - }) - .collect::>(); + let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); let mut tx = EIP4844Transaction { to, @@ -630,15 +618,15 @@ impl EthClient { #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct GetTransactionByHashTransaction { - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub chain_id: u64, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub nonce: u64, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub max_priority_fee_per_gas: u64, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub max_fee_per_gas: u64, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub gas_limit: u64, #[serde(default)] pub to: Address, @@ -652,9 +640,9 @@ pub struct GetTransactionByHashTransaction { pub r#type: TxType, #[serde(default)] pub signature_y_parity: bool, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub signature_r: u64, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub signature_s: u64, #[serde(default)] pub block_number: U256, @@ -664,6 +652,6 @@ pub struct GetTransactionByHashTransaction { pub from: Address, #[serde(default)] pub hash: H256, - #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] pub transaction_index: u64, } diff --git a/crates/l2/utils/eth_client/transaction.rs b/crates/l2/utils/eth_client/transaction.rs deleted file mode 100644 index 212538857..000000000 --- a/crates/l2/utils/eth_client/transaction.rs +++ /dev/null @@ -1,24 +0,0 @@ -use bytes::Bytes; -use c_kzg::{Blob, BYTES_PER_BLOB}; - -pub fn blob_from_bytes(bytes: Bytes) -> Result { - // We set the first byte of every 32-bytes chunk to 0x00 - // so it's always under the field module. - if bytes.len() > BYTES_PER_BLOB * 31 / 32 { - return Err(c_kzg::Error::InvalidBytesLength(format!( - "Bytes too long for a Blob ({})", - bytes.len() - ))); - } - - let mut buf = [0u8; BYTES_PER_BLOB]; - buf[..(bytes.len() * 32).div_ceil(31)].copy_from_slice( - &bytes - .chunks(31) - .map(|x| [&[0x00], x].concat()) - .collect::>() - .concat(), - ); - - Blob::from_bytes(&buf) -} diff --git a/crates/l2/utils/test_data_io.rs b/crates/l2/utils/test_data_io.rs index dcc7833ca..782b0b7e0 100644 --- a/crates/l2/utils/test_data_io.rs +++ b/crates/l2/utils/test_data_io.rs @@ -1,6 +1,6 @@ -use ethereum_rust_core::types::{Block, Genesis}; -use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; -use ethereum_rust_storage::Store; +use ethrex_core::types::{Block, Genesis}; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethrex_storage::Store; use tracing::info; use std::{ @@ -9,13 +9,13 @@ use std::{ path::PathBuf, }; -// From cmd/ethereum_rust +// From cmd/ethrex pub fn read_chain_file(chain_rlp_path: &str) -> Vec { let chain_file = File::open(chain_rlp_path).expect("Failed to open chain rlp file"); _chain_file(chain_file).expect("Failed to decode chain rlp file") } -// From cmd/ethereum_rust +// From cmd/ethrex pub fn read_genesis_file(genesis_file_path: &str) -> Genesis { let genesis_file = std::fs::File::open(genesis_file_path).expect("Failed to open genesis file"); _genesis_file(genesis_file).expect("Failed to decode genesis file") @@ -51,7 +51,7 @@ pub fn generate_rlp( Ok(()) } -// From cmd/ethereum_rust/decode.rs +// From cmd/ethrex/decode.rs fn _chain_file(file: File) -> Result, Box> { let mut chain_rlp_reader = BufReader::new(file); let mut buf = vec![]; @@ -65,7 +65,7 @@ fn _chain_file(file: File) -> Result, Box> { Ok(blocks) } -// From cmd/ethereum_rust/decode.rs +// From cmd/ethrex/decode.rs fn _genesis_file(file: File) -> Result { let genesis_reader = BufReader::new(file); serde_json::from_reader(genesis_reader) diff --git a/crates/networking/p2p/Cargo.toml b/crates/networking/p2p/Cargo.toml index 55661aa8b..f3db303f9 100644 --- a/crates/networking/p2p/Cargo.toml +++ b/crates/networking/p2p/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethereum_rust-net" +name = "ethrex-net" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum_rust-core.workspace = true -ethereum_rust-rlp.workspace = true -ethereum_rust-storage.workspace = true +ethrex-core.workspace = true +ethrex-rlp.workspace = true +ethrex-storage.workspace = true tracing.workspace = true tokio.workspace = true @@ -21,6 +21,8 @@ snap.workspace = true k256 = { version = "0.13.3", features = ["ecdh"] } sha3 = "0.10.8" +serde_json = "1.0.117" + # RLPx concat-kdf = "0.1.0" hmac = "0.12.1" diff --git a/crates/networking/p2p/README.md b/crates/networking/p2p/README.md index 39030074b..9a2187416 100644 --- a/crates/networking/p2p/README.md +++ b/crates/networking/p2p/README.md @@ -19,7 +19,7 @@ At startup, the discovery server launches three concurrent tokio tasks: Before starting these tasks, we run a [startup](#startup) process to connect to an array of initial nodes. -Before diving into what each task does, first, we need to understand how we are storing our nodes. Nodes are stored in an in-memory matrix which we call a [Kademlia table](https://github.com/lambdaclass/ethereum_rust/blob/main/crates/net/kademlia.rs#L20-L23), though it isn't really a Kademlia table as we don't thoroughly follow the spec but we take it as a reference, you can read more [here](https://en.wikipedia.org/wiki/Kademlia). This table holds: +Before diving into what each task does, first, we need to understand how we are storing our nodes. Nodes are stored in an in-memory matrix which we call a [Kademlia table](https://github.com/lambdaclass/ethrex/blob/main/crates/net/kademlia.rs#L20-L23), though it isn't really a Kademlia table as we don't thoroughly follow the spec but we take it as a reference, you can read more [here](https://en.wikipedia.org/wiki/Kademlia). This table holds: - Our `node_id`: `node_id`s are derived from the public key. They are the 64 bytes starting from index 1 of the encoded pub key. - A vector of 256 `bucket`s which holds: @@ -46,7 +46,7 @@ Before starting the server, we do a startup where we connect to an array of seed - Inserting them into our table - Pinging them to notify our presence, so they acknowledge us. -This startup is far from being completed. The current state allows us to do basic tests and connections. Later, we want to do a real startup by first trying to connect to those nodes we were previously connected. For that, we'd need to store nodes on the database. If those nodes aren't enough to fill our table, then we also ping some bootnodes, which could be hardcoded or received through the cli. Current issues are opened regarding [startup](https://github.com/lambdaclass/ethereum_rust/issues/398) and [nodes db](https://github.com/lambdaclass/ethereum_rust/issues/454). +This startup is far from being completed. The current state allows us to do basic tests and connections. Later, we want to do a real startup by first trying to connect to those nodes we were previously connected. For that, we'd need to store nodes on the database. If those nodes aren't enough to fill our table, then we also ping some bootnodes, which could be hardcoded or received through the cli. Current issues are opened regarding [startup](https://github.com/lambdaclass/ethrex/issues/398) and [nodes db](https://github.com/lambdaclass/ethrex/issues/454). ### Listen loop @@ -55,8 +55,8 @@ The listen loop handles messages sent to our socket. The spec defines 6 types of - **Ping**: Responds with a `pong` message. If the peer is not in our table we add it, if the corresponding bucket is already filled then we add it as a replacement for that bucket. If it was inserted we send a `ping from our end to get an endpoint proof. - **Pong**: Verifies that the `pong` corresponds to a previously sent `ping`, if so we mark the peer as proven. - **FindNodes**: Responds with a `neighbors` message that contains as many as the 16 closest nodes from the given target. A target is a pubkey provided by the peer in the message. The response can't be sent in one packet as it might exceed the discv4 max packet size. So we split it into different packets. -- **Neighbors**: First we verify that we have sent the corresponding `find_node` message. If so, we receive the peers, store them, and ping them. Also, every [`find_node` request](https://github.com/lambdaclass/ethereum_rust/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/discv4.rs#L305-L314) may have a [tokio `Sender`](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html) attached, if that is the case, we forward the nodes from the message through the channel. This becomes useful when waiting for a `find_node` response, [something we do in the lookups](https://github.com/lambdaclass/ethereum_rust/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/net.rs#L517-L570). -- **ENRRequest**: currently not implemented see [here](https://github.com/lambdaclass/ethereum_rust/issues/432). +- **Neighbors**: First we verify that we have sent the corresponding `find_node` message. If so, we receive the peers, store them, and ping them. Also, every [`find_node` request](https://github.com/lambdaclass/ethrex/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/discv4.rs#L305-L314) may have a [tokio `Sender`](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html) attached, if that is the case, we forward the nodes from the message through the channel. This becomes useful when waiting for a `find_node` response, [something we do in the lookups](https://github.com/lambdaclass/ethrex/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/net.rs#L517-L570). +- **ENRRequest**: currently not implemented see [here](https://github.com/lambdaclass/ethrex/issues/432). - **ENRResponse**: same as above. ### Re-validations @@ -103,7 +103,7 @@ Finally, here is an example of how you could build a network and see how they co We'll have three nodes: `a`, `b`, and `c`, we'll start `a`, then `b` setting `a` as a bootnode, and finally we'll start `c` with `b` as bootnode we should see that `c` connects to both `a` and `b` and so all the network should be connected. **node a**: -`cargo run --bin ethereum_rust --network test_data/kurtosis.json` +`cargo run --bin ethrex --network test_data/kurtosis.json` We get the `enode` by querying the node_info: `curl http://localhost:8545 \ @@ -115,7 +115,7 @@ We get the `enode` by querying the node_info: We start a new server passing the `node_a` `enode` as bootnodes ```bash -cargo run --bin ethereum_rust --network ./test_data/kurtosis.json --bootnodes=`NODE_A_ENODE` \ +cargo run --bin ethrex --network ./test_data/kurtosis.json --bootnodes=`NODE_A_ENODE` \ --authrpc.port=8552 --http.port=8546 --p2p.port=30305 --discovery.port=3036 ``` @@ -123,7 +123,7 @@ cargo run --bin ethereum_rust --network ./test_data/kurtosis.json --bootnodes=`N Finally, with `node_c` we connect to `node_b`. When the lookup runs, `node_c` should end up connecting to `node_a`: ```bash - cargo run --bin ethereum_rust --network ./test_data/kurtosis.json --bootnodes=`NODE_B_ENODE`" \ + cargo run --bin ethrex --network ./test_data/kurtosis.json --bootnodes=`NODE_B_ENODE`" \ --authrpc.port=8553 --http.port=8547 --p2p.port=30308 --discovery.port=30310 ``` diff --git a/crates/networking/p2p/bootnode.rs b/crates/networking/p2p/bootnode.rs index 0a0bbf2f8..c4caed177 100644 --- a/crates/networking/p2p/bootnode.rs +++ b/crates/networking/p2p/bootnode.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::H512; +use ethrex_core::H512; use std::{net::SocketAddr, num::ParseIntError, str::FromStr}; #[derive(Debug, Clone, PartialEq, Eq, Copy)] @@ -12,7 +12,7 @@ impl FromStr for BootNode { /// Takes a str with the format "enode://nodeID@IPaddress:port" and /// parses it to a BootNode // TODO: fix it to support different UDP and TCP ports, according to - // https://github.com/lambdaclass/lambda_ethereum_rust/issues/905 + // https://github.com/lambdaclass/lambda_ethrex/issues/905 fn from_str(input: &str) -> Result { // TODO: error handling let node_id = H512::from_str(&input[8..136]).expect("Failed to parse node id"); diff --git a/crates/networking/p2p/discv4.rs b/crates/networking/p2p/discv4.rs index 9a76bd510..692c64066 100644 --- a/crates/networking/p2p/discv4.rs +++ b/crates/networking/p2p/discv4.rs @@ -2,8 +2,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::types::{Endpoint, Node, NodeRecord}; use bytes::BufMut; -use ethereum_rust_core::{H256, H512, H520}; -use ethereum_rust_rlp::{ +use ethrex_core::{H256, H512, H520}; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -509,7 +509,7 @@ impl RLPEncode for ENRResponseMessage { mod tests { use super::*; use bytes::Bytes; - use ethereum_rust_core::{H256, H264}; + use ethrex_core::{H256, H264}; use std::fmt::Write; use std::net::IpAddr; use std::num::ParseIntError; diff --git a/crates/networking/p2p/kademlia.rs b/crates/networking/p2p/kademlia.rs index 26578a540..94b665001 100644 --- a/crates/networking/p2p/kademlia.rs +++ b/crates/networking/p2p/kademlia.rs @@ -2,7 +2,7 @@ use crate::{ discv4::{time_now_unix, FindNodeRequest}, types::Node, }; -use ethereum_rust_core::{H256, H512, U256}; +use ethrex_core::{H256, H512, U256}; use sha3::{Digest, Keccak256}; use tokio::sync::mpsc::UnboundedSender; diff --git a/crates/networking/p2p/net.rs b/crates/networking/p2p/net.rs index 9b027fa92..b7cb064f7 100644 --- a/crates/networking/p2p/net.rs +++ b/crates/networking/p2p/net.rs @@ -10,15 +10,15 @@ use discv4::{ get_expiration, is_expired, time_now_unix, time_since_in_hs, FindNodeMessage, Message, NeighborsMessage, Packet, PingMessage, PongMessage, }; -use ethereum_rust_core::{H256, H512}; -use ethereum_rust_storage::Store; +use ethrex_core::{H256, H512}; +use ethrex_storage::Store; use k256::{ ecdsa::SigningKey, elliptic_curve::{sec1::ToEncodedPoint, PublicKey}, }; use kademlia::{bucket_number, KademliaTable, MAX_NODES_PER_BUCKET}; use rand::rngs::OsRng; -use rlpx::{connection::RLPxConnection, error::RLPxError, message::Message as RLPxMessage}; +use rlpx::{connection::RLPxConnection, message::Message as RLPxMessage}; use tokio::{ net::{TcpSocket, TcpStream, UdpSocket}, sync::{broadcast, Mutex}, @@ -345,7 +345,7 @@ async fn discovery_startup( ip: bootnode.socket_address.ip(), udp_port: bootnode.socket_address.port(), // TODO: udp port can differ from tcp port. - // see https://github.com/lambdaclass/lambda_ethereum_rust/issues/905 + // see https://github.com/lambdaclass/lambda_ethrex/issues/905 tcp_port: bootnode.socket_address.port(), node_id: bootnode.node_id, }); @@ -782,8 +782,8 @@ async fn handle_peer_as_receiver( table: Arc>, connection_broadcast: broadcast::Sender<(tokio::task::Id, Arc)>, ) { - let conn = RLPxConnection::receiver(signer, stream, storage, connection_broadcast); - handle_peer(conn, table).await; + let mut conn = RLPxConnection::receiver(signer, stream, storage, connection_broadcast); + conn.start_peer(table).await; } async fn handle_peer_as_initiator( @@ -801,40 +801,13 @@ async fn handle_peer_as_initiator( .await .unwrap(); match RLPxConnection::initiator(signer, msg, stream, storage, connection_broadcast).await { - Ok(conn) => handle_peer(conn, table).await, + Ok(mut conn) => conn.start_peer(table).await, Err(e) => { error!("Error: {e}, Could not start connection with {node:?}"); } } } -async fn handle_peer(mut conn: RLPxConnection, table: Arc>) { - // Perform handshake - if let Err(e) = conn.handshake().await { - peer_conn_failed("Handshake failed", e, conn, table).await; - } else { - // Handshake OK: handle connection - if let Err(e) = conn.handle_peer_conn().await { - peer_conn_failed("Error during RLPx connection", e, conn, table).await; - } - } -} - -async fn peer_conn_failed( - error_text: &str, - error: RLPxError, - conn: RLPxConnection, - table: Arc>, -) { - if let Ok(node_id) = conn.get_remote_node_id() { - // Discard peer from kademlia table - info!("{error_text}: ({error}), discarding peer {node_id}"); - table.lock().await.replace_peer(node_id); - } else { - info!("{error_text}: ({error}), unknown peer") - } -} - pub fn node_id_from_signing_key(signer: &SigningKey) -> H512 { let public_key = PublicKey::from(signer.verifying_key()); let encoded = public_key.to_encoded_point(false); @@ -844,7 +817,7 @@ pub fn node_id_from_signing_key(signer: &SigningKey) -> H512 { #[cfg(test)] mod tests { use super::*; - use ethereum_rust_storage::EngineType; + use ethrex_storage::EngineType; use kademlia::bucket_number; use rand::rngs::OsRng; use std::{ diff --git a/crates/networking/p2p/rlpx/connection.rs b/crates/networking/p2p/rlpx/connection.rs index b319f09c8..bb4815b62 100644 --- a/crates/networking/p2p/rlpx/connection.rs +++ b/crates/networking/p2p/rlpx/connection.rs @@ -9,7 +9,7 @@ use crate::{ }, handshake::encode_ack_message, message::Message, - p2p::{self, PingMessage, PongMessage}, + p2p::{self, DisconnectMessage, PingMessage, PongMessage}, utils::id2pubkey, }, snap::{ @@ -28,9 +28,9 @@ use super::{ utils::{ecdh_xchng, pubkey2id}, }; use aes::cipher::KeyIvInit; -use ethereum_rust_core::{H256, H512}; -use ethereum_rust_rlp::decode::RLPDecode; -use ethereum_rust_storage::Store; +use ethrex_core::{H256, H512}; +use ethrex_rlp::decode::RLPDecode; +use ethrex_storage::Store; use k256::{ ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}, PublicKey, SecretKey, @@ -38,7 +38,10 @@ use k256::{ use sha3::{Digest, Keccak256}; use tokio::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, - sync::broadcast::{self, error::RecvError}, + sync::{ + broadcast::{self, error::RecvError}, + Mutex, + }, task, time::{sleep, Instant}, }; @@ -85,7 +88,7 @@ impl RLPxConnection { storage, capabilities: vec![], next_periodic_task_check: Instant::now() + PERIODIC_TASKS_CHECK_INTERVAL, - connection_broadcast_send: connection_broadcast.clone(), + connection_broadcast_send: connection_broadcast, } } @@ -139,7 +142,50 @@ impl RLPxConnection { )) } - pub async fn handshake(&mut self) -> Result<(), RLPxError> { + /// Starts a handshake and runs the peer connection. + /// It runs in it's own task and blocks until the connection is dropped + pub async fn start_peer(&mut self, table: Arc>) { + // Perform handshake + if let Err(e) = self.handshake().await { + self.peer_conn_failed("Handshake failed", e, table).await; + } else { + // Handshake OK: handle connection + if let Err(e) = self.handle_peer_conn().await { + self.peer_conn_failed("Error during RLPx connection", e, table) + .await; + } + } + } + + async fn peer_conn_failed( + &mut self, + error_text: &str, + error: RLPxError, + table: Arc>, + ) { + self.send(Message::Disconnect(DisconnectMessage { + reason: self.match_disconnect_reason(&error), + })) + .await + .unwrap_or_else(|e| info!("Could not send Disconnect message: ({e})")); + if let Ok(node_id) = self.get_remote_node_id() { + // Discard peer from kademlia table + info!("{error_text}: ({error}), discarding peer {node_id}"); + table.lock().await.replace_peer(node_id); + } else { + info!("{error_text}: ({error}), unknown peer") + } + } + + fn match_disconnect_reason(&self, error: &RLPxError) -> Option { + match error { + RLPxError::RLPDecodeError(_) => Some(2_u8), + // TODO build a proper matching between error types and disconnection reasons + _ => None, + } + } + + async fn handshake(&mut self) -> Result<(), RLPxError> { match &self.state { RLPxConnectionState::Initiator(_) => { self.send_auth().await?; @@ -161,7 +207,7 @@ impl RLPxConnection { Ok(()) } - pub async fn exchange_hello_messages(&mut self) -> Result<(), RLPxError> { + async fn exchange_hello_messages(&mut self) -> Result<(), RLPxError> { let hello_msg = Message::Hello(p2p::HelloMessage::new( SUPPORTED_CAPABILITIES.to_vec(), PublicKey::from(self.signer.verifying_key()), @@ -191,7 +237,7 @@ impl RLPxConnection { } } - pub async fn handle_peer_conn(&mut self) -> Result<(), RLPxError> { + async fn handle_peer_conn(&mut self) -> Result<(), RLPxError> { if let RLPxConnectionState::Established(_) = &self.state { self.init_peer_conn().await?; info!("Started peer main loop"); @@ -244,7 +290,7 @@ impl RLPxConnection { } } - pub fn get_remote_node_id(&self) -> Result { + fn get_remote_node_id(&self) -> Result { if let RLPxConnectionState::Established(state) = &self.state { Ok(state.remote_node_id) } else { @@ -277,13 +323,10 @@ impl RLPxConnection { Message::Pong(_) => { // We ignore received Pong messages } - // Implmenent Status vaidations - // https://github.com/lambdaclass/lambda_ethereum_rust/issues/420 - Message::Status(_) if !peer_supports_eth => { + Message::Status(msg_data) if !peer_supports_eth => { info!("Received Status"); - // TODO: Check peer's status message. + backend::validate_status(msg_data, &self.storage)? } - // TODO: implement handlers for each message type Message::GetAccountRange(req) => { let response = process_account_range_request(req, self.storage.clone())?; self.send(Message::AccountRange(response)).await? @@ -354,16 +397,16 @@ impl RLPxConnection { // Sending eth Status if peer supports it if self.capabilities.contains(&CAP_ETH) { let status = backend::get_status(&self.storage)?; + info!("Sending status"); self.send(Message::Status(status)).await?; // The next immediate message in the ETH protocol is the // status, reference here: // https://github.com/ethereum/devp2p/blob/master/caps/eth.md#status-0x00 - // let Ok(Message::Status(_)) = self.receive().await else { - // self.capabilities.iter_mut().position(|cap| cap == &CAP_ETH).map(|indx| self.capabilities.remove(indx)); - // } match self.receive().await? { - Message::Status(_) => { + Message::Status(msg_data) => { // TODO: Check message status is correct. + info!("Received Status"); + backend::validate_status(msg_data, &self.storage)? } _msg => { return Err(RLPxError::HandshakeError( @@ -372,7 +415,6 @@ impl RLPxConnection { } } } - // TODO: add new capabilities startup when required (eg. snap) Ok(()) } diff --git a/crates/networking/p2p/rlpx/error.rs b/crates/networking/p2p/rlpx/error.rs index a3b6efa46..bc8ca31b4 100644 --- a/crates/networking/p2p/rlpx/error.rs +++ b/crates/networking/p2p/rlpx/error.rs @@ -1,5 +1,5 @@ -use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; -use ethereum_rust_storage::error::StoreError; +use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethrex_storage::error::StoreError; use thiserror::Error; use tokio::sync::broadcast::error::RecvError; diff --git a/crates/networking/p2p/rlpx/eth/backend.rs b/crates/networking/p2p/rlpx/eth/backend.rs index cc51bd8f2..9ba059dcd 100644 --- a/crates/networking/p2p/rlpx/eth/backend.rs +++ b/crates/networking/p2p/rlpx/eth/backend.rs @@ -1,5 +1,5 @@ -use ethereum_rust_core::{types::ForkId, U256}; -use ethereum_rust_storage::Store; +use ethrex_core::{types::ForkId, U256}; +use ethrex_storage::Store; use crate::rlpx::error::RLPxError; @@ -35,3 +35,92 @@ pub fn get_status(storage: &Store) -> Result { fork_id, }) } + +pub fn validate_status(msg_data: StatusMessage, storage: &Store) -> Result<(), RLPxError> { + let chain_config = storage.get_chain_config()?; + + // These blocks must always be available + let genesis_header = storage + .get_block_header(0)? + .ok_or(RLPxError::NotFound("Genesis Block".to_string()))?; + let block_number = storage + .get_latest_block_number()? + .ok_or(RLPxError::NotFound("Latest Block Number".to_string()))?; + let block_header = storage + .get_block_header(block_number)? + .ok_or(RLPxError::NotFound(format!("Block {block_number}")))?; + + let genesis = genesis_header.compute_block_hash(); + let fork_id = ForkId::new(chain_config, genesis, block_header.timestamp, block_number); + + //Check networkID + if msg_data.network_id != chain_config.chain_id { + return Err(RLPxError::HandshakeError( + "Network Id does not match".to_string(), + )); + } + //Check Protocol Version + if msg_data.eth_version != ETH_VERSION { + return Err(RLPxError::HandshakeError( + "Eth protocol version does not match".to_string(), + )); + } + //Check Genesis + if msg_data.genesis != genesis { + return Err(RLPxError::HandshakeError( + "Genesis does not match".to_string(), + )); + } + // Check ForkID + if msg_data.fork_id != fork_id { + return Err(RLPxError::HandshakeError( + "Fork Id does not match".to_string(), + )); + } + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::validate_status; + use crate::rlpx::eth::status::StatusMessage; + use ethrex_core::{ + types::{ForkId, Genesis}, + H256, U256, + }; + use ethrex_storage::{EngineType, Store}; + use std::{fs::File, io::BufReader}; + + #[test] + // TODO add tests for failing validations + fn test_validate_status() { + // Setup + // TODO we should have this setup exported to some test_utils module and use from there + let storage = + Store::new("temp.db", EngineType::InMemory).expect("Failed to create test DB"); + let file = File::open("../../../test_data/genesis-execution-api.json") + .expect("Failed to open genesis file"); + let reader = BufReader::new(file); + let genesis: Genesis = + serde_json::from_reader(reader).expect("Failed to deserialize genesis file"); + storage + .add_initial_state(genesis.clone()) + .expect("Failed to add genesis block to DB"); + let config = genesis.config; + let total_difficulty = U256::from(config.terminal_total_difficulty.unwrap_or_default()); + let genesis_hash = genesis.get_block().hash(); + let fork_id = ForkId::new(config, genesis_hash, 2707305664, 123); + + let message = StatusMessage { + eth_version: 68u32, + network_id: 3503995874084926, + total_difficulty, + block_hash: H256::random(), + genesis: genesis_hash, + fork_id, + }; + let result = validate_status(message, &storage); + assert!(result.is_ok()); + } +} diff --git a/crates/networking/p2p/rlpx/eth/blocks.rs b/crates/networking/p2p/rlpx/eth/blocks.rs index 1ec932c0c..4d50374c0 100644 --- a/crates/networking/p2p/rlpx/eth/blocks.rs +++ b/crates/networking/p2p/rlpx/eth/blocks.rs @@ -3,14 +3,14 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethereum_rust_core::types::{BlockBody, BlockHash, BlockHeader, BlockNumber}; -use ethereum_rust_rlp::{ +use ethrex_core::types::{BlockBody, BlockHash, BlockHeader, BlockNumber}; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; -use ethereum_rust_storage::Store; +use ethrex_storage::Store; use tracing::error; pub const HASH_FIRST_BYTE_DECODER: u8 = 160; @@ -303,7 +303,7 @@ impl RLPxMessage for BlockBodies { #[cfg(test)] mod tests { - use ethereum_rust_core::types::BlockHash; + use ethrex_core::types::BlockHash; use crate::rlpx::{ eth::blocks::{BlockBodies, GetBlockBodies, GetBlockHeaders}, diff --git a/crates/networking/p2p/rlpx/eth/receipts.rs b/crates/networking/p2p/rlpx/eth/receipts.rs index 0eae0af0a..0ec74af35 100644 --- a/crates/networking/p2p/rlpx/eth/receipts.rs +++ b/crates/networking/p2p/rlpx/eth/receipts.rs @@ -3,8 +3,8 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethereum_rust_core::types::{BlockHash, Receipt}; -use ethereum_rust_rlp::{ +use ethrex_core::types::{BlockHash, Receipt}; +use ethrex_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; @@ -86,7 +86,7 @@ impl RLPxMessage for Receipts { #[cfg(test)] mod tests { - use ethereum_rust_core::types::{BlockHash, Receipt}; + use ethrex_core::types::{BlockHash, Receipt}; use crate::rlpx::{ eth::receipts::{GetReceipts, Receipts}, diff --git a/crates/networking/p2p/rlpx/eth/status.rs b/crates/networking/p2p/rlpx/eth/status.rs index b0e21bbf8..f39c36dcf 100644 --- a/crates/networking/p2p/rlpx/eth/status.rs +++ b/crates/networking/p2p/rlpx/eth/status.rs @@ -3,11 +3,11 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethereum_rust_core::{ +use ethrex_core::{ types::{BlockHash, ForkId}, U256, }; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; diff --git a/crates/networking/p2p/rlpx/eth/transactions.rs b/crates/networking/p2p/rlpx/eth/transactions.rs index d40e85dec..8ad8c627c 100644 --- a/crates/networking/p2p/rlpx/eth/transactions.rs +++ b/crates/networking/p2p/rlpx/eth/transactions.rs @@ -1,6 +1,6 @@ use bytes::BufMut; -use ethereum_rust_core::{types::Transaction, H256}; -use ethereum_rust_rlp::{ +use ethrex_core::{types::Transaction, H256}; +use ethrex_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; @@ -218,7 +218,7 @@ impl RLPxMessage for PooledTransactions { #[cfg(test)] mod tests { - use ethereum_rust_core::{types::Transaction, H256}; + use ethrex_core::{types::Transaction, H256}; use crate::rlpx::{ eth::transactions::{GetPooledTransactions, PooledTransactions}, diff --git a/crates/networking/p2p/rlpx/frame.rs b/crates/networking/p2p/rlpx/frame.rs index 0480c5e8d..f766f8e4d 100644 --- a/crates/networking/p2p/rlpx/frame.rs +++ b/crates/networking/p2p/rlpx/frame.rs @@ -2,8 +2,8 @@ use aes::{ cipher::{BlockEncrypt as _, KeyInit as _, StreamCipher as _}, Aes256Enc, }; -use ethereum_rust_core::H128; -use ethereum_rust_rlp::encode::RLPEncode as _; +use ethrex_core::H128; +use ethrex_rlp::encode::RLPEncode as _; use sha3::Digest as _; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; diff --git a/crates/networking/p2p/rlpx/handshake.rs b/crates/networking/p2p/rlpx/handshake.rs index 87caa0038..91ea09d41 100644 --- a/crates/networking/p2p/rlpx/handshake.rs +++ b/crates/networking/p2p/rlpx/handshake.rs @@ -1,7 +1,7 @@ use crate::rlpx::utils::{ecdh_xchng, id2pubkey, kdf, pubkey2id, sha256, sha256_hmac}; use aes::cipher::{KeyIvInit, StreamCipher}; -use ethereum_rust_core::{Signature, H128, H256, H512}; -use ethereum_rust_rlp::{ +use ethrex_core::{Signature, H128, H256, H512}; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -320,7 +320,7 @@ impl RLPDecode for AckMessage { mod tests { use std::str::FromStr; - use ethereum_rust_core::H256; + use ethrex_core::H256; use hex_literal::hex; use k256::SecretKey; diff --git a/crates/networking/p2p/rlpx/message.rs b/crates/networking/p2p/rlpx/message.rs index 6415d74e4..d44f1b4ca 100644 --- a/crates/networking/p2p/rlpx/message.rs +++ b/crates/networking/p2p/rlpx/message.rs @@ -1,5 +1,5 @@ use bytes::BufMut; -use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; use std::fmt::Display; use super::eth::blocks::{BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders}; @@ -11,7 +11,7 @@ use super::snap::{ StorageRanges, TrieNodes, }; -use ethereum_rust_rlp::encode::RLPEncode; +use ethrex_rlp::encode::RLPEncode; pub trait RLPxMessage: Sized { fn encode(&self, buf: &mut dyn BufMut) -> Result<(), RLPEncodeError>; diff --git a/crates/networking/p2p/rlpx/p2p.rs b/crates/networking/p2p/rlpx/p2p.rs index 70573c021..fdc752a83 100644 --- a/crates/networking/p2p/rlpx/p2p.rs +++ b/crates/networking/p2p/rlpx/p2p.rs @@ -1,6 +1,6 @@ use bytes::BufMut; -use ethereum_rust_core::H512; -use ethereum_rust_rlp::{ +use ethrex_core::H512; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, diff --git a/crates/networking/p2p/rlpx/snap.rs b/crates/networking/p2p/rlpx/snap.rs index abec6c9f1..2c7643790 100644 --- a/crates/networking/p2p/rlpx/snap.rs +++ b/crates/networking/p2p/rlpx/snap.rs @@ -3,11 +3,11 @@ use super::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::{BufMut, Bytes}; -use ethereum_rust_core::{ +use ethrex_core::{ types::{AccountState, EMPTY_KECCACK_HASH, EMPTY_TRIE_HASH}, H256, U256, }; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, diff --git a/crates/networking/p2p/rlpx/utils.rs b/crates/networking/p2p/rlpx/utils.rs index 348932401..6efe31d59 100644 --- a/crates/networking/p2p/rlpx/utils.rs +++ b/crates/networking/p2p/rlpx/utils.rs @@ -1,5 +1,5 @@ -use ethereum_rust_core::H512; -use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethrex_core::H512; +use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; use k256::{ elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}, EncodedPoint, PublicKey, SecretKey, diff --git a/crates/networking/p2p/snap.rs b/crates/networking/p2p/snap.rs index fefd7374b..498101954 100644 --- a/crates/networking/p2p/snap.rs +++ b/crates/networking/p2p/snap.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_storage::{error::StoreError, Store}; +use ethrex_rlp::encode::RLPEncode; +use ethrex_storage::{error::StoreError, Store}; use crate::rlpx::{ error::RLPxError, @@ -157,9 +157,9 @@ pub fn process_trie_nodes_request( mod tests { use std::str::FromStr; - use ethereum_rust_core::{types::AccountState, BigEndianHash, H256}; - use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; - use ethereum_rust_storage::EngineType; + use ethrex_core::{types::AccountState, BigEndianHash, H256}; + use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; + use ethrex_storage::EngineType; use crate::rlpx::snap::AccountStateSlim; diff --git a/crates/networking/p2p/types.rs b/crates/networking/p2p/types.rs index c44397a63..583f943a9 100644 --- a/crates/networking/p2p/types.rs +++ b/crates/networking/p2p/types.rs @@ -1,6 +1,6 @@ use bytes::{BufMut, Bytes}; -use ethereum_rust_core::H512; -use ethereum_rust_rlp::{ +use ethrex_core::H512; +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/networking/rpc/Cargo.toml b/crates/networking/rpc/Cargo.toml index f461c4033..a694b8243 100644 --- a/crates/networking/rpc/Cargo.toml +++ b/crates/networking/rpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethereum_rust-rpc" +name = "ethrex-rpc" version = "0.1.0" edition = "2021" @@ -13,12 +13,12 @@ tokio.workspace = true bytes.workspace = true tracing.workspace = true tracing-subscriber.workspace = true -ethereum_rust-core.workspace = true -ethereum_rust-storage.workspace = true -ethereum_rust-vm.workspace = true -ethereum_rust-blockchain.workspace = true -ethereum_rust-net.workspace = true -ethereum_rust-rlp.workspace = true +ethrex-core.workspace = true +ethrex-storage.workspace = true +ethrex-vm.workspace = true +ethrex-blockchain.workspace = true +ethrex-net.workspace = true +ethrex-rlp.workspace = true hex.workspace = true axum-extra = { version = "0.9.3", features = ["typed-header"] } jsonwebtoken.workspace = true diff --git a/crates/networking/rpc/admin/mod.rs b/crates/networking/rpc/admin/mod.rs index 1891f0e1a..53311de94 100644 --- a/crates/networking/rpc/admin/mod.rs +++ b/crates/networking/rpc/admin/mod.rs @@ -1,6 +1,6 @@ -use ethereum_rust_core::types::ChainConfig; -use ethereum_rust_net::types::Node; -use ethereum_rust_storage::Store; +use ethrex_core::types::ChainConfig; +use ethrex_net::types::Node; +use ethrex_storage::Store; use serde::Serialize; use serde_json::Value; use std::collections::HashMap; @@ -41,7 +41,7 @@ pub fn node_info(storage: Store, local_node: Node) -> Result { let node_info = NodeInfo { enode: enode_url, id: hex::encode(local_node.node_id), - name: "ethereum_rust/0.1.0/rust1.80".to_string(), + name: "ethrex/0.1.0/rust1.80".to_string(), ip: local_node.ip.to_string(), ports: Ports { discovery: local_node.udp_port, diff --git a/crates/networking/rpc/engine/exchange_transition_config.rs b/crates/networking/rpc/engine/exchange_transition_config.rs index 7d0faa2f1..751b1ad49 100644 --- a/crates/networking/rpc/engine/exchange_transition_config.rs +++ b/crates/networking/rpc/engine/exchange_transition_config.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::{serde_utils, H256}; +use ethrex_core::{serde_utils, H256}; use serde::{Deserialize, Serialize}; use serde_json::Value; use tracing::{info, warn}; diff --git a/crates/networking/rpc/engine/fork_choice.rs b/crates/networking/rpc/engine/fork_choice.rs index 78b7f7e89..a6bd6c1e1 100644 --- a/crates/networking/rpc/engine/fork_choice.rs +++ b/crates/networking/rpc/engine/fork_choice.rs @@ -1,4 +1,4 @@ -use ethereum_rust_blockchain::{ +use ethrex_blockchain::{ error::{ChainError, InvalidForkChoice}, fork_choice::apply_fork_choice, latest_canonical_block_hash, diff --git a/crates/networking/rpc/engine/payload.rs b/crates/networking/rpc/engine/payload.rs index b85188b7d..f0d351e9d 100644 --- a/crates/networking/rpc/engine/payload.rs +++ b/crates/networking/rpc/engine/payload.rs @@ -1,8 +1,8 @@ -use ethereum_rust_blockchain::add_block; -use ethereum_rust_blockchain::error::ChainError; -use ethereum_rust_blockchain::payload::build_payload; -use ethereum_rust_core::types::Fork; -use ethereum_rust_core::{H256, U256}; +use ethrex_blockchain::add_block; +use ethrex_blockchain::error::ChainError; +use ethrex_blockchain::payload::build_payload; +use ethrex_core::types::Fork; +use ethrex_core::{H256, U256}; use serde_json::Value; use tracing::{error, info, warn}; diff --git a/crates/networking/rpc/eth/account.rs b/crates/networking/rpc/eth/account.rs index 76e296570..94f880da3 100644 --- a/crates/networking/rpc/eth/account.rs +++ b/crates/networking/rpc/eth/account.rs @@ -1,4 +1,4 @@ -use ethereum_rust_blockchain::mempool; +use ethrex_blockchain::mempool; use serde_json::Value; use tracing::info; @@ -6,7 +6,7 @@ use crate::types::account_proof::{AccountProof, StorageProof}; use crate::types::block_identifier::{BlockIdentifierOrHash, BlockTag}; use crate::RpcApiContext; use crate::{utils::RpcErr, RpcHandler}; -use ethereum_rust_core::{Address, BigEndianHash, H256, U256}; +use ethrex_core::{Address, BigEndianHash, H256, U256}; pub struct GetBalanceRequest { pub address: Address, diff --git a/crates/networking/rpc/eth/block.rs b/crates/networking/rpc/eth/block.rs index 74295d090..fe014cc79 100644 --- a/crates/networking/rpc/eth/block.rs +++ b/crates/networking/rpc/eth/block.rs @@ -1,5 +1,5 @@ -use ethereum_rust_blockchain::find_parent_header; -use ethereum_rust_rlp::encode::RLPEncode; +use ethrex_blockchain::find_parent_header; +use ethrex_rlp::encode::RLPEncode; use serde_json::Value; use tracing::info; @@ -12,14 +12,14 @@ use crate::{ utils::RpcErr, RpcApiContext, RpcHandler, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{ calculate_base_fee_per_blob_gas, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, Receipt, }, U256, }; -use ethereum_rust_storage::Store; +use ethrex_storage::Store; pub struct GetBlockByNumberRequest { pub block: BlockIdentifier, diff --git a/crates/networking/rpc/eth/fee_market.rs b/crates/networking/rpc/eth/fee_market.rs index e1b8cf663..ea01ce612 100644 --- a/crates/networking/rpc/eth/fee_market.rs +++ b/crates/networking/rpc/eth/fee_market.rs @@ -1,12 +1,12 @@ -use ethereum_rust_blockchain::constants::MAX_BLOB_GAS_PER_BLOCK; -use ethereum_rust_core::types::{Block, Transaction}; +use ethrex_blockchain::constants::MAX_BLOB_GAS_PER_BLOCK; +use ethrex_core::types::{Block, Transaction}; use serde::Serialize; use serde_json::Value; use tracing::info; use crate::{types::block_identifier::BlockIdentifier, utils::RpcErr, RpcApiContext, RpcHandler}; -use ethereum_rust_core::types::calculate_base_fee_per_blob_gas; -use ethereum_rust_storage::Store; +use ethrex_core::types::calculate_base_fee_per_blob_gas; +use ethrex_storage::Store; #[derive(Clone, Debug)] pub struct FeeHistoryRequest { diff --git a/crates/networking/rpc/eth/filter.rs b/crates/networking/rpc/eth/filter.rs index c0e6018fd..0a7027dfd 100644 --- a/crates/networking/rpc/eth/filter.rs +++ b/crates/networking/rpc/eth/filter.rs @@ -2,8 +2,8 @@ // - Manually testing the behaviour deploying contracts on the Sepolia test network. // - Go-Ethereum, specifically: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/filters/filter.go // - Ethereum's reference: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newfilter -use ethereum_rust_core::types::BlockNumber; -use ethereum_rust_storage::Store; +use ethrex_core::types::BlockNumber; +use ethrex_storage::Store; use std::{ collections::HashMap, sync::{Arc, Mutex}, @@ -67,7 +67,7 @@ impl NewFilterRequest { pub fn handle( &self, - storage: ethereum_rust_storage::Store, + storage: ethrex_storage::Store, filters: ActiveFilters, ) -> Result { let from = self @@ -141,7 +141,7 @@ impl DeleteFilterRequest { pub fn handle( &self, - _storage: ethereum_rust_storage::Store, + _storage: ethrex_storage::Store, filters: ActiveFilters, ) -> Result { let mut active_filters_guard = filters.lock().unwrap_or_else(|mut poisoned_guard| { @@ -158,7 +158,7 @@ impl DeleteFilterRequest { pub fn stateful_call( req: &RpcRequest, - storage: ethereum_rust_storage::Store, + storage: ethrex_storage::Store, filters: ActiveFilters, ) -> Result { let request = Self::parse(&req.params)?; @@ -185,7 +185,7 @@ impl FilterChangesRequest { } pub fn handle( &self, - storage: ethereum_rust_storage::Store, + storage: ethrex_storage::Store, filters: ActiveFilters, ) -> Result { let Some(latest_block_num) = storage.get_latest_block_number()? else { @@ -242,7 +242,7 @@ impl FilterChangesRequest { } pub fn stateful_call( req: &RpcRequest, - storage: ethereum_rust_storage::Store, + storage: ethrex_storage::Store, filters: ActiveFilters, ) -> Result { let request = Self::parse(&req.params)?; @@ -272,8 +272,8 @@ mod tests { types::block_identifier::BlockIdentifier, utils::{test_utils::example_p2p_node, RpcRequest}, }; - use ethereum_rust_core::types::Genesis; - use ethereum_rust_storage::{EngineType, Store}; + use ethrex_core::types::Genesis; + use ethrex_storage::{EngineType, Store}; use serde_json::{json, Value}; use test_utils::TEST_GENESIS; diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index c35c5f241..cba06811f 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,4 +1,4 @@ -use ethereum_rust_blockchain::constants::MIN_GAS_LIMIT; +use ethrex_blockchain::constants::MIN_GAS_LIMIT; use tracing::error; use crate::utils::RpcErr; @@ -107,15 +107,15 @@ mod tests { RpcApiContext, RpcHandler, }; use bytes::Bytes; - use ethereum_rust_core::{ + use ethrex_core::{ types::{ Block, BlockBody, BlockHeader, EIP1559Transaction, Genesis, LegacyTransaction, Transaction, TxKind, }, Address, Bloom, H256, U256, }; - use ethereum_rust_net::types::Node; - use ethereum_rust_storage::{EngineType, Store}; + use ethrex_net::types::Node; + use ethrex_storage::{EngineType, Store}; use hex_literal::hex; use serde_json::json; use std::{net::Ipv4Addr, str::FromStr}; diff --git a/crates/networking/rpc/eth/logs.rs b/crates/networking/rpc/eth/logs.rs index b45034e70..72eb0aed6 100644 --- a/crates/networking/rpc/eth/logs.rs +++ b/crates/networking/rpc/eth/logs.rs @@ -6,8 +6,8 @@ use crate::{ types::{block_identifier::BlockIdentifier, receipt::RpcLog}, RpcApiContext, RpcErr, RpcHandler, }; -use ethereum_rust_core::{H160, H256}; -use ethereum_rust_storage::Store; +use ethrex_core::{H160, H256}; +use ethrex_storage::Store; use serde::Deserialize; use serde_json::Value; use std::collections::HashSet; diff --git a/crates/networking/rpc/eth/transaction.rs b/crates/networking/rpc/eth/transaction.rs index 2727e0e8b..efa7ec8e7 100644 --- a/crates/networking/rpc/eth/transaction.rs +++ b/crates/networking/rpc/eth/transaction.rs @@ -7,16 +7,16 @@ use crate::{ utils::RpcErr, RpcApiContext, RpcHandler, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{AccessListEntry, BlockHash, BlockHeader, BlockNumber, GenericTransaction, TxKind}, H256, U256, }; -use ethereum_rust_blockchain::mempool; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_storage::Store; +use ethrex_blockchain::mempool; +use ethrex_rlp::encode::RLPEncode; +use ethrex_storage::Store; -use ethereum_rust_vm::{evm_state, ExecutionResult, SpecId}; +use ethrex_vm::{evm_state, ExecutionResult, SpecId}; use serde::Serialize; use serde_json::Value; @@ -68,7 +68,7 @@ pub struct AccessListResult { access_list: Vec, #[serde(skip_serializing_if = "Option::is_none")] error: Option, - #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] gas_used: u64, } @@ -233,7 +233,7 @@ impl RpcHandler for GetTransactionByHashRequest { _ => return Ok(Value::Null), }; - let transaction: ethereum_rust_core::types::Transaction = + let transaction: ethrex_core::types::Transaction = match storage.get_transaction_by_location(block_hash, index)? { Some(transaction) => transaction, _ => return Ok(Value::Null), @@ -319,7 +319,7 @@ impl RpcHandler for CreateAccessListRequest { _ => return Ok(Value::Null), }; // Run transaction and obtain access list - let (gas_used, access_list, error) = match ethereum_rust_vm::create_access_list( + let (gas_used, access_list, error) = match ethrex_vm::create_access_list( &self.transaction, &header, &mut evm_state(context.storage, header.compute_block_hash()), @@ -449,8 +449,7 @@ impl RpcHandler for EstimateGasRequest { } }; - let spec_id = - ethereum_rust_vm::spec_id(&storage.get_chain_config()?, block_header.timestamp); + let spec_id = ethrex_vm::spec_id(&storage.get_chain_config()?, block_header.timestamp); // If the transaction is a plain value transfer, short circuit estimation. if let TxKind::Call(address) = transaction.to { @@ -548,7 +547,7 @@ fn simulate_tx( storage: Store, spec_id: SpecId, ) -> Result { - match ethereum_rust_vm::simulate_tx_from_generic( + match ethrex_vm::simulate_tx_from_generic( transaction, block_header, &mut evm_state(storage, block_header.compute_block_hash()), diff --git a/crates/networking/rpc/rpc.rs b/crates/networking/rpc/rpc.rs index 645386c29..d8382aa38 100644 --- a/crates/networking/rpc/rpc.rs +++ b/crates/networking/rpc/rpc.rs @@ -56,8 +56,8 @@ pub mod utils; mod web3; use axum::extract::State; -use ethereum_rust_net::types::Node; -use ethereum_rust_storage::Store; +use ethrex_net::types::Node; +use ethrex_storage::Store; #[derive(Debug, Clone)] pub struct RpcApiContext { @@ -301,8 +301,8 @@ where mod tests { use super::*; use crate::utils::test_utils::example_p2p_node; - use ethereum_rust_core::types::{ChainConfig, Genesis}; - use ethereum_rust_storage::EngineType; + use ethrex_core::types::{ChainConfig, Genesis}; + use ethrex_storage::EngineType; use std::fs::File; use std::io::BufReader; @@ -329,7 +329,7 @@ mod tests { let result = map_http_requests(&request, context); let rpc_response = rpc_response(request.id, result); let expected_response = to_rpc_response_success_value( - r#"{"jsonrpc":"2.0","id":1,"result":{"enode":"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@127.0.0.1:30303","id":"d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666","ip":"127.0.0.1","name":"ethereum_rust/0.1.0/rust1.80","ports":{"discovery":30303,"listener":30303},"protocols":{"eth":{"chainId":3151908,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":false,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":null,"berlinBlock":0,"londonBlock":0,"arrowGlacierBlock":null,"grayGlacierBlock":null,"mergeNetsplitBlock":0,"shanghaiTime":0,"cancunTime":0,"pragueTime":1718232101,"verkleTime":null,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true}}}}"#, + r#"{"jsonrpc":"2.0","id":1,"result":{"enode":"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@127.0.0.1:30303","id":"d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666","ip":"127.0.0.1","name":"ethrex/0.1.0/rust1.80","ports":{"discovery":30303,"listener":30303},"protocols":{"eth":{"chainId":3151908,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":false,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":null,"berlinBlock":0,"londonBlock":0,"arrowGlacierBlock":null,"grayGlacierBlock":null,"mergeNetsplitBlock":0,"shanghaiTime":0,"cancunTime":0,"pragueTime":1718232101,"verkleTime":null,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true}}}}"#, ); assert_eq!(rpc_response.to_string(), expected_response.to_string()) } diff --git a/crates/networking/rpc/types/account_proof.rs b/crates/networking/rpc/types/account_proof.rs index 332026b30..ed0ca8cdf 100644 --- a/crates/networking/rpc/types/account_proof.rs +++ b/crates/networking/rpc/types/account_proof.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::{serde_utils, Address, H256, U256}; +use ethrex_core::{serde_utils, Address, H256, U256}; use serde::{ser::SerializeSeq, Serialize, Serializer}; #[derive(Debug, Serialize)] diff --git a/crates/networking/rpc/types/block.rs b/crates/networking/rpc/types/block.rs index 54a307610..25ec4a78e 100644 --- a/crates/networking/rpc/types/block.rs +++ b/crates/networking/rpc/types/block.rs @@ -1,10 +1,10 @@ use super::transaction::RpcTransaction; -use ethereum_rust_core::{ +use ethrex_core::{ serde_utils, types::{Block, BlockBody, BlockHash, BlockHeader, BlockNumber, Withdrawal}, H256, U256, }; -use ethereum_rust_rlp::encode::RLPEncode; +use ethrex_rlp::encode::RLPEncode; use serde::{Deserialize, Serialize}; @@ -101,7 +101,7 @@ impl FullBlockBody { mod test { use bytes::Bytes; - use ethereum_rust_core::{ + use ethrex_core::{ types::{EIP1559Transaction, Transaction, TxKind}, Address, Bloom, H256, U256, }; diff --git a/crates/networking/rpc/types/block_identifier.rs b/crates/networking/rpc/types/block_identifier.rs index 5e59db067..b74963820 100644 --- a/crates/networking/rpc/types/block_identifier.rs +++ b/crates/networking/rpc/types/block_identifier.rs @@ -1,7 +1,7 @@ use std::{fmt::Display, str::FromStr}; -use ethereum_rust_core::types::{BlockHash, BlockHeader, BlockNumber}; -use ethereum_rust_storage::{error::StoreError, Store}; +use ethrex_core::types::{BlockHash, BlockHeader, BlockNumber}; +use ethrex_storage::{error::StoreError, Store}; use serde::Deserialize; use serde_json::Value; diff --git a/crates/networking/rpc/types/fork_choice.rs b/crates/networking/rpc/types/fork_choice.rs index 86189038a..4cfcf2bd3 100644 --- a/crates/networking/rpc/types/fork_choice.rs +++ b/crates/networking/rpc/types/fork_choice.rs @@ -1,5 +1,5 @@ use super::payload::PayloadStatus; -use ethereum_rust_core::{serde_utils, types::Withdrawal, Address, H256}; +use ethrex_core::{serde_utils, types::Withdrawal, Address, H256}; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] diff --git a/crates/networking/rpc/types/payload.rs b/crates/networking/rpc/types/payload.rs index b1abb57cb..b35825039 100644 --- a/crates/networking/rpc/types/payload.rs +++ b/crates/networking/rpc/types/payload.rs @@ -1,8 +1,8 @@ use bytes::Bytes; -use ethereum_rust_rlp::error::RLPDecodeError; +use ethrex_rlp::error::RLPDecodeError; use serde::{Deserialize, Serialize}; -use ethereum_rust_core::{ +use ethrex_core::{ serde_utils, types::{ compute_transactions_root, compute_withdrawals_root, BlobsBundle, Block, BlockBody, diff --git a/crates/networking/rpc/types/receipt.rs b/crates/networking/rpc/types/receipt.rs index 829394b0b..7c79bc145 100644 --- a/crates/networking/rpc/types/receipt.rs +++ b/crates/networking/rpc/types/receipt.rs @@ -1,10 +1,10 @@ -use ethereum_rust_blockchain::constants::GAS_PER_BLOB; -use ethereum_rust_core::{ +use ethrex_blockchain::constants::GAS_PER_BLOB; +use ethrex_core::{ serde_utils, types::{BlockHash, BlockHeader, BlockNumber, Log, Receipt, Transaction, TxKind, TxType}, Address, Bloom, Bytes, H256, }; -use ethereum_rust_vm::RevmAddress; +use ethrex_vm::RevmAddress; use serde::{Deserialize, Serialize}; @@ -138,18 +138,18 @@ impl RpcReceiptBlockInfo { #[serde(rename_all = "camelCase")] pub struct RpcReceiptTxInfo { pub transaction_hash: H256, - #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] pub transaction_index: u64, pub from: Address, pub to: Option
, pub contract_address: Option
, - #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] pub gas_used: u64, - #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] + #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] pub effective_gas_price: u64, #[serde( skip_serializing_if = "Option::is_none", - with = "ethereum_rust_core::serde_utils::u64::hex_str_opt", + with = "ethrex_core::serde_utils::u64::hex_str_opt", default = "Option::default" )] pub blob_gas_price: Option, @@ -207,7 +207,7 @@ impl RpcReceiptTxInfo { #[cfg(test)] mod tests { use super::*; - use ethereum_rust_core::{ + use ethrex_core::{ types::{Log, TxType}, Bloom, Bytes, }; diff --git a/crates/networking/rpc/types/transaction.rs b/crates/networking/rpc/types/transaction.rs index fce3b5ff1..df4b5a29c 100644 --- a/crates/networking/rpc/types/transaction.rs +++ b/crates/networking/rpc/types/transaction.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::{ +use ethrex_core::{ serde_utils, types::{ BlobsBundle, BlockHash, BlockNumber, EIP1559Transaction, EIP2930Transaction, @@ -6,7 +6,7 @@ use ethereum_rust_core::{ }, Address, H256, }; -use ethereum_rust_rlp::{ +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/networking/rpc/utils.rs b/crates/networking/rpc/utils.rs index 3fd23c2da..4ea572ed1 100644 --- a/crates/networking/rpc/utils.rs +++ b/crates/networking/rpc/utils.rs @@ -1,10 +1,10 @@ -use ethereum_rust_storage::error::StoreError; -use ethereum_rust_vm::EvmError; +use ethrex_storage::error::StoreError; +use ethrex_vm::EvmError; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::authentication::AuthenticationError; -use ethereum_rust_blockchain::error::MempoolError; +use ethrex_blockchain::error::MempoolError; #[derive(Debug, Deserialize)] pub enum RpcErr { @@ -246,9 +246,9 @@ pub fn parse_json_hex(hex: &serde_json::Value) -> Result { pub mod test_utils { use std::{net::SocketAddr, str::FromStr}; - use ethereum_rust_core::H512; - use ethereum_rust_net::types::Node; - use ethereum_rust_storage::{EngineType, Store}; + use ethrex_core::H512; + use ethrex_net::types::Node; + use ethrex_storage::{EngineType, Store}; use crate::start_api; diff --git a/crates/networking/rpc/web3/mod.rs b/crates/networking/rpc/web3/mod.rs index e5e628ec1..1bbad9690 100644 --- a/crates/networking/rpc/web3/mod.rs +++ b/crates/networking/rpc/web3/mod.rs @@ -1,8 +1,8 @@ -use ethereum_rust_storage::Store; +use ethrex_storage::Store; use serde_json::Value; use crate::utils::{RpcErr, RpcRequest}; pub fn client_version(_req: &RpcRequest, _store: Store) -> Result { - Ok(Value::String("ethereum_rust@0.1.0".to_owned())) + Ok(Value::String("ethrex@0.1.0".to_owned())) } diff --git a/crates/storage/store/Cargo.toml b/crates/storage/store/Cargo.toml index 92c8422e3..94cb2b178 100644 --- a/crates/storage/store/Cargo.toml +++ b/crates/storage/store/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethereum_rust-storage" +name = "ethrex-storage" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethereum_rust-rlp.workspace = true -ethereum_rust-core = { path = "../../common", default-features = false } -ethereum_rust-trie = { path = "../../storage/trie", default-features = false } +ethrex-rlp.workspace = true +ethrex-core = { path = "../../common", default-features = false } +ethrex-trie = { path = "../../storage/trie", default-features = false } ethereum-types = "0.14.1" anyhow = "1.0.86" @@ -25,8 +25,8 @@ libmdbx = { workspace = true, optional = true } default = ["libmdbx"] libmdbx = [ "dep:libmdbx", - "ethereum_rust-trie/libmdbx", - "ethereum_rust-core/libmdbx", + "ethrex-trie/libmdbx", + "ethrex-core/libmdbx", ] [dev-dependencies] diff --git a/crates/storage/store/engines/api.rs b/crates/storage/store/engines/api.rs index ce1c898b9..cca2e7333 100644 --- a/crates/storage/store/engines/api.rs +++ b/crates/storage/store/engines/api.rs @@ -1,12 +1,12 @@ use bytes::Bytes; -use ethereum_rust_core::types::{ +use ethereum_types::{H256, U256}; +use ethrex_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, }; -use ethereum_types::{H256, U256}; use std::{fmt::Debug, panic::RefUnwindSafe}; use crate::error::StoreError; -use ethereum_rust_trie::Trie; +use ethrex_trie::Trie; pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe { /// Add block header diff --git a/crates/storage/store/engines/in_memory.rs b/crates/storage/store/engines/in_memory.rs index afaff9cfa..4d4ed4dbb 100644 --- a/crates/storage/store/engines/in_memory.rs +++ b/crates/storage/store/engines/in_memory.rs @@ -1,10 +1,10 @@ use crate::error::StoreError; use bytes::Bytes; -use ethereum_rust_core::types::{ +use ethereum_types::{H256, U256}; +use ethrex_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, }; -use ethereum_rust_trie::{InMemoryTrieDB, Trie}; -use ethereum_types::{H256, U256}; +use ethrex_trie::{InMemoryTrieDB, Trie}; use std::{ collections::HashMap, fmt::Debug, diff --git a/crates/storage/store/engines/libmdbx.rs b/crates/storage/store/engines/libmdbx.rs index 5b4bfa817..52b28e803 100644 --- a/crates/storage/store/engines/libmdbx.rs +++ b/crates/storage/store/engines/libmdbx.rs @@ -6,13 +6,13 @@ use crate::rlp::{ }; use anyhow::Result; use bytes::Bytes; -use ethereum_rust_core::types::{ +use ethereum_types::{H256, U256}; +use ethrex_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, }; -use ethereum_rust_rlp::decode::RLPDecode; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_trie::{LibmdbxDupsortTrieDB, LibmdbxTrieDB, Trie}; -use ethereum_types::{H256, U256}; +use ethrex_rlp::decode::RLPDecode; +use ethrex_rlp::encode::RLPEncode; +use ethrex_trie::{LibmdbxDupsortTrieDB, LibmdbxTrieDB, Trie}; use libmdbx::orm::{Decodable, Encodable, Table}; use libmdbx::{ dupsort, diff --git a/crates/storage/store/error.rs b/crates/storage/store/error.rs index ce8b8f335..61687f543 100644 --- a/crates/storage/store/error.rs +++ b/crates/storage/store/error.rs @@ -1,5 +1,5 @@ -use ethereum_rust_rlp::error::RLPDecodeError; -use ethereum_rust_trie::TrieError; +use ethrex_rlp::error::RLPDecodeError; +use ethrex_trie::TrieError; use thiserror::Error; // TODO improve errors diff --git a/crates/storage/store/rlp.rs b/crates/storage/store/rlp.rs index 7d2aca7d3..50991db8e 100644 --- a/crates/storage/store/rlp.rs +++ b/crates/storage/store/rlp.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; use bytes::Bytes; -use ethereum_rust_core::{ +use ethereum_types::U256; +use ethrex_core::{ types::{Block, BlockBody, BlockHash, BlockHeader, Receipt}, H256, }; -use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; -use ethereum_types::U256; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; #[cfg(feature = "libmdbx")] use libmdbx::orm::{Decodable, Encodable}; diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index 6e440ae17..0f43e67a9 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -4,15 +4,15 @@ use self::engines::libmdbx::Store as LibmdbxStore; use self::error::StoreError; use bytes::Bytes; use engines::api::StoreEngine; -use ethereum_rust_core::types::{ +use ethereum_types::{Address, H256, U256}; +use ethrex_core::types::{ code_hash, AccountInfo, AccountState, BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Genesis, GenesisAccount, Index, MempoolTransaction, Receipt, Transaction, TxType, EMPTY_TRIE_HASH, }; -use ethereum_rust_rlp::decode::RLPDecode; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_trie::Trie; -use ethereum_types::{Address, H256, U256}; +use ethrex_rlp::decode::RLPDecode; +use ethrex_rlp::encode::RLPEncode; +use ethrex_trie::Trie; use sha3::{Digest as _, Keccak256}; use std::collections::HashMap; use std::fmt::Debug; @@ -915,12 +915,12 @@ mod tests { use std::{fs, panic, str::FromStr}; use bytes::Bytes; - use ethereum_rust_core::{ + use ethereum_types::{H256, U256}; + use ethrex_core::{ types::{Transaction, TxType, BYTES_PER_BLOB}, Bloom, }; - use ethereum_rust_rlp::decode::RLPDecode; - use ethereum_types::{H256, U256}; + use ethrex_rlp::decode::RLPDecode; use super::*; diff --git a/crates/storage/trie/Cargo.toml b/crates/storage/trie/Cargo.toml index 25ddbdccf..a9069e488 100644 --- a/crates/storage/trie/Cargo.toml +++ b/crates/storage/trie/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "ethereum_rust-trie" +name = "ethrex-trie" version.workspace = true edition.workspace = true [dependencies] -ethereum_rust-rlp.workspace = true +ethrex-rlp.workspace = true ethereum-types = "0.14.1" anyhow = "1.0.86" @@ -16,8 +16,6 @@ hex.workspace = true serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" libmdbx = { workspace = true, optional = true } - -# trie deps smallvec = { version = "1.10.0", features = ["const_generics", "union"] } digest = "0.10.6" lazy_static.workspace = true diff --git a/crates/storage/trie/error.rs b/crates/storage/trie/error.rs index f7cdf60e9..fe3bbfd37 100644 --- a/crates/storage/trie/error.rs +++ b/crates/storage/trie/error.rs @@ -1,4 +1,4 @@ -use ethereum_rust_rlp::error::RLPDecodeError; +use ethrex_rlp::error::RLPDecodeError; use thiserror::Error; #[derive(Debug, Error)] @@ -7,4 +7,6 @@ pub enum TrieError { LibmdbxError(anyhow::Error), #[error(transparent)] RLPDecode(#[from] RLPDecodeError), + #[error("Verification Error: {0}")] + Verify(String), } diff --git a/crates/storage/trie/nibbles.rs b/crates/storage/trie/nibbles.rs index 8c7f4b33b..6b7a91d3c 100644 --- a/crates/storage/trie/nibbles.rs +++ b/crates/storage/trie/nibbles.rs @@ -1,4 +1,6 @@ -use ethereum_rust_rlp::{ +use std::cmp; + +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -56,6 +58,14 @@ impl Nibbles { } } + pub fn compare_prefix(&self, prefix: &Nibbles) -> cmp::Ordering { + if self.len() > prefix.len() { + self.data[..prefix.len()].cmp(&prefix.data) + } else { + self.data.cmp(&prefix.data) + } + } + /// Compares self to another and returns the shared nibble count (amount of nibbles that are equal, from the start) pub fn count_prefix(&self, other: &Nibbles) -> usize { self.as_ref() diff --git a/crates/storage/trie/node.rs b/crates/storage/trie/node.rs index d24ccb49e..92c634a46 100644 --- a/crates/storage/trie/node.rs +++ b/crates/storage/trie/node.rs @@ -5,8 +5,8 @@ mod leaf; use std::array; pub use branch::BranchNode; -use ethereum_rust_rlp::{decode::decode_bytes, error::RLPDecodeError, structs::Decoder}; use ethereum_types::H256; +use ethrex_rlp::{decode::decode_bytes, error::RLPDecodeError, structs::Decoder}; pub use extension::ExtensionNode; pub use leaf::LeafNode; diff --git a/crates/storage/trie/node/branch.rs b/crates/storage/trie/node/branch.rs index 0b42455af..83b95b541 100644 --- a/crates/storage/trie/node/branch.rs +++ b/crates/storage/trie/node/branch.rs @@ -1,4 +1,4 @@ -use ethereum_rust_rlp::structs::Encoder; +use ethrex_rlp::structs::Encoder; use crate::{error::TrieError, nibbles::Nibbles, node_hash::NodeHash, state::TrieState, ValueRLP}; diff --git a/crates/storage/trie/node/extension.rs b/crates/storage/trie/node/extension.rs index ae1bd8c5c..a104716cf 100644 --- a/crates/storage/trie/node/extension.rs +++ b/crates/storage/trie/node/extension.rs @@ -1,4 +1,4 @@ -use ethereum_rust_rlp::structs::Encoder; +use ethrex_rlp::structs::Encoder; use crate::error::TrieError; use crate::nibbles::Nibbles; diff --git a/crates/storage/trie/node/leaf.rs b/crates/storage/trie/node/leaf.rs index 969bd4bea..f27cedfb5 100644 --- a/crates/storage/trie/node/leaf.rs +++ b/crates/storage/trie/node/leaf.rs @@ -1,4 +1,4 @@ -use ethereum_rust_rlp::structs::Encoder; +use ethrex_rlp::structs::Encoder; use crate::{ error::TrieError, nibbles::Nibbles, node::BranchNode, node_hash::NodeHash, state::TrieState, diff --git a/crates/storage/trie/node_hash.rs b/crates/storage/trie/node_hash.rs index 655e98647..e4a6397ed 100644 --- a/crates/storage/trie/node_hash.rs +++ b/crates/storage/trie/node_hash.rs @@ -1,5 +1,5 @@ -use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; use ethereum_types::H256; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; #[cfg(feature = "libmdbx")] use libmdbx::orm::{Decodable, Encodable}; use sha3::{Digest, Keccak256}; @@ -123,9 +123,7 @@ impl RLPEncode for NodeHash { } impl RLPDecode for NodeHash { - fn decode_unfinished( - rlp: &[u8], - ) -> Result<(Self, &[u8]), ethereum_rust_rlp::error::RLPDecodeError> { + fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), ethrex_rlp::error::RLPDecodeError> { let (hash, rest): (Vec, &[u8]); (hash, rest) = RLPDecode::decode_unfinished(rlp)?; let hash = NodeHash::from(hash); diff --git a/crates/storage/trie/rlp.rs b/crates/storage/trie/rlp.rs index 13eff2282..5e8a139c0 100644 --- a/crates/storage/trie/rlp.rs +++ b/crates/storage/trie/rlp.rs @@ -1,6 +1,6 @@ // Contains RLP encoding and decoding implementations for Trie Nodes // This encoding is only used to store the nodes in the DB, it is not the encoding used for hash computation -use ethereum_rust_rlp::{ +use ethrex_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/storage/trie/state.rs b/crates/storage/trie/state.rs index 213e3716d..be42925e9 100644 --- a/crates/storage/trie/state.rs +++ b/crates/storage/trie/state.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::error::TrieError; -use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; use super::db::TrieDB; diff --git a/crates/storage/trie/trie.rs b/crates/storage/trie/trie.rs index f4e4d707e..389bb5e7a 100644 --- a/crates/storage/trie/trie.rs +++ b/crates/storage/trie/trie.rs @@ -1,17 +1,17 @@ mod db; mod error; +mod nibbles; mod node; mod node_hash; mod rlp; mod state; -mod trie_iter; - -mod nibbles; #[cfg(test)] mod test_utils; -use ethereum_rust_rlp::constants::RLP_NULL; +mod trie_iter; +mod verify_range; use ethereum_types::H256; +use ethrex_rlp::constants::RLP_NULL; use nibbles::Nibbles; use node::Node; use node_hash::NodeHash; @@ -21,6 +21,7 @@ use sha3::{Digest, Keccak256}; pub use self::db::{libmdbx::LibmdbxTrieDB, libmdbx_dupsort::LibmdbxDupsortTrieDB}; pub use self::db::{in_memory::InMemoryTrieDB, TrieDB}; +pub use self::verify_range::verify_range; pub use self::error::TrieError; use self::{node::LeafNode, state::TrieState, trie_iter::TrieIterator}; @@ -168,19 +169,8 @@ impl Trie { // 2. the trie contains the (key, value) pair to verify // We will only be using the trie's cache so we don't need a working DB - // struct NullTrieDB; - - // impl TrieDB for NullTrieDB { - // fn get(&self, _key: Vec) -> Result>, TrieError> { - // Ok(None) - // } - // fn put(&self, _key: Vec, _value: Vec) -> Result<(), TrieError> { - // Ok(()) - // } - // } - - // let mut trie = Trie::new(Box::new(NullTrieDB)); + // let mut trie = Trie::stateless(); // Insert root into trie // let mut proof = proof.into_iter(); @@ -213,6 +203,20 @@ impl Trie { pub fn compute_hash_from_unsorted_iter( iter: impl Iterator, ) -> H256 { + let mut trie = Trie::stateless(); + for (path, value) in iter { + // Unwraping here won't panic as our in_memory trie DB won't fail + trie.insert(path, value).unwrap(); + } + trie.root + .as_ref() + .map(|root| root.clone().finalize()) + .unwrap_or(*EMPTY_TRIE_HASH) + } + + /// Creates a new stateless trie. This trie won't be able to store any nodes so all data will be lost after calculating the hash + /// Only use it for proof verification or computing a hash from an iterator + pub(crate) fn stateless() -> Trie { // We will only be using the trie's cache so we don't need a working DB struct NullTrieDB; @@ -226,15 +230,7 @@ impl Trie { } } - let mut trie = Trie::new(Box::new(NullTrieDB)); - for (path, value) in iter { - // Unwraping here won't panic as our in_memory trie DB won't fail - trie.insert(path, value).unwrap(); - } - trie.root - .as_ref() - .map(|root| root.clone().finalize()) - .unwrap_or(*EMPTY_TRIE_HASH) + Trie::new(Box::new(NullTrieDB)) } /// Obtain the encoded node given its path. diff --git a/crates/storage/trie/verify_range.rs b/crates/storage/trie/verify_range.rs new file mode 100644 index 000000000..836535433 --- /dev/null +++ b/crates/storage/trie/verify_range.rs @@ -0,0 +1,841 @@ +use std::{cmp::Ordering, collections::HashMap}; + +use ethereum_types::H256; +use sha3::{Digest, Keccak256}; + +use crate::{ + nibbles::Nibbles, node::Node, node_hash::NodeHash, state::TrieState, Trie, TrieError, ValueRLP, +}; + +/// Verifies that the key value range belongs to the trie with the given root given the edge proofs for the range +/// Also returns true if there is more state to be fetched (aka if there are more keys to the right of the given range) +pub fn verify_range( + root: H256, + first_key: &H256, + keys: &[H256], + values: &[ValueRLP], + proof: &[Vec], +) -> Result { + // Store proof nodes by hash + let proof_nodes = ProofNodeStorage::from_proof(proof); + // Validate range + if keys.len() != values.len() { + return Err(TrieError::Verify(format!( + "inconsistent proof data, got {} keys and {} values", + keys.len(), + values.len() + ))); + } + // Check that the key range is monotonically increasing + for keys in keys.windows(2) { + if keys[0] >= keys[1] { + return Err(TrieError::Verify(String::from( + "key range is not monotonically increasing", + ))); + } + } + // Check for empty values + if values.iter().any(|value| value.is_empty()) { + return Err(TrieError::Verify(String::from( + "value range contains empty value", + ))); + } + + // Verify ranges depending on the given proof + let mut trie = Trie::stateless(); + + // Special Case: No proofs given, the range is expected to be the full set of leaves + if proof.is_empty() { + // Check that the trie constructed from the given keys and values has the expected root + for (key, value) in keys.iter().zip(values.iter()) { + trie.insert(key.0.to_vec(), value.clone())?; + } + let hash = trie.hash()?; + if hash != root { + return Err(TrieError::Verify(format!( + "invalid proof, expected root hash {}, got {}", + root, hash + ))); + } + return Ok(false); + } + + // Special Case: One edge proof, no range given, there are no more values in the trie + if keys.is_empty() { + // We need to check that the proof confirms the non-existance of the first key + // and that there are no more elements to the right of the first key + let value = fill_state(&mut trie.state, root, first_key, &proof_nodes)?; + let has_right_element = has_right_element(root, first_key.as_bytes(), &trie.state)?; + if has_right_element || !value.is_empty() { + return Err(TrieError::Verify( + "no keys returned but more are available on the trie".to_string(), + )); + } else { + return Ok(false); + } + } + + let last_key = keys.last().unwrap(); + + // Special Case: There is only one element and the two edge keys are the same + if keys.len() == 1 && first_key == last_key { + // We need to check that the proof confirms the existance of the first key + let value = fill_state(&mut trie.state, root, first_key, &proof_nodes)?; + if first_key != &keys[0] { + return Err(TrieError::Verify( + "correct proof but invalid key".to_string(), + )); + } + if value != values[0] { + return Err(TrieError::Verify( + "correct proof but invalid data".to_string(), + )); + } + return has_right_element(root, first_key.as_bytes(), &trie.state); + } + + // Regular Case: Two edge proofs + if first_key >= last_key { + return Err(TrieError::Verify("invalid edge keys".to_string())); + } + // Fill up the state with the nodes from the proof + fill_state(&mut trie.state, root, first_key, &proof_nodes)?; + fill_state(&mut trie.state, root, last_key, &proof_nodes)?; + // Remove all references to the internal nodes that belong to the range so they can be reconstructed + let empty = remove_internal_references(root, first_key, last_key, &mut trie.state)?; + if !empty { + trie.root = Some(NodeHash::from(root)); + } + // Reconstruct the internal nodes by inserting the elements on the range + for (key, value) in keys.iter().zip(values.iter()) { + trie.insert(key.0.to_vec(), value.clone())?; + } + // Check for elements to the right of the range before we wipe the sate + let has_right_element = has_right_element(root, last_key.as_bytes(), &trie.state)?; + // Check that the hash is the one we expected (aka the trie was properly reconstructed from the edge proofs and the range) + let hash = trie.hash()?; + if hash != root { + return Err(TrieError::Verify(format!( + "invalid proof, expected root hash {}, got {}", + root, hash + ))); + } + Ok(has_right_element) +} + +/// Fills up the TrieState with nodes from the proof traversing the path given by first_key +/// Returns an error if there are gaps in the proof node path +/// Also returns the value if it is part of the proof +fn fill_state( + trie_state: &mut TrieState, + root_hash: H256, + first_key: &H256, + proof_nodes: &ProofNodeStorage, +) -> Result, TrieError> { + let mut path = Nibbles::from_bytes(&first_key.0); + fill_node( + &mut path, + &NodeHash::from(root_hash), + trie_state, + proof_nodes, + ) +} + +/// Fills up the TrieState with nodes from the proof traversing the path given by first_key +/// Returns an error if there are gaps in the proof node path +/// Also returns the value if it is part of the proof +fn fill_node( + path: &mut Nibbles, + node_hash: &NodeHash, + trie_state: &mut TrieState, + proof_nodes: &ProofNodeStorage, +) -> Result, TrieError> { + let node = proof_nodes.get_node(node_hash)?; + let child_hash = get_child(path, &node); + if let Some(ref child_hash) = child_hash { + trie_state.insert_node(node, node_hash.clone()); + fill_node(path, child_hash, trie_state, proof_nodes) + } else { + let value = match &node { + Node::Branch(n) => n.value.clone(), + Node::Extension(_) => vec![], + Node::Leaf(n) => (*path == n.partial) + .then_some(n.value.clone()) + .unwrap_or_default(), + }; + trie_state.insert_node(node, node_hash.clone()); + Ok(value) + } +} + +/// Returns the node hash of the node's child (if any) following the given path +fn get_child<'a>(path: &'a mut Nibbles, node: &'a Node) -> Option { + match node { + Node::Branch(n) => { + if let Some(choice) = path.next_choice() { + if n.choices[choice].is_valid() { + return Some(n.choices[choice].clone()); + } + } + None + } + Node::Extension(n) => path.skip_prefix(&n.prefix).then_some(n.child.clone()), + Node::Leaf(_) => None, + } +} + +/// Returns true if the trie contains elements to the right of the given key +/// (Aka if the given key is not the edge key of the trie) +fn has_right_element( + root_hash: H256, + key: &[u8], + trie_state: &TrieState, +) -> Result { + let path = Nibbles::from_bytes(key); + has_right_element_inner(root_hash.into(), path, trie_state) +} + +/// Returns true if the node's subtrie contains elements to the right of the given key +/// (Aka if the given key is not the edge key of the subtrie) +fn has_right_element_inner( + node_hash: NodeHash, + mut path: Nibbles, + trie_state: &TrieState, +) -> Result { + let Ok(Some(node)) = trie_state.get_node(node_hash.clone()) else { + return Ok(false); + }; + match node { + Node::Branch(ref n) => { + // Check if there are children to the right side + if let Some(choice) = path.next_choice() { + if n.choices[choice + 1..].iter().any(|child| child.is_valid()) { + return Ok(true); + } else if n.choices[choice].is_valid() { + return has_right_element_inner(n.choices[choice].clone(), path, trie_state); + } + } + } + Node::Extension(n) => { + if path.skip_prefix(&n.prefix) { + return has_right_element_inner(n.child, path, trie_state); + } else { + return Ok(n.prefix.as_ref() > path.as_ref()); + } + } + // We reached the end of the path + Node::Leaf(_) => {} + } + Ok(false) +} + +/// Removes references to internal nodes between the left and right key +/// These nodes should be entirely reconstructed when inserting the elements between left and right key (the proven range) +/// Returns true if the trie is left empty (rootless) as a result of this process +/// Asumes that left_key & right_key are not equal and of same length +fn remove_internal_references( + root_hash: H256, + left_key: &H256, + right_key: &H256, + trie_state: &mut TrieState, +) -> Result { + // First find the node at which the left and right path differ + let left_path = Nibbles::from_bytes(&left_key.0); + let right_path = Nibbles::from_bytes(&right_key.0); + + remove_internal_references_inner(NodeHash::from(root_hash), left_path, right_path, trie_state) +} + +/// Traverses the left and right path starting from the given node until the paths diverge +/// Once the paths diverge, removes the nodes between the left and right path +/// Returns true if the given node was completely removed as a result of this process +/// In which case the caller should remove the reference to this node from its parent node +/// Asumes that left_key & right_key are not equal and of same length +fn remove_internal_references_inner( + node_hash: NodeHash, + mut left_path: Nibbles, + mut right_path: Nibbles, + trie_state: &mut TrieState, +) -> Result { + if !node_hash.is_valid() { + return Ok(true); + } + // We already looked up the nodes when filling the state so this shouldn't fail + let node = trie_state.get_node(node_hash.clone())?.unwrap(); + match node { + Node::Branch(mut n) => { + // If none of the paths have a next choice nibble then it means that this is the end of the path + // which would mean that both paths are equal, which we already checked before + // If only one path doesn't have a next choice then it would mean that the paths have different lengths, + // which we also checked before calling this function + // Therefore we can safely unwrap here + let left_choice = left_path.next_choice().unwrap(); + let right_choice = right_path.next_choice().unwrap(); + + if left_choice == right_choice && n.choices[left_choice].is_valid() { + // Keep going + // Check if the child extension node should be removed as a result of this process + let should_remove = remove_internal_references_inner( + n.choices[left_choice].clone(), + left_path, + right_path, + trie_state, + )?; + if should_remove { + // Remove child node + n.choices[left_choice] = NodeHash::default(); + // Update node in the state + trie_state.insert_node(n.into(), node_hash); + } + } else { + // We found our fork node, now we can remove the internal references + // Remove all child nodes between the left and right child nodes + for choice in &mut n.choices[left_choice + 1..right_choice] { + *choice = NodeHash::default() + } + // Remove nodes on the left and right choice's subtries + let should_remove_left = + remove_node(n.choices[left_choice].clone(), left_path, false, trie_state); + let should_remove_right = remove_node( + n.choices[right_choice].clone(), + right_path, + true, + trie_state, + ); + // Remove left and right child nodes if their subtries where wiped in the process + if should_remove_left { + n.choices[left_choice] = NodeHash::default(); + } + if should_remove_right { + n.choices[right_choice] = NodeHash::default(); + } + // Update node in the state + trie_state.insert_node(n.into(), node_hash); + } + } + Node::Extension(n) => { + // Compare left and right paths against prefix + + let left_fork = left_path.compare_prefix(&n.prefix); + let right_fork = right_path.compare_prefix(&n.prefix); + + match (left_fork, right_fork) { + // If both paths contain the same prefix as the extension node, keep going + (Ordering::Equal, Ordering::Equal) => { + return remove_internal_references_inner( + n.child, + left_path.offset(n.prefix.len()), + right_path.offset(n.prefix.len()), + trie_state, + ); + } + // If both paths are greater or lesser than the node's prefix then the range is empty + (Ordering::Greater, Ordering::Greater) | (Ordering::Less, Ordering::Less) => { + return Err(TrieError::Verify("empty range".to_string())) + } + // None of the paths fit the prefix, remove the entire subtrie + (left, right) if left.is_ne() && right.is_ne() => { + // Return true so that the parent node removes this node + return Ok(true); + } + // One path fits the prefix, the other one doesn't + (left, right) => { + // Remove the nodes from the child's subtrie + let path = if left.is_eq() { left_path } else { right_path }; + // Propagate the response so that this node will be removed too if the child's subtrie is wiped + return Ok(remove_node( + node_hash, + path.offset(n.prefix.len()), + right.is_eq(), + trie_state, + )); + } + } + } + // This case should be unreachable as we checked that left_path != right_path + // before calling this function + Node::Leaf(_) => {} + } + Ok(false) +} + +// Removes all nodes in the node's subtrie to the left or right of the path (given by the `remove_left` flag) +// If the whole subtrie is removed in the process this function will return true, in which case +// the caller must remove the reference to this node from it's parent node +fn remove_node( + node_hash: NodeHash, + mut path: Nibbles, + remove_left: bool, + trie_state: &mut TrieState, +) -> bool { + // Node doesn't exist already, no need to remove it + if !node_hash.is_valid() { + return false; + } + // We already looked up the nodes when filling the state so this shouldn't fail + let node = trie_state.get_node(node_hash.clone()).unwrap().unwrap(); + match node { + Node::Branch(mut n) => { + // Remove child nodes + let Some(choice) = path.next_choice() else { + // Path ends in the branch node + return true; + }; + if remove_left { + for child in &mut n.choices[..choice] { + *child = NodeHash::default() + } + } else { + for child in &mut n.choices[choice + 1..] { + *child = NodeHash::default() + } + } + // Remove nodes to the left/right of the choice's subtrie + let should_remove = + remove_node(n.choices[choice].clone(), path, remove_left, trie_state); + if should_remove { + n.choices[choice] = NodeHash::default(); + } + // Update node in the state + trie_state.insert_node(n.into(), node_hash); + } + Node::Extension(n) => { + // If no child subtrie would result from this process remove the node entirely + // (Such as removing the left side of a trie with no right side) + if !path.skip_prefix(&n.prefix) { + if (remove_left && path.compare_prefix(&n.prefix).is_gt()) + || !remove_left && path.compare_prefix(&n.prefix).is_lt() + { + return true; + } + } else { + // Remove left/right side of the child subtrie + return remove_node(n.child, path, remove_left, trie_state); + } + } + Node::Leaf(_) => return true, + } + false +} + +/// An intermediate storage for proof nodes, containing encoded nodes indexed by hash +struct ProofNodeStorage<'a> { + nodes: HashMap, &'a Vec>, +} + +impl<'a> ProofNodeStorage<'a> { + // Construct a ProofNodeStorage for a proof + fn from_proof(proof: &'a [Vec]) -> Self { + Self { + nodes: proof + .iter() + .map(|node| (Keccak256::new_with_prefix(node).finalize().to_vec(), node)) + .collect::>(), + } + } + // Fetch a node by its hash, return an error if the node is not present or badly encoded + fn get_node(&self, hash: &NodeHash) -> Result { + let encoded = match hash { + NodeHash::Hashed(hash) => { + let Some(encoded) = self.nodes.get(hash.as_bytes()) else { + return Err(TrieError::Verify(format!("proof node missing: {hash}"))); + }; + *encoded + } + + NodeHash::Inline(ref encoded) => encoded, + }; + Ok(Node::decode_raw(encoded)?) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::collection::{btree_set, vec}; + use proptest::prelude::any; + use proptest::{bool, proptest}; + use std::str::FromStr; + + #[test] + fn verify_range_regular_case_only_branch_nodes() { + // The trie will have keys and values ranging from 25-100 + // We will prove the range from 50-75 + // Note values are written as hashes in the form i -> [i;32] + let mut trie = Trie::new_temp(); + for k in 25..100_u8 { + trie.insert([k; 32].to_vec(), [k; 32].to_vec()).unwrap() + } + let mut proof = trie.get_proof(&[50; 32].to_vec()).unwrap(); + proof.extend(trie.get_proof(&[75; 32].to_vec()).unwrap()); + let root = trie.hash().unwrap(); + let keys = (50_u8..=75).map(|i| H256([i; 32])).collect::>(); + let values = (50_u8..=75).map(|i| [i; 32].to_vec()).collect::>(); + let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); + // Our trie contains more elements to the right + assert!(fetch_more) + } + + #[test] + fn verify_range_regular_case() { + // The account ranges were taken form a hive test state, but artificially modified + // so that the resulting trie has a wide variety of different nodes (and not only branches) + let account_addresses: [&str; 26] = [ + "0xaa56789abcde80cde11add7d3447cd4ca93a5f2205d9874261484ae180718bd6", + "0xaa56789abcdeda9ae19dd26a33bd10bbf825e28b3de84fc8fe1d15a21645067f", + "0xaa56789abc39a8284ef43790e3a511b2caa50803613c5096bc782e8de08fa4c5", + "0xaa5678931f4754834b0502de5b0342ceff21cde5bef386a83d2292f4445782c2", + "0xaa567896492bfe767f3d18be2aab96441c449cd945770ef7ef8555acc505b2e4", + "0xaa5f478d53bf78add6fa3708d9e061d59bfe14b21329b2a4cf1156d4f81b3d2d", + "0xaa67c643f67b47cac9efacf6fcf0e4f4e1b273a727ded155db60eb9907939eb6", + "0xaa04d8eaccf0b942c468074250cbcb625ec5c4688b6b5d17d2a9bdd8dd565d5a", + "0xaa63e52cda557221b0b66bd7285b043071df4c2ab146260f4e010970f3a0cccf", + "0xaad9aa4f67f8b24d70a0ffd757e82456d9184113106b7d9e8eb6c3e8a8df27ee", + "0xaa3df2c3b574026812b154a99b13b626220af85cd01bb1693b1d42591054bce6", + "0xaa79e46a5ed8a88504ac7d579b12eb346fbe4fd7e281bdd226b891f8abed4789", + "0xbbf68e241fff876598e8e01cd529bd76416b248caf11e0552047c5f1d516aab6", + "0xbbf68e241fff876598e8e01cd529c908cdf0d646049b5b83629a70b0117e2957", + "0xbbf68e241fff876598e8e0180b89744abb96f7af1171ed5f47026bdf01df1874", + "0xbbf68e241fff876598e8a4cd8e43f08be4715d903a0b1d96b3d9c4e811cbfb33", + "0xbbf68e241fff8765182a510994e2b54d14b731fac96b9c9ef434bc1924315371", + "0xbbf68e241fff87655379a3b66c2d8983ba0b2ca87abaf0ca44836b2a06a2b102", + "0xbbf68e241fffcbcec8301709a7449e2e7371910778df64c89f48507390f2d129", + "0xbbf68e241ffff228ed3aa7a29644b1915fde9ec22e0433808bf5467d914e7c7a", + "0xbbf68e24190b881949ec9991e48dec768ccd1980896aefd0d51fd56fd5689790", + "0xbbf68e2419de0a0cb0ff268c677aba17d39a3190fe15aec0ff7f54184955cba4", + "0xbbf68e24cc6cbd96c1400150417dd9b30d958c58f63c36230a90a02b076f78b5", + "0xbbf68e2490f33f1d1ba6d1521a00935630d2c81ab12fa03d4a0f4915033134f3", + "0xc017b10a7cc3732d729fe1f71ced25e5b7bc73dc62ca61309a8c7e5ac0af2f72", + "0xc098f06082dc467088ecedb143f9464ebb02f19dc10bd7491b03ba68d751ce45", + ]; + let mut account_addresses = account_addresses + .iter() + .map(|addr| H256::from_str(addr).unwrap()) + .collect::>(); + account_addresses.sort(); + let trie_values = account_addresses + .iter() + .map(|addr| addr.0.to_vec()) + .collect::>(); + let keys = account_addresses[7..=17].to_vec(); + let values = account_addresses[7..=17] + .iter() + .map(|v| v.0.to_vec()) + .collect::>(); + let mut trie = Trie::new_temp(); + for val in trie_values.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let mut proof = trie.get_proof(&trie_values[7]).unwrap(); + proof.extend(trie.get_proof(&trie_values[17]).unwrap()); + let root = trie.hash().unwrap(); + let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); + // Our trie contains more elements to the right + assert!(fetch_more) + } + + // Proptests for verify_range + proptest! { + + // Successful Cases + + #[test] + // Regular Case: Two Edge Proofs, both keys exist + fn proptest_verify_range_regular_case(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>()[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Generate proofs + let mut proof = trie.get_proof(&values[0]).unwrap(); + proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); + // Verify the range proof + let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); + if end == 199 { + // The last key is at the edge of the trie + assert!(!fetch_more) + } else { + // Our trie contains more elements to the right + assert!(fetch_more) + } + } + + #[test] + // Two Edge Proofs, first and last keys dont exist + fn proptest_verify_range_nonexistant_edge_keys(data in btree_set(vec(1..u8::MAX-1, 32), 200), start in 1_usize..=100_usize, end in 101..199_usize) { + let data = data.into_iter().collect::>(); + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Select the first and last keys + // As we will be using non-existant keys we will choose values that are `just` higer/lower than + // the first and last values in our key range + // Skip the test entirely in the unlucky case that the values just next to the edge keys are also part of the trie + let mut first_key = data[start].clone(); + first_key[31] -=1; + if first_key == data[start -1] { + // Skip test + return Ok(()); + } + let mut last_key = data[end].clone(); + last_key[31] +=1; + if last_key == data[end +1] { + // Skip test + return Ok(()); + } + // Generate proofs + let mut proof = trie.get_proof(&first_key).unwrap(); + proof.extend(trie.get_proof(&last_key).unwrap()); + // Verify the range proof + let fetch_more = verify_range(root, &H256::from_slice(&first_key), &keys, &values, &proof).unwrap(); + // Our trie contains more elements to the right + assert!(fetch_more) + } + + #[test] + // Two Edge Proofs, one key doesn't exist + fn proptest_verify_range_one_key_doesnt_exist(data in btree_set(vec(1..u8::MAX-1, 32), 200), start in 1_usize..=100_usize, end in 101..199_usize, first_key_exists in bool::ANY) { + let data = data.into_iter().collect::>(); + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Select the first and last keys + // As we will be using non-existant keys we will choose values that are `just` higer/lower than + // the first and last values in our key range + // Skip the test entirely in the unlucky case that the values just next to the edge keys are also part of the trie + let mut first_key = data[start].clone(); + let mut last_key = data[end].clone(); + if first_key_exists { + last_key[31] +=1; + if last_key == data[end +1] { + // Skip test + return Ok(()); + } + } else { + first_key[31] -=1; + if first_key == data[start -1] { + // Skip test + return Ok(()); + } + } + // Generate proofs + let mut proof = trie.get_proof(&first_key).unwrap(); + proof.extend(trie.get_proof(&last_key).unwrap()); + // Verify the range proof + let fetch_more = verify_range(root, &H256::from_slice(&first_key), &keys, &values, &proof).unwrap(); + // Our trie contains more elements to the right + assert!(fetch_more) + } + + #[test] + // Special Case: Range contains all the leafs in the trie, no proofs + fn proptest_verify_range_full_leafset(data in btree_set(vec(any::(), 32), 100..200)) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // The keyset contains the entire trie so we don't need edge proofs + let proof = vec![]; + // Verify the range proof + let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); + // Our range is the full leafset, there shouldn't be more values left in the trie + assert!(!fetch_more) + } + + #[test] + // Special Case: No values, one edge proof (of non-existance) + fn proptest_verify_range_no_values(mut data in btree_set(vec(any::(), 32), 100..200)) { + // Remove the last element so we can use it as key for the proof of non-existance + let last_element = data.pop_last().unwrap(); + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Range is empty + let values = vec![]; + let keys = vec![]; + let first_key = H256::from_slice(&last_element); + // Generate proof (last element) + let proof = trie.get_proof(&last_element).unwrap(); + // Verify the range proof + let fetch_more = verify_range(root, &first_key, &keys, &values, &proof).unwrap(); + // There are no more elements to the right of the range + assert!(!fetch_more) + } + + #[test] + // Special Case: One element range + fn proptest_verify_range_one_element(data in btree_set(vec(any::(), 32), 200), start in 0_usize..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = vec![data.iter().collect::>()[start].clone()]; + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Generate proofs + let proof = trie.get_proof(&values[0]).unwrap(); + // Verify the range proof + let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); + if start == 199 { + // The last key is at the edge of the trie + assert!(!fetch_more) + } else { + // Our trie contains more elements to the right + assert!(fetch_more) + } + } + + // Unsuccesful Cases + + #[test] + // Regular Case: Only one edge proof, both keys exist + fn proptest_verify_range_regular_case_only_one_edge_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>()[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Generate proofs (only prove first key) + let proof = trie.get_proof(&values[0]).unwrap(); + // Verify the range proof + assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); + } + + #[test] + // Regular Case: Two Edge Proofs, both keys exist, but there is a missing node in the proof + fn proptest_verify_range_regular_case_gap_in_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>()[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Generate proofs + let mut proof = trie.get_proof(&values[0]).unwrap(); + proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); + // Remove the last node of the second proof (to make sure we don't remove a node that is also part of the first proof) + proof.pop(); + // Verify the range proof + assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); + } + + #[test] + // Regular Case: Two Edge Proofs, both keys exist, but there is a missing node in the proof + fn proptest_verify_range_regular_case_gap_in_middle_of_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>()[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Generate proofs + let mut proof = trie.get_proof(&values[0]).unwrap(); + let mut second_proof = trie.get_proof(&values[0]).unwrap(); + proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); + // Remove the middle node of the second proof + let gap_idx = second_proof.len() / 2; + let removed = second_proof.remove(gap_idx); + // Remove the node from the first proof if it is also there + proof.retain(|n| n != &removed); + proof.extend(second_proof); + // Verify the range proof + assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); + } + + #[test] + // Regular Case: No proofs both keys exist + fn proptest_verify_range_regular_case_no_proofs(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = data.into_iter().collect::>()[start..=end].to_vec(); + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Dont generate proof + let proof = vec![]; + // Verify the range proof + assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); + } + + #[test] + // Special Case: No values, one edge proof (of existance) + fn proptest_verify_range_no_values_proof_of_existance(data in btree_set(vec(any::(), 32), 100..200)) { + // Fetch the last element so we can use it as key for the proof + let last_element = data.last().unwrap(); + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Range is empty + let values = vec![]; + let keys = vec![]; + let first_key = H256::from_slice(last_element); + // Generate proof (last element) + let proof = trie.get_proof(last_element).unwrap(); + // Verify the range proof + assert!(verify_range(root, &first_key, &keys, &values, &proof).is_err()); + } + + #[test] + // Special Case: One element range (but the proof is of nonexistance) + fn proptest_verify_range_one_element_bad_proof(data in btree_set(vec(any::(), 32), 200), start in 0_usize..200_usize) { + // Build trie + let mut trie = Trie::new_temp(); + for val in data.iter() { + trie.insert(val.clone(), val.clone()).unwrap() + } + let root = trie.hash().unwrap(); + // Select range to prove + let values = vec![data.iter().collect::>()[start].clone()]; + let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); + // Remove the value to generate a proof of non-existance + trie.remove(values[0].clone()).unwrap(); + // Generate proofs + let proof = trie.get_proof(&values[0]).unwrap(); + // Verify the range proof + assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); + } + } +} diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index 616712a43..a9892ef84 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethereum_rust-vm" +name = "ethrex-vm" version = "0.1.0" edition = "2021" [dependencies] -ethereum_rust-core = { path = "../common", default-features = false } -ethereum_rust-storage = { path = "../storage/store", default-features = false } -ethereum_rust-levm = { path = "./levm", optional = true } -ethereum_rust-trie = { path = "../storage/trie", default-features = false } -ethereum_rust-rlp = { path = "../common/rlp", default-features = false } +ethrex-core = { path = "../common", default-features = false } +ethrex-storage = { path = "../storage/store", default-features = false } +ethrex-levm = { path = "./levm", optional = true } +ethrex-trie = { path = "../storage/trie", default-features = false } +ethrex-rlp = { path = "../common/rlp", default-features = false } revm = { version = "14.0.3", features = [ "serde", "std", @@ -42,8 +42,8 @@ default = ["libmdbx", "c-kzg", "blst"] l2 = [] c-kzg = ["revm/c-kzg"] blst = ["revm/blst"] -libmdbx = ["ethereum_rust-storage/default", "ethereum_rust-core/libmdbx"] -levm = ["ethereum_rust-levm"] +libmdbx = ["ethrex-storage/default", "ethrex-core/libmdbx"] +levm = ["ethrex-levm"] [profile.test] opt-level = 3 diff --git a/crates/vm/db.rs b/crates/vm/db.rs index 216cfb51a..d015ffa62 100644 --- a/crates/vm/db.rs +++ b/crates/vm/db.rs @@ -1,5 +1,5 @@ -use ethereum_rust_core::{types::BlockHash, Address as CoreAddress, H256 as CoreH256}; -use ethereum_rust_storage::{error::StoreError, Store}; +use ethrex_core::{types::BlockHash, Address as CoreAddress, H256 as CoreH256}; +use ethrex_storage::{error::StoreError, Store}; use revm::primitives::{ AccountInfo as RevmAccountInfo, Address as RevmAddress, Bytecode as RevmBytecode, Bytes as RevmBytes, B256 as RevmB256, U256 as RevmU256, @@ -12,11 +12,11 @@ pub struct StoreWrapper { cfg_if::cfg_if! { if #[cfg(feature = "levm")] { - use ethereum_rust_core::{U256 as CoreU256}; - use ethereum_rust_levm::db::Database as LevmDatabase; + use ethrex_core::{U256 as CoreU256}; + use ethrex_levm::db::Database as LevmDatabase; impl LevmDatabase for StoreWrapper { - fn get_account_info(&self, address: CoreAddress) -> ethereum_rust_levm::account::AccountInfo { + fn get_account_info(&self, address: CoreAddress) -> ethrex_levm::account::AccountInfo { let acc_info = self .store .get_account_info_by_hash(self.block_hash, address) @@ -29,7 +29,7 @@ cfg_if::cfg_if! { .unwrap() .unwrap_or_default(); - ethereum_rust_levm::account::AccountInfo { + ethrex_levm::account::AccountInfo { balance: acc_info.balance, nonce: acc_info.nonce, bytecode: acc_code, diff --git a/crates/vm/errors.rs b/crates/vm/errors.rs index 82ba7c646..a2635b5f6 100644 --- a/crates/vm/errors.rs +++ b/crates/vm/errors.rs @@ -1,7 +1,7 @@ -use ethereum_rust_core::types::BlockHash; -use ethereum_rust_storage::error::StoreError; -use ethereum_rust_trie::TrieError; use ethereum_types::H160; +use ethrex_core::types::BlockHash; +use ethrex_storage::error::StoreError; +use ethrex_trie::TrieError; use revm::primitives::{ result::EVMError as RevmError, Address as RevmAddress, B256 as RevmB256, U256 as RevmU256, }; diff --git a/crates/vm/execution_db.rs b/crates/vm/execution_db.rs index 2b70b8ce4..edf65021c 100644 --- a/crates/vm/execution_db.rs +++ b/crates/vm/execution_db.rs @@ -1,13 +1,13 @@ use std::collections::HashMap; -use ethereum_rust_core::{ +use ethereum_types::{Address, H160, U256}; +use ethrex_core::{ types::{AccountState, Block, ChainConfig}, H256, }; -use ethereum_rust_rlp::encode::RLPEncode; -use ethereum_rust_storage::{hash_address, hash_key, Store}; -use ethereum_rust_trie::Trie; -use ethereum_types::{Address, H160, U256}; +use ethrex_rlp::encode::RLPEncode; +use ethrex_storage::{hash_address, hash_key, Store}; +use ethrex_trie::Trie; use revm::{ primitives::{ AccountInfo as RevmAccountInfo, Address as RevmAddress, Bytecode as RevmBytecode, @@ -72,12 +72,13 @@ impl ExecutionDB { for account_update in account_updates.iter() { let address = RevmAddress::from_slice(account_update.address.as_bytes()); - let account_state = store - .get_account_state_by_hash( - block.header.parent_hash, - H160::from_slice(address.as_slice()), - )? - .ok_or(ExecutionDBError::NewMissingAccountInfo(address))?; + let account_state = match store.get_account_state_by_hash( + block.header.parent_hash, + H160::from_slice(address.as_slice()), + )? { + Some(state) => state, + None => continue, + }; accounts.insert(address, account_state); let account_storage = account_update diff --git a/crates/vm/execution_result.rs b/crates/vm/execution_result.rs index 1914b7939..9f0d37086 100644 --- a/crates/vm/execution_result.rs +++ b/crates/vm/execution_result.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethereum_rust_core::Address; -use ethereum_rust_core::{types::Log, H256}; +use ethrex_core::Address; +use ethrex_core::{types::Log, H256}; use revm::primitives::result::Output as RevmOutput; use revm::primitives::result::SuccessReason as RevmSuccessReason; use revm::primitives::ExecutionResult as RevmExecutionResult; diff --git a/crates/vm/levm/Cargo.toml b/crates/vm/levm/Cargo.toml index 827c28636..3635db593 100644 --- a/crates/vm/levm/Cargo.toml +++ b/crates/vm/levm/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "ethereum_rust-levm" +name = "ethrex-levm" version.workspace = true edition.workspace = true [dependencies] -ethereum_rust-core.workspace = true -ethereum_rust-rlp.workspace = true +ethrex-core.workspace = true +ethrex-rlp.workspace = true bytes.workspace = true sha3 = "0.10.8" diff --git a/crates/vm/levm/Makefile b/crates/vm/levm/Makefile index 316dd74a1..b2947f224 100644 --- a/crates/vm/levm/Makefile +++ b/crates/vm/levm/Makefile @@ -6,7 +6,7 @@ help: ## 📚 Show help for each of the Makefile recipes @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' test: ## 🧪 Runs all tests except Ethereum tests - cargo test -p ethereum_rust-levm + cargo test -p ethrex-levm lint: ## 🧹 Linter check cargo clippy --all-targets --all-features -- -D warnings diff --git a/crates/vm/levm/bench/revm_comparison/Cargo.toml b/crates/vm/levm/bench/revm_comparison/Cargo.toml index a97b97b81..59e2c0431 100644 --- a/crates/vm/levm/bench/revm_comparison/Cargo.toml +++ b/crates/vm/levm/bench/revm_comparison/Cargo.toml @@ -8,7 +8,7 @@ name = "revm_comparison" path = "src/lib.rs" [dependencies] -ethereum_rust-levm = { path = "../../" } +ethrex-levm = { path = "../../" } hex = "0.4.3" revm = "9.0.0" bytes = "1.8.0" diff --git a/crates/vm/levm/bench/revm_comparison/src/lib.rs b/crates/vm/levm/bench/revm_comparison/src/lib.rs index fa88a2993..1a224d4bd 100644 --- a/crates/vm/levm/bench/revm_comparison/src/lib.rs +++ b/crates/vm/levm/bench/revm_comparison/src/lib.rs @@ -1,5 +1,5 @@ use bytes::Bytes; -use ethereum_rust_levm::{call_frame::CallFrame, errors::TxResult, utils::new_vm_with_bytecode}; +use ethrex_levm::{call_frame::CallFrame, errors::TxResult, utils::new_vm_with_bytecode}; use revm::{ db::BenchmarkDB, primitives::{address, Bytecode, TransactTo}, diff --git a/crates/vm/levm/src/account.rs b/crates/vm/levm/src/account.rs index 558d131bf..da8872a92 100644 --- a/crates/vm/levm/src/account.rs +++ b/crates/vm/levm/src/account.rs @@ -3,7 +3,7 @@ use crate::{ errors::{InternalError, VMError}, }; use bytes::Bytes; -use ethereum_rust_core::{H256, U256}; +use ethrex_core::{H256, U256}; use keccak_hash::keccak; use std::collections::HashMap; diff --git a/crates/vm/levm/src/call_frame.rs b/crates/vm/levm/src/call_frame.rs index 28535a27f..0602e26da 100644 --- a/crates/vm/levm/src/call_frame.rs +++ b/crates/vm/levm/src/call_frame.rs @@ -5,7 +5,7 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethereum_rust_core::{types::Log, Address, U256}; +use ethrex_core::{types::Log, Address, U256}; use std::collections::HashMap; /// [EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153#reference-implementation diff --git a/crates/vm/levm/src/constants.rs b/crates/vm/levm/src/constants.rs index 8984a6673..6b07e6e87 100644 --- a/crates/vm/levm/src/constants.rs +++ b/crates/vm/levm/src/constants.rs @@ -1,4 +1,4 @@ -use ethereum_rust_core::{H256, U256}; +use ethrex_core::{H256, U256}; pub const SUCCESS_FOR_CALL: i32 = 1; pub const REVERT_FOR_CALL: i32 = 0; @@ -27,7 +27,7 @@ pub const MAX_CREATE_CODE_SIZE: usize = 2 * MAX_CODE_SIZE; pub const INVALID_CONTRACT_PREFIX: u8 = 0xef; pub mod create_opcode { - use ethereum_rust_core::U256; + use ethrex_core::U256; pub const INIT_CODE_WORD_COST: U256 = U256([2, 0, 0, 0]); pub const CODE_DEPOSIT_COST: U256 = U256([200, 0, 0, 0]); diff --git a/crates/vm/levm/src/db.rs b/crates/vm/levm/src/db.rs index f1b124550..d9abe7681 100644 --- a/crates/vm/levm/src/db.rs +++ b/crates/vm/levm/src/db.rs @@ -2,7 +2,7 @@ use crate::{ account::{Account, AccountInfo, StorageSlot}, errors::{InternalError, VMError}, }; -use ethereum_rust_core::{Address, H256, U256}; +use ethrex_core::{Address, H256, U256}; use std::collections::HashMap; pub trait Database { diff --git a/crates/vm/levm/src/environment.rs b/crates/vm/levm/src/environment.rs index 0f5f91ab0..3e57703f2 100644 --- a/crates/vm/levm/src/environment.rs +++ b/crates/vm/levm/src/environment.rs @@ -1,5 +1,5 @@ use crate::constants::TX_BASE_COST; -use ethereum_rust_core::{Address, H256, U256}; +use ethrex_core::{Address, H256, U256}; #[derive(Debug, Default, Clone)] pub struct Environment { diff --git a/crates/vm/levm/src/errors.rs b/crates/vm/levm/src/errors.rs index e7c874218..b6bad188c 100644 --- a/crates/vm/levm/src/errors.rs +++ b/crates/vm/levm/src/errors.rs @@ -1,6 +1,6 @@ use crate::account::Account; use bytes::Bytes; -use ethereum_rust_core::{types::Log, Address}; +use ethrex_core::{types::Log, Address}; use std::collections::HashMap; use thiserror; diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 5b1e31a47..75cdc321e 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -6,7 +6,7 @@ use crate::{ }; use bytes::Bytes; /// Contains the gas costs of the EVM instructions (in wei) -use ethereum_rust_core::U256; +use ethrex_core::U256; // Opcodes cost pub const ADD: U256 = U256([3, 0, 0, 0]); diff --git a/crates/vm/levm/src/memory.rs b/crates/vm/levm/src/memory.rs index 6c7b9d1a0..2eab4ada7 100644 --- a/crates/vm/levm/src/memory.rs +++ b/crates/vm/levm/src/memory.rs @@ -2,7 +2,7 @@ use crate::{ constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE}, errors::{InternalError, OutOfGasError, VMError}, }; -use ethereum_rust_core::U256; +use ethrex_core::U256; #[derive(Debug, Clone, Default, PartialEq)] pub struct Memory { diff --git a/crates/vm/levm/src/opcode_handlers/arithmetic.rs b/crates/vm/levm/src/opcode_handlers/arithmetic.rs index 3ab87d470..975392dc1 100644 --- a/crates/vm/levm/src/opcode_handlers/arithmetic.rs +++ b/crates/vm/levm/src/opcode_handlers/arithmetic.rs @@ -5,7 +5,7 @@ use crate::{ opcode_handlers::bitwise_comparison::checked_shift_left, vm::VM, }; -use ethereum_rust_core::{U256, U512}; +use ethrex_core::{U256, U512}; // Arithmetic Operations (11) // Opcodes: ADD, SUB, MUL, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND diff --git a/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs b/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs index aef3fb81c..c80812430 100644 --- a/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs +++ b/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethereum_rust_core::U256; +use ethrex_core::U256; // Comparison and Bitwise Logic Operations (14) // Opcodes: LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR diff --git a/crates/vm/levm/src/opcode_handlers/block.rs b/crates/vm/levm/src/opcode_handlers/block.rs index 1b092900f..d252b4d4a 100644 --- a/crates/vm/levm/src/opcode_handlers/block.rs +++ b/crates/vm/levm/src/opcode_handlers/block.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{address_to_word, VM}, }; -use ethereum_rust_core::{ +use ethrex_core::{ types::{BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS}, H256, U256, }; diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index a3f2c8e11..f0d7e02c8 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{word_to_address, VM}, }; -use ethereum_rust_core::U256; +use ethrex_core::U256; use sha3::{Digest, Keccak256}; // Environmental Information (16) diff --git a/crates/vm/levm/src/opcode_handlers/keccak.rs b/crates/vm/levm/src/opcode_handlers/keccak.rs index 7b002e8bd..77a11c4df 100644 --- a/crates/vm/levm/src/opcode_handlers/keccak.rs +++ b/crates/vm/levm/src/opcode_handlers/keccak.rs @@ -4,7 +4,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethereum_rust_core::U256; +use ethrex_core::U256; use sha3::{Digest, Keccak256}; // KECCAK256 (1) diff --git a/crates/vm/levm/src/opcode_handlers/logging.rs b/crates/vm/levm/src/opcode_handlers/logging.rs index 88d29a255..8efa48268 100644 --- a/crates/vm/levm/src/opcode_handlers/logging.rs +++ b/crates/vm/levm/src/opcode_handlers/logging.rs @@ -6,7 +6,7 @@ use crate::{ vm::VM, }; use bytes::Bytes; -use ethereum_rust_core::{types::Log, H256}; +use ethrex_core::{types::Log, H256}; // Logging Operations (5) // Opcodes: LOG0 ... LOG4 diff --git a/crates/vm/levm/src/opcode_handlers/push.rs b/crates/vm/levm/src/opcode_handlers/push.rs index 39bed978b..2a83c4c65 100644 --- a/crates/vm/levm/src/opcode_handlers/push.rs +++ b/crates/vm/levm/src/opcode_handlers/push.rs @@ -5,7 +5,7 @@ use crate::{ opcodes::Opcode, vm::VM, }; -use ethereum_rust_core::U256; +use ethrex_core::U256; // Push Operations // Opcodes: PUSH0, PUSH1 ... PUSH32 diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index bb70985d6..55b38884d 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -6,7 +6,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethereum_rust_core::{H256, U256}; +use ethrex_core::{H256, U256}; // Stack, Memory, Storage and Flow Operations (15) // Opcodes: POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE, JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, TLOAD, TSTORE, MCOPY @@ -159,7 +159,7 @@ impl VM { } // SSTORE operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1087 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1087 pub fn op_sstore( &mut self, current_call_frame: &mut CallFrame, diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index ab42f8837..1012a2e95 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{word_to_address, VM}, }; -use ethereum_rust_core::{types::TxKind, U256}; +use ethrex_core::{types::TxKind, U256}; // System Operations (10) // Opcodes: CREATE, CALL, CALLCODE, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT @@ -87,7 +87,7 @@ impl VM { } // CALLCODE operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 pub fn op_callcode( &mut self, current_call_frame: &mut CallFrame, @@ -192,7 +192,7 @@ impl VM { } // DELEGATECALL operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 pub fn op_delegatecall( &mut self, current_call_frame: &mut CallFrame, @@ -260,7 +260,7 @@ impl VM { } // STATICCALL operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 pub fn op_staticcall( &mut self, current_call_frame: &mut CallFrame, @@ -328,7 +328,7 @@ impl VM { } // CREATE operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 pub fn op_create( &mut self, current_call_frame: &mut CallFrame, @@ -357,7 +357,7 @@ impl VM { } // CREATE2 operation - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 pub fn op_create2( &mut self, current_call_frame: &mut CallFrame, diff --git a/crates/vm/levm/src/operations.rs b/crates/vm/levm/src/operations.rs index 5f36d17f5..03e99f91a 100644 --- a/crates/vm/levm/src/operations.rs +++ b/crates/vm/levm/src/operations.rs @@ -3,7 +3,7 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethereum_rust_core::U256; +use ethrex_core::U256; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Operation { diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index dd8a037d4..56dc716fe 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -7,7 +7,7 @@ use crate::{ vm::VM, }; use bytes::Bytes; -use ethereum_rust_core::{types::TxKind, Address, U256}; +use ethrex_core::{types::TxKind, Address, U256}; use std::{collections::HashMap, sync::Arc}; pub fn ops_to_bytecode(operations: &[Operation]) -> Result { diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index 1fc4a1953..f3041fbbf 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -12,9 +12,9 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethereum_rust_core::{types::TxKind, Address, H256, U256}; -use ethereum_rust_rlp; -use ethereum_rust_rlp::encode::RLPEncode; +use ethrex_core::{types::TxKind, Address, H256, U256}; +use ethrex_rlp; +use ethrex_rlp::encode::RLPEncode; use keccak_hash::keccak; use sha3::{Digest, Keccak256}; use std::{ @@ -25,7 +25,7 @@ use std::{ pub type Storage = HashMap; #[derive(Debug, Clone, Default)] -// TODO: https://github.com/lambdaclass/ethereum_rust/issues/604 +// TODO: https://github.com/lambdaclass/ethrex/issues/604 pub struct Substate { // accessed addresses and storage keys are considered WARM // pub accessed_addresses: HashSet
, @@ -151,7 +151,7 @@ impl VM { }) } } - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1088 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1088 } pub fn execute(&mut self, current_call_frame: &mut CallFrame) -> TransactionReport { @@ -600,7 +600,7 @@ impl VM { )) } - // TODO: Improve and test REVERT behavior for XCALL opcodes. Issue: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1061 + // TODO: Improve and test REVERT behavior for XCALL opcodes. Issue: https://github.com/lambdaclass/lambda_ethrex/issues/1061 #[allow(clippy::too_many_arguments)] pub fn generic_call( &mut self, @@ -782,7 +782,7 @@ impl VM { /// Common behavior for CREATE and CREATE2 opcodes /// /// Could be used for CREATE type transactions - // TODO: Improve and test REVERT behavior for CREATE. Issue: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1061 + // TODO: Improve and test REVERT behavior for CREATE. Issue: https://github.com/lambdaclass/lambda_ethrex/issues/1061 pub fn create( &mut self, value_in_wei_to_send: U256, diff --git a/crates/vm/levm/tests/edge_case_tests.rs b/crates/vm/levm/tests/edge_case_tests.rs index 4cb8bb422..99b198d05 100644 --- a/crates/vm/levm/tests/edge_case_tests.rs +++ b/crates/vm/levm/tests/edge_case_tests.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethereum_rust_core::U256; -use ethereum_rust_levm::{ +use ethrex_core::U256; +use ethrex_levm::{ operations::Operation, utils::{new_vm_with_bytecode, new_vm_with_ops}, }; diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index cb2e64048..06fef1b97 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -2,8 +2,8 @@ #![allow(clippy::unwrap_used)] use bytes::Bytes; -use ethereum_rust_core::{types::TxKind, Address, H256, U256}; -use ethereum_rust_levm::{ +use ethrex_core::{types::TxKind, Address, H256, U256}; +use ethrex_levm::{ account::Account, constants::*, db::{Cache, Db}, diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index 0733b095d..0c54dff60 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -9,14 +9,14 @@ use db::StoreWrapper; use execution_db::ExecutionDB; use std::cmp::min; -use ethereum_rust_core::{ +use ethrex_core::{ types::{ AccountInfo, Block, BlockHash, BlockHeader, ChainConfig, Fork, GenericTransaction, PrivilegedTxType, Receipt, Transaction, TxKind, Withdrawal, GWEI_TO_WEI, INITIAL_BASE_FEE, }, Address, BigEndianHash, H256, U256, }; -use ethereum_rust_storage::{error::StoreError, AccountUpdate, Store}; +use ethrex_storage::{error::StoreError, AccountUpdate, Store}; use lazy_static::lazy_static; use revm::{ db::{states::bundle_state::BundleRetention, AccountState, AccountStatus}, @@ -79,14 +79,14 @@ impl From for EvmState { cfg_if::cfg_if! { if #[cfg(feature = "levm")] { - use ethereum_rust_levm::{ + use ethrex_levm::{ db::{Cache, Database as LevmDatabase}, errors::{TransactionReport, TxResult, VMError}, vm::VM, Environment, }; use std::{collections::HashMap, sync::Arc}; - use ethereum_rust_core::types::code_hash; + use ethrex_core::types::code_hash; /// Executes all transactions in a block and returns their receipts. pub fn execute_block( @@ -116,7 +116,7 @@ cfg_if::cfg_if! { transaction.tx_type(), matches!(result.result, TxResult::Success), cumulative_gas_used, - // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1089 + // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1089 vec![], ); receipts.push(receipt); diff --git a/test_data/genesis-l2-old.json b/test_data/genesis-l2-old.json new file mode 100644 index 000000000..eb629d94d --- /dev/null +++ b/test_data/genesis-l2-old.json @@ -0,0 +1,548 @@ +{ + "config": { + "chainId": 1729, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "mergeNetsplitBlock": 0, + "terminalTotalDifficulty": 0, + "terminalTotalDifficultyPassed": true, + "shanghaiTime": 0, + "cancunTime": 0, + "pragueTime": 1718232101 + }, + "alloc": { + "0x0007a881CD95B1484fca47615B64803dad620C8d": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0000bd19F707CA481886244bDd20Bd6B8a81bd3e": { + "balance": "0xc097ce7bc90715b34b9f1000000000", + "nonce": "0" + }, + "0x000cD1537A823Ae7609E3897DA8d95801B557a8a": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006d77295a0260ceAC113c5Aa15CFf0d28d9723": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000eA2e72065A2ceCA7f677Bc5E648279c2D843d": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000a52D537c4150ec274dcE3962a0d179B7E71B0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0009aEFF154De37C8e02E83f93D2FeC5EC96f8a3": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000f1EB7F258D4A7683E5D0FC3C01058841DDC6f": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000aC79590dCc656c00c4453f123AcBf10DBb086": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002Bf507275217c9E5EE250bC1B5ca177bb4f74": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000a3fC3BFD55b37025E6F4f57B0B6121F54e5bF": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000b4C43cce938dfD3420F975591Ee46D872C136": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004b0C6de796fD980554cc7ff7B062b3B5079E1": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00025eea83bA285532F5054b238c938076833d13": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000352E93fe11f9B715fdc61864315970B3DC082": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c0d6b7C4516a5B274C51EA331A9410fe69127": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000D06C23EeD09A7Fa81cADd7eD5C783E8a25635": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003Ea7fDFCdb89E9ddAb0128ec5C628F8D09D45": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005C34d7B8b06CE8019C3Bb232dE82B2748A560": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00079f33619F70F1DCE64EB6782E45D3498d807C": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003E72436Ff296B3d39339784499D021b72Aca5": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00075af7E665F3Ca4A4b05520CD6d5c13BbFEAf8": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000b59AeD48ADCd6c36Ae5f437AbB9CA730a2c43": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004e4dfCed9d798767A4d7BA2B03495cE80A2b7": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e73282F60E2CdE0D4FA9B323B6D54d860f330": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00010AB05661Bfde304A4d884DF99d3011A83C54": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000B9Ea41A9dF00b7ae597afc0D10AF42666081F": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00087C666bf7f52758DE186570979C4C79747157": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0008a52c83D34f0791D07FfeD04Fb6b14f94E2D4": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A7Bbde38Fc53925D0De9cc1beE3038d36c2d2": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000Aa0154ed6560257d222B5dbE6ce4b66c48979": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000b681738e1f8aF387c41b2b1f0A04E0C33e9DB": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000D66A7706f2DD5F557d5b68e01E07E8FFDfaf5": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00069DA530A71Dc92D02090d7f5f63e326e9beD0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000db74a3da16609F183ACE7AF65B43D896349CE": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003B1aB565508e095a543C89531e3fbc4a349DA": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001c94c108BcE19CDb36b00F867A1798A81DedA": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000995137728C7C2a9142F4628f95c98Cac433d7": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000Ec60762AD0425A04C40c118Db5B9710Aa639e": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000Ebf88AE1BA960B06b0a9bbE576baa3B72E92E": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e1a554572dd96fF3d1F2664832F3E4a66E7b7": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00032C03f3b02D816128Fb5D2752398E2919a03c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A073dAC5ec2058a0De0e175874D5E297E086E": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e06626Bb8618D9A1867362D46ddb1bF95ad75": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000212949b4866db43bAF7c4e0975426710ED081": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00094cc0653B52406170105F4eb96C5e2f31Ab74": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000E67E4b1A23A3826304099cb24f337c916CF4b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000885A4932ebeD6D760EA381e4EdAe51A53db05": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000883A40409Fa2193b698928459CB9E4DD5f8D8": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002590DD45738F909115B163F1322A8A24a8B4E": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005f132597da3152a6Da6beDB7C10bcC9B1B7f5": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00031470def99c1d4dfE1fd08DD7A8520Ce21DB7": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001Ebe3a3bA36f57F5989B3F0e5BEEBc710569C": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006Bd0469166f63D0A1c33F71898D2b2E009b9b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00000A8d3f37af8DeF18832962Ee008d8dCa4F7b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e490f26249951F8527779399aa8F281509aC0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0000638374f7dB166990BDc6aBeE884Ee01a8920": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00031dE95353DeE86dc9B1248e825500DE0B39aF": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000511B42328794337D8b6846E5cFFef30c2d77A": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000d0576AdEf7083d53F6676bfc7c30d03b6Db1B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001E8Ff6406a7cd9071F46B8255Db6C16178448": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000C47c771A8db282eC233b28AD8525dc74D13FE": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000798832bb08268dB237898b95A8DaE9D58b62c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c877a5D9b9De61e5318B3f4330c56ecdC0865": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003Ffc1f09d39FBFE87eD63E98249039C7b1d9A": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000d72403c18B2516d8ada074E1E7822bF1084DB": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00054e17Db8C8Db028B19cB0f631888AdEb35E4b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002d9b2a816717C4d70040D66A714795F9B27a4": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002AfCC1B0B608E86b5a1Dc45dE08184E629796": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000b1db69627F04688aA47951d847c8BFAB3fFaE": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c2de896E4a92e796d6A9c1E4B01feB3e6Ed61": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000EDC52118DadB4B81f013005b6db2665B682ac": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0009e10C0D2F1a7A2b00b61c476aa8b608c60aDc": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000f2AbaA7581fAA2ad5C82b604C77ef68c3eAD9": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000F74AA6EE08C15076b3576eE33Ed3a80c9A1AD": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001533C6C5b425815b2BaDdCdd42DFF3be04BCb": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002D79686DeF20a0aB43FEA4a41a1Ad56529621": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00077A336FCA40F933a7A301F4a39C26594F3EB5": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000B05E15C62CBC266A4DD1804b017d1f6dB078b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000130badE00212bE1AA2F4aCFe965934635C9cD": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0008Bd31Ee6A758e168844cBEA107Ca4d87251aF": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A390975F21371F1Cf3C783a4A7C1aF49074Fe": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000701F7d594Fb146e4d1c71342012e48A788055": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005c6BeD054FEad199D72C6f663fC6fBf996153": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0009d862F87F26c638AAd14F2cc48FCa54DBf49d": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00029637dA962294449549f804f8184046F5fbB0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000279CB54E00B858774afEA4601034Db41c1A05": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003dDe6f01e3B755e24891a5B0f2463BaD83e15": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000086Eeea461Ca48e4D319F9789F3Efd134E574": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004351AD413792131011CC7ed8299dd783C6487": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00097B4463159340Ac83B9bdf657C304cD70c11c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004ad0D0823e3d31C6ECA2A3495373fA76c43aC": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005E815c1A3F40011Bd70C76062bbcBc51c546B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006A070bAC6195b59d4bC7f73741DCBe4e16b5e": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006cEE23d8E9BC8d99E826cDa50481394aD9bDD": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000a523148845bEe3EE1e9F83df8257a1191C85B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000D268F322F10925cdB5d2AD527E582259Da655": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000E5DE0a0175866d21F4Ec6c41F0422A05f14D6": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000cDF8Dba2393a40857cbCB0FCD9b998a941078": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A341763112a5E3452c7AEE45c382a3fb7dc78": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000635BCbB109781Cea0Cd53e9f1370Dbac9937f": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000E0ea540095B3853c4cb09E5Cdd197330D3B55": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00044cbfb4Ef6054667994C37c0fe0B6BB639718": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00065fC4337dF331242bEE738031dAf35817Ee9e": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000815A8A659a51A8EF01F02441947Ea99182568": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004C8da21c68dED2F63efD9836De7D43e7cDa10": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006ed38815a9439c59bD917c12f77a9A7D39BCE": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004Aa0442d0d43222431b3017912EC6a099771C": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000b3F6da04b6261B4154C8FaEd119632C49DBd5": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000AEBc2568796FDB763CAB67B31e0feE58Fe17d": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000425E97fC6692891876012824a210451cC06C4": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000036e0f87f8Cd3e97f9cfDB2e4E5Ff193c217a": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000305CD7184aB37fdd3D826B92A640218D09527": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c95f1D83De53B76a0828F1bCdB1DfE12C0ab3": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000882c5FbD315801e4C367BCB04dBD299B9F571": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0000E101815A78EbB9FBBa34F4871aD32d5eb6CD": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A997c1ceCB1DA78C16249e032e77d1865646a": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00056bde49E3cAA9166C2a4C4951d0Cf067956A0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e65342176C7dac47bc75113F569695d6A113C": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0008D608884cd733642ab17aCa0c8504850B94fA": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000dFE27e1b71a49B641ad762aB95558584878D1": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00085D9D1a71acf1080cED44CB501B350900627f": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0007d272a1f7Dfe862b030adE2922D149f3bDe3B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0004b230511F921934F33E8B4425E43295232680": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0007514395022786B59ff91408692462C48d872c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005b34eB0d99dE72DB14d466f692009c4049D46": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001a2c749FE0Ab1C09f1131BA17530f9D764fBC": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c6c1D8F778D981968F9904772B0c455E1C17c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000e64e0a2Fd76B4883c800833c82c5F2420b813": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000577bDc84B4019F77D9D09BDD8ED6145E0e890": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000029bD811D292E7f1CF36c0FA08fd753C45074": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000cE6740261E297FaD4c975D6D8F89f95C29add": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0001d0bAE8B1B9fe61d0B788E562A987813cbD98": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000E3388598A0534275104Ad44745620AF31EC7E": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000791D3185781e14eBb342E5df3BC9910f62E6F": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000Df55E76cf6dfD9598DD2b54948dE937f50f2B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000055acf237931902ceBf4B905BF59813180555": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00009074D8fc5Eeb25f1548Df05AD955E21FB08D": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000C1aE5FeCf09595C0C76Db609FEB2a5Af0962E": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000F76B2Fe7cCC13474de28586A877664EBA16B4": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000F7cFBa0B176Afc2eBadA9d4764d2eA6BBC5a1": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00002132cE94eEfB06eB15898C1AABd94feb0AC2": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00069dC0cc6b9d7B48B5348b12F625E8aB704104": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000A0191cf913E03bd594bC8817FC3B2895C0a25": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0007316aEDc52EB35c9B5c2E44e9fD712d1DF887": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000EBd066B6FEBB9d7f3B767DF06C08e369Dc20F": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00096af89fd96f0d6E1721d9145944e813317d46": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000C5e39879228A1Fc8dF2470822CB8ce2Af8e07": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000ea86B4A3d7e4AF8CFab052c8b9a040149b507": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000796370C839773893a2cEFA5fc81f2332936fB": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000990B05481b1661bc6211298f6429451B09425": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0008a02d3E8507621f430345b98478058cDca79A": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000d35f8cd11bd989216b3669cBaac6fd8c07196": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000541653a96ABAdDba52fAA8D118e570d529543": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006264bf7E3395309F728222641Ff8D0e1ad2C0": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000688AA0fBfB3F1e6554A63dF13bE08cB671b3b": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00030da862690D170F096074e9E8b38db7D6f037": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0005e37296348571bd3604f7E56B67a7022801f6": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000ed6E0F4Fdc3615663BF4A601E35e7A8d66E1c": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000c53b37fA4977B59FD3Efdb473D8069844aDeA": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00057714949aD700733C5b8E6cF3e8c6B7D228a2": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000C8FC4132881c31f67638c3941dF8D94a92299": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000fA71E446e1EcFd74d835b5bD6fA848A770d26": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000784B47aC2843419Df4cAd697d4e7b65CE1F93": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0002869e27c6FaEe08cCA6b765a726E7a076Ee0F": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0003135C47c441506b58483Ec6173F767182670B": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0006E80d584cbF9EB8C41CF2b009C607744a70F6": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000C1C05dBFf111c79D5c9E91420DFBEA1c31716": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x0009Bf72AF31A4E6B8Ef6FbbFcb017823E4d2aF2": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x00021C20F3e68F930077Cca109Ca3C044E8B39bD": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000E90875aC71eD46A11dc1b509d2B35E2c9C31F": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + }, + "0x000f17eB09AA3f28132323E6075C672949526d5A": { + "balance": "0xc097ce7bc90715b34b9f1000000000" + } + }, + "coinbase": "0x0000000000000000000000000000000000000000", + "difficulty": "0x01", + "extraData": "", + "gasLimit": "0x17d7840", + "nonce": "0x1234", + "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "timestamp": "1718040081" +} diff --git a/test_data/network_params.yaml b/test_data/network_params.yaml index f15c36bd2..6e251057b 100644 --- a/test_data/network_params.yaml +++ b/test_data/network_params.yaml @@ -1,5 +1,5 @@ participants: - - el_type: ethereumrust + - el_type: ethrex cl_type: lighthouse validator_count: 32 - el_type: geth @@ -20,4 +20,4 @@ assertoor_params: run_block_proposal_check: false run_blob_transaction_test: true tests: - - 'https://raw.githubusercontent.com/lambdaclass/lambda_ethereum_rust/refs/heads/main/test_data/el-stability-check.yml' + - 'https://raw.githubusercontent.com/lambdaclass/lambda_ethrex/refs/heads/main/test_data/el-stability-check.yml' From 3e7ece3b6332a99a9c87ff5f3af0e24302230b93 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 21 Nov 2024 19:07:40 -0300 Subject: [PATCH 12/49] Revert "Merge main" This reverts commit 1391e03ad0dd6b979acc5b8e82342f11bf1041eb. --- .github/workflows/ci.yaml | 2 +- .github/workflows/docker-build.yaml | 30 - .github/workflows/hive_and_assertoor.yaml | 41 +- .github/workflows/hive_coverage.yaml | 51 -- .github/workflows/l2_prover_ci.yaml | 11 +- .github/workflows/loc.yaml | 29 - .gitignore | 4 +- Cargo.toml | 35 +- Dockerfile | 9 +- Makefile | 27 +- README.md | 141 ++- .../{ethrex => ethereum_rust}/Cargo.toml | 10 +- .../{ethrex => ethereum_rust}/ef_tests.rs | 0 .../{ethrex => ethereum_rust}/network.rs | 2 +- .../{ethrex => ethereum_rust}/test_runner.rs | 8 +- .../{ethrex => ethereum_rust}/tests/cancun.rs | 2 +- .../tests/shanghai.rs | 2 +- .../{ethrex => ethereum_rust}/types.rs | 38 +- cmd/ef_tests/levm/Cargo.toml | 12 +- cmd/ef_tests/levm/deserialize.rs | 2 +- cmd/ef_tests/levm/report.rs | 8 +- cmd/ef_tests/levm/runner/levm_runner.rs | 8 +- cmd/ef_tests/levm/runner/mod.rs | 4 +- cmd/ef_tests/levm/runner/revm_runner.rs | 10 +- cmd/ef_tests/levm/types.rs | 4 +- cmd/ef_tests/levm/utils.rs | 6 +- cmd/ethereum_rust/Cargo.toml | 44 + cmd/{ethrex => ethereum_rust}/cli.rs | 12 +- cmd/{ethrex => ethereum_rust}/decode.rs | 6 +- .../ethereum_rust.rs} | 136 +-- .../Cargo.toml | 14 +- cmd/{ethrex_l2 => ethereum_rust_l2}/README.md | 45 +- .../assets/config_create.cast.gif | Bin .../assets/config_delete.cast.gif | Bin .../assets/config_edit.cast.gif | Bin .../assets/config_set.cast.gif | Bin .../assets/stack_init.cast.gif | Bin .../assets/stack_restart.cast.gif | Bin .../src/cli.rs | 24 +- .../src/commands/autocomplete.rs | 23 +- .../src/commands/config.rs | 0 .../src/commands/mod.rs | 0 .../src/commands/stack.rs | 34 +- .../src/commands/test.rs | 10 +- .../src/commands/utils.rs | 0 .../src/commands/wallet.rs | 12 +- .../src/config.rs | 10 +- .../src/lib.rs | 0 .../src/main.rs | 2 +- .../src/utils/config/default_values.rs | 0 .../src/utils/config/mod.rs | 36 +- .../src/utils/messages.rs | 2 +- .../src/utils/mod.rs | 0 cmd/ethrex/Cargo.toml | 44 - cmd/hive_report/Cargo.toml | 8 - cmd/hive_report/src/main.rs | 69 -- cmd/loc/Cargo.toml | 8 - cmd/loc/src/main.rs | 36 - crates/blockchain/Cargo.toml | 21 +- crates/blockchain/blockchain.rs | 12 +- crates/blockchain/constants.rs | 2 + crates/blockchain/dev/Cargo.toml | 4 +- crates/blockchain/dev/Dockerfile | 8 +- crates/blockchain/dev/block_producer.rs | 2 +- crates/blockchain/dev/docker-compose-dev.yaml | 6 +- .../blockchain/dev/utils/engine_client/mod.rs | 4 +- crates/blockchain/error.rs | 12 +- crates/blockchain/fork_choice.rs | 4 +- crates/blockchain/mempool.rs | 183 +++- crates/blockchain/payload.rs | 10 +- crates/blockchain/smoke_test.rs | 4 +- crates/common/Cargo.toml | 14 +- crates/common/rlp/Cargo.toml | 2 +- crates/common/rlp/decode.rs | 3 - crates/common/rlp/structs.rs | 10 +- crates/common/types/account.rs | 4 +- crates/common/types/blobs_bundle.rs | 305 +------ crates/common/types/block.rs | 6 +- crates/common/types/constants.rs | 1 - crates/common/types/fork_id.rs | 4 +- crates/common/types/genesis.rs | 4 +- crates/common/types/mod.rs | 2 +- crates/common/types/receipt.rs | 4 +- crates/common/types/transaction.rs | 2 +- crates/l2/.env.example | 1 + crates/l2/Cargo.toml | 20 +- crates/l2/Dockerfile | 8 +- crates/l2/Makefile | 66 +- crates/l2/contracts/Cargo.toml | 8 +- crates/l2/contracts/Dockerfile | 12 +- crates/l2/contracts/deployer.rs | 6 +- crates/l2/contracts/src/l1/CommonBridge.sol | 5 - .../src/l1/interfaces/ICommonBridge.sol | 5 - crates/l2/docker-compose-l2.yaml | 14 +- crates/l2/docs/README.md | 4 +- crates/l2/docs/contracts.md | 17 +- crates/l2/docs/overview.md | 10 +- crates/l2/docs/proposer.md | 15 +- crates/l2/docs/prover.md | 31 +- crates/l2/proposer/errors.rs | 11 +- crates/l2/proposer/l1_committer.rs | 84 +- crates/l2/proposer/l1_watcher.rs | 82 +- crates/l2/proposer/mod.rs | 6 +- crates/l2/proposer/prover_server.rs | 19 +- crates/l2/prover/Cargo.toml | 24 +- crates/l2/prover/src/lib.rs | 2 +- crates/l2/prover/src/main.rs | 4 +- crates/l2/prover/src/prover.rs | 8 +- crates/l2/prover/src/prover_client.rs | 2 +- crates/l2/prover/tests/perf_zkvm.rs | 29 +- crates/l2/prover/zkvm/interface/Cargo.toml | 2 +- .../l2/prover/zkvm/interface/guest/Cargo.toml | 8 +- .../prover/zkvm/interface/guest/src/main.rs | 8 +- crates/l2/sdk/Cargo.toml | 10 +- crates/l2/sdk/src/sdk.rs | 8 +- crates/l2/tests/tests.rs | 18 +- crates/l2/utils/config/l1_watcher.rs | 1 + crates/l2/utils/eth_client/errors.rs | 2 +- crates/l2/utils/eth_client/eth_sender.rs | 6 +- crates/l2/utils/eth_client/mod.rs | 38 +- crates/l2/utils/eth_client/transaction.rs | 24 + crates/l2/utils/test_data_io.rs | 14 +- crates/networking/p2p/Cargo.toml | 10 +- crates/networking/p2p/README.md | 14 +- crates/networking/p2p/bootnode.rs | 4 +- crates/networking/p2p/discv4.rs | 6 +- crates/networking/p2p/kademlia.rs | 2 +- crates/networking/p2p/net.rs | 43 +- crates/networking/p2p/rlpx/connection.rs | 82 +- crates/networking/p2p/rlpx/error.rs | 4 +- crates/networking/p2p/rlpx/eth/backend.rs | 93 +- crates/networking/p2p/rlpx/eth/blocks.rs | 8 +- crates/networking/p2p/rlpx/eth/receipts.rs | 6 +- crates/networking/p2p/rlpx/eth/status.rs | 4 +- .../networking/p2p/rlpx/eth/transactions.rs | 6 +- crates/networking/p2p/rlpx/frame.rs | 4 +- crates/networking/p2p/rlpx/handshake.rs | 6 +- crates/networking/p2p/rlpx/message.rs | 4 +- crates/networking/p2p/rlpx/p2p.rs | 4 +- crates/networking/p2p/rlpx/snap.rs | 4 +- crates/networking/p2p/rlpx/utils.rs | 4 +- crates/networking/p2p/snap.rs | 10 +- crates/networking/p2p/types.rs | 4 +- crates/networking/rpc/Cargo.toml | 14 +- crates/networking/rpc/admin/mod.rs | 8 +- .../rpc/engine/exchange_transition_config.rs | 2 +- crates/networking/rpc/engine/fork_choice.rs | 2 +- crates/networking/rpc/engine/payload.rs | 10 +- crates/networking/rpc/eth/account.rs | 4 +- crates/networking/rpc/eth/block.rs | 8 +- crates/networking/rpc/eth/fee_market.rs | 8 +- crates/networking/rpc/eth/filter.rs | 18 +- crates/networking/rpc/eth/gas_price.rs | 8 +- crates/networking/rpc/eth/logs.rs | 4 +- crates/networking/rpc/eth/transaction.rs | 21 +- crates/networking/rpc/rpc.rs | 10 +- crates/networking/rpc/types/account_proof.rs | 2 +- crates/networking/rpc/types/block.rs | 6 +- .../networking/rpc/types/block_identifier.rs | 4 +- crates/networking/rpc/types/fork_choice.rs | 2 +- crates/networking/rpc/types/payload.rs | 4 +- crates/networking/rpc/types/receipt.rs | 16 +- crates/networking/rpc/types/transaction.rs | 4 +- crates/networking/rpc/utils.rs | 12 +- crates/networking/rpc/web3/mod.rs | 4 +- crates/storage/store/Cargo.toml | 12 +- crates/storage/store/engines/api.rs | 6 +- crates/storage/store/engines/in_memory.rs | 6 +- crates/storage/store/engines/libmdbx.rs | 10 +- crates/storage/store/error.rs | 4 +- crates/storage/store/rlp.rs | 6 +- crates/storage/store/storage.rs | 16 +- crates/storage/trie/Cargo.toml | 6 +- crates/storage/trie/error.rs | 4 +- crates/storage/trie/nibbles.rs | 12 +- crates/storage/trie/node.rs | 2 +- crates/storage/trie/node/branch.rs | 2 +- crates/storage/trie/node/extension.rs | 2 +- crates/storage/trie/node/leaf.rs | 2 +- crates/storage/trie/node_hash.rs | 6 +- crates/storage/trie/rlp.rs | 2 +- crates/storage/trie/state.rs | 2 +- crates/storage/trie/trie.rs | 46 +- crates/storage/trie/verify_range.rs | 841 ------------------ crates/vm/Cargo.toml | 16 +- crates/vm/db.rs | 12 +- crates/vm/errors.rs | 6 +- crates/vm/execution_db.rs | 23 +- crates/vm/execution_result.rs | 4 +- crates/vm/levm/Cargo.toml | 6 +- crates/vm/levm/Makefile | 2 +- .../vm/levm/bench/revm_comparison/Cargo.toml | 2 +- .../vm/levm/bench/revm_comparison/src/lib.rs | 2 +- crates/vm/levm/src/account.rs | 2 +- crates/vm/levm/src/call_frame.rs | 2 +- crates/vm/levm/src/constants.rs | 4 +- crates/vm/levm/src/db.rs | 2 +- crates/vm/levm/src/environment.rs | 2 +- crates/vm/levm/src/errors.rs | 2 +- crates/vm/levm/src/gas_cost.rs | 2 +- crates/vm/levm/src/memory.rs | 2 +- .../vm/levm/src/opcode_handlers/arithmetic.rs | 2 +- .../src/opcode_handlers/bitwise_comparison.rs | 2 +- crates/vm/levm/src/opcode_handlers/block.rs | 2 +- .../levm/src/opcode_handlers/environment.rs | 2 +- crates/vm/levm/src/opcode_handlers/keccak.rs | 2 +- crates/vm/levm/src/opcode_handlers/logging.rs | 2 +- crates/vm/levm/src/opcode_handlers/push.rs | 2 +- .../stack_memory_storage_flow.rs | 4 +- crates/vm/levm/src/opcode_handlers/system.rs | 12 +- crates/vm/levm/src/operations.rs | 2 +- crates/vm/levm/src/utils.rs | 2 +- crates/vm/levm/src/vm.rs | 14 +- crates/vm/levm/tests/edge_case_tests.rs | 4 +- crates/vm/levm/tests/tests.rs | 4 +- crates/vm/vm.rs | 10 +- test_data/genesis-l2-old.json | 548 ------------ test_data/network_params.yaml | 4 +- 218 files changed, 1311 insertions(+), 3214 deletions(-) delete mode 100644 .github/workflows/docker-build.yaml delete mode 100644 .github/workflows/hive_coverage.yaml delete mode 100644 .github/workflows/loc.yaml rename cmd/ef_tests/{ethrex => ethereum_rust}/Cargo.toml (66%) rename cmd/ef_tests/{ethrex => ethereum_rust}/ef_tests.rs (100%) rename cmd/ef_tests/{ethrex => ethereum_rust}/network.rs (97%) rename cmd/ef_tests/{ethrex => ethereum_rust}/test_runner.rs (97%) rename cmd/ef_tests/{ethrex => ethereum_rust}/tests/cancun.rs (91%) rename cmd/ef_tests/{ethrex => ethereum_rust}/tests/shanghai.rs (88%) rename cmd/ef_tests/{ethrex => ethereum_rust}/types.rs (91%) create mode 100644 cmd/ethereum_rust/Cargo.toml rename cmd/{ethrex => ethereum_rust}/cli.rs (92%) rename cmd/{ethrex => ethereum_rust}/decode.rs (94%) rename cmd/{ethrex/ethrex.rs => ethereum_rust/ethereum_rust.rs} (72%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/Cargo.toml (71%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/README.md (74%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/config_create.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/config_delete.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/config_edit.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/config_set.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/stack_init.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/assets/stack_restart.cast.gif (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/cli.rs (62%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/autocomplete.rs (85%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/config.rs (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/mod.rs (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/stack.rs (89%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/test.rs (93%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/utils.rs (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/commands/wallet.rs (98%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/config.rs (87%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/lib.rs (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/main.rs (90%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/utils/config/default_values.rs (100%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/utils/config/mod.rs (90%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/utils/messages.rs (97%) rename cmd/{ethrex_l2 => ethereum_rust_l2}/src/utils/mod.rs (100%) delete mode 100644 cmd/ethrex/Cargo.toml delete mode 100644 cmd/hive_report/Cargo.toml delete mode 100644 cmd/hive_report/src/main.rs delete mode 100644 cmd/loc/Cargo.toml delete mode 100644 cmd/loc/src/main.rs create mode 100644 crates/l2/utils/eth_client/transaction.rs delete mode 100644 crates/storage/trie/verify_range.rs delete mode 100644 test_data/genesis-l2-old.json diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index b98a92d72..7d50e032f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -43,7 +43,7 @@ jobs: - name: Run cargo clippy run: | - cargo clippy --all-targets --all-features --workspace --exclude ethrex-prover -- -D warnings + cargo clippy --all-targets --all-features --workspace --exclude ethereum_rust-prover -- -D warnings - name: Run cargo fmt run: | diff --git a/.github/workflows/docker-build.yaml b/.github/workflows/docker-build.yaml deleted file mode 100644 index 4ae4be06e..000000000 --- a/.github/workflows/docker-build.yaml +++ /dev/null @@ -1,30 +0,0 @@ -name: Docker build - -on: - workflow_call: - -jobs: - docker-build: - name: Docker Build image - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build Docker image - uses: docker/build-push-action@v5 - with: - context: . - file: ./Dockerfile - load: true - tags: ethrex - outputs: type=docker,dest=/tmp/ethrex_image.tar - - - name: Upload artifacts - uses: actions/upload-artifact@v4 - with: - name: ethrex_image - path: /tmp/ethrex_image.tar - diff --git a/.github/workflows/hive_and_assertoor.yaml b/.github/workflows/hive_and_assertoor.yaml index 78287eab7..22a25aa6c 100644 --- a/.github/workflows/hive_and_assertoor.yaml +++ b/.github/workflows/hive_and_assertoor.yaml @@ -22,12 +22,33 @@ env: RUST_VERSION: 1.80.1 jobs: - build: - uses: ./.github/workflows/docker-build.yaml + docker-build: + name: Docker Build image + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build Docker image + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + load: true + tags: ethereum_rust + outputs: type=docker,dest=/tmp/ethereum_rust_image.tar + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: ethereum_rust_image + path: /tmp/ethereum_rust_image.tar run-hive: name: Hive - ${{ matrix.name }} - needs: [build] + needs: [docker-build] runs-on: ubuntu-latest strategy: matrix: @@ -46,7 +67,7 @@ jobs: run_command: make run-hive-on-latest SIMULATION=devp2p TEST_PATTERN="/AccountRange|StorageRanges|ByteCodes|TrieNodes" - simulation: eth name: "Devp2p eth tests" - run_command: make run-hive SIMULATION=devp2p TEST_PATTERN="eth/status|getblockheaders|getblockbodies|transaction" + run_command: make run-hive SIMULATION=devp2p TEST_PATTERN="eth/getblockheaders|getblockbodies|transaction" - simulation: engine name: "Engine tests" run_command: make run-hive-on-latest SIMULATION=ethereum/engine TEST_PATTERN="/Blob Transactions On Block 1, Cancun Genesis|Blob Transactions On Block 1, Shanghai Genesis|Blob Transaction Ordering, Single Account, Single Blob|Blob Transaction Ordering, Single Account, Dual Blob|Blob Transaction Ordering, Multiple Accounts|Replace Blob Transactions|Parallel Blob Transactions|ForkchoiceUpdatedV3 Modifies Payload ID on Different Beacon Root|NewPayloadV3 After Cancun|NewPayloadV3 Versioned Hashes|ForkchoiceUpdated Version on Payload Request" @@ -57,12 +78,12 @@ jobs: - name: Download artifacts uses: actions/download-artifact@v4 with: - name: ethrex_image + name: ethereum_rust_image path: /tmp - name: Load image run: | - docker load --input /tmp/ethrex_image.tar + docker load --input /tmp/ethereum_rust_image.tar - name: Checkout sources uses: actions/checkout@v3 @@ -81,24 +102,24 @@ jobs: run-assertoor: name: Assertoor - Stability Check runs-on: ubuntu-latest - needs: [build] + needs: [docker-build] steps: - uses: actions/checkout@v4 - name: Download artifacts uses: actions/download-artifact@v4 with: - name: ethrex_image + name: ethereum_rust_image path: /tmp - name: Load image run: | - docker load --input /tmp/ethrex_image.tar + docker load --input /tmp/ethereum_rust_image.tar - name: Setup kurtosis testnet and run assertoor tests uses: ethpandaops/kurtosis-assertoor-github-action@v1 with: kurtosis_version: '1.3.1' ethereum_package_url: 'github.com/lambdaclass/ethereum-package' - ethereum_package_branch: 'ethrex-integration' + ethereum_package_branch: 'ethereum-rust-integration' ethereum_package_args: './test_data/network_params.yaml' diff --git a/.github/workflows/hive_coverage.yaml b/.github/workflows/hive_coverage.yaml deleted file mode 100644 index b11ec2ffb..000000000 --- a/.github/workflows/hive_coverage.yaml +++ /dev/null @@ -1,51 +0,0 @@ -name: Daily Hive Coverage - -on: - schedule: - # Every day at UTC midnight - - cron: '0 0 * * *' - workflow_dispatch: - -env: - RUST_VERSION: 1.80.1 - -jobs: - build: - uses: ./.github/workflows/docker-build.yaml - - hive-coverage: - name: Run engine hive simulator to gather coverage information. - runs-on: ubuntu-latest - needs: [build] - steps: - # TODO: Maybe this can be reused as well. - - name: Download artifacts - uses: actions/download-artifact@v4 - with: - name: ethrex_image - path: /tmp - - - name: Load image - run: | - docker load --input /tmp/ethrex_image.tar - - - name: Checkout sources - uses: actions/checkout@v3 - - - name: Rustup toolchain install - uses: dtolnay/rust-toolchain@stable - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Setup Go - uses: actions/setup-go@v3 - - - name: Run Hive Simulation - run: make run-hive-on-latest SIMULATION=ethereum/engine - continue-on-error: true - - - name: Caching - uses: Swatinem/rust-cache@v2 - - - name: Generate the hive report - run: cargo run -p hive_report diff --git a/.github/workflows/l2_prover_ci.yaml b/.github/workflows/l2_prover_ci.yaml index f5d27b0e6..9dc4fcb20 100644 --- a/.github/workflows/l2_prover_ci.yaml +++ b/.github/workflows/l2_prover_ci.yaml @@ -1,8 +1,13 @@ name: L2 Prover CI on: - merge_group: + push: + branches: ["main"] + paths: + - "crates/l2/prover/**" pull_request: branches: ["**"] + paths: + - "crates/l2/prover/**" concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} @@ -17,9 +22,9 @@ jobs: matrix: action: - command: check - args: -p ethrex-prover + args: -p ethereum_rust-prover - command: clippy - args: -p ethrex-prover --all-targets --no-default-features + args: -p ethereum_rust-prover --all-targets --no-default-features steps: - name: Checkout sources uses: actions/checkout@v4 diff --git a/.github/workflows/loc.yaml b/.github/workflows/loc.yaml deleted file mode 100644 index b8a7046b4..000000000 --- a/.github/workflows/loc.yaml +++ /dev/null @@ -1,29 +0,0 @@ -name: Weekly LoC - -on: - schedule: - # Every Friday at midnight - - cron: "0 0 * * 5" - workflow_dispatch: - -env: - RUST_VERSION: 1.80.1 - -jobs: - loc: - name: Count ethrex loc and generate report - runs-on: ubuntu-latest - steps: - - name: Checkout sources - uses: actions/checkout@v4 - - - name: Rustup toolchain install - uses: dtolnay/rust-toolchain@stable - with: - toolchain: ${{ env.RUST_VERSION }} - - - name: Add Rust Cache - uses: Swatinem/rust-cache@v2 - - - name: Generate the loc report - run: make loc diff --git a/.gitignore b/.gitignore index 3ee1c729c..b595512e4 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ Cargo.lock # MSVC Windows builds of rustc generate these, which store debugging information *.pdb -cmd/ef_tests/ethrex/vectors +cmd/ef_tests/ethereum_rust/vectors cmd/ef_tests/levm/vectors # Repos checked out by make target @@ -36,7 +36,7 @@ crates/levm_mlir/output crates/levm_mlir/ethtests crates/levm_mlir/*.tar.gz -# ethrex L2 stuff +# Ethereum Rust L2 stuff volumes jwt.hex diff --git a/Cargo.toml b/Cargo.toml index 68e03158b..446ea3fa7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,38 +9,40 @@ members = [ "crates/vm", "crates/storage/trie", "crates/common/rlp", - "cmd/ethrex", - "cmd/ef_tests/ethrex", + "cmd/ethereum_rust", + "cmd/ef_tests/ethereum_rust", "cmd/ef_tests/levm", - "cmd/ethrex_l2", - "cmd/hive_report", + "cmd/ethereum_rust_l2", "crates/vm/levm", "crates/vm/levm/bench/revm_comparison", "crates/l2/", "crates/l2/prover", "crates/l2/contracts", "crates/l2/sdk", - "cmd/loc", ] resolver = "2" -default-members = ["cmd/ethrex", "cmd/ethrex_l2", "crates/l2/prover", "cmd/loc"] +default-members = [ + "cmd/ethereum_rust", + "cmd/ethereum_rust_l2", + "crates/l2/prover", +] [workspace.package] version = "0.1.0" edition = "2021" [workspace.dependencies] -ethrex-blockchain = { path = "./crates/blockchain" } -ethrex-core = { path = "./crates/common" } -ethrex-net = { path = "./crates/networking/p2p" } -ethrex-rpc = { path = "./crates/networking/rpc" } -ethrex-storage = { path = "./crates/storage/store" } -ethrex-vm = { path = "./crates/vm" } -ethrex-trie = { path = "./crates/storage/trie" } -ethrex-rlp = { path = "./crates/common/rlp" } -ethrex-l2 = { path = "./crates/l2" } -ethrex-prover = { path = "./crates/l2/prover" } +ethereum_rust-blockchain = { path = "./crates/blockchain" } +ethereum_rust-core = { path = "./crates/common" } +ethereum_rust-net = { path = "./crates/networking/p2p" } +ethereum_rust-rpc = { path = "./crates/networking/rpc" } +ethereum_rust-storage = { path = "./crates/storage/store" } +ethereum_rust-vm = { path = "./crates/vm" } +ethereum_rust-trie = { path = "./crates/storage/trie" } +ethereum_rust-rlp = { path = "./crates/common/rlp" } +ethereum_rust-l2 = { path = "./crates/l2" } +ethereum_rust-prover = { path = "./crates/l2/prover" } tracing = { version = "0.1", features = ["log"] } tracing-subscriber = "0.3.0" @@ -63,7 +65,6 @@ rand = "0.8.5" cfg-if = "1.0.0" reqwest = { version = "0.12.7", features = ["json"] } snap = "1.1.1" -k256 = { version = "0.13.3", features = ["ecdh"] } secp256k1 = { version = "0.29", default-features = false, features = [ "global-context", "recovery", diff --git a/Dockerfile b/Dockerfile index 0f3bec28b..c945fd449 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethrex +WORKDIR /ethereum_rust FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethrex/recipe.json recipe.json +COPY --from=planner /ethereum_rust/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,6 +27,7 @@ RUN cargo build --release FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethrex/target/release/ethrex . +COPY --from=builder ethereum_rust/target/release/ethereum_rust . EXPOSE 8545 -ENTRYPOINT [ "./ethrex" ] +ENTRYPOINT [ "./ethereum_rust" ] + diff --git a/Makefile b/Makefile index 706a9c7b0..63cd3c479 100644 --- a/Makefile +++ b/Makefile @@ -8,15 +8,15 @@ build: ## 🔨 Build the client cargo build --workspace lint: ## 🧹 Linter check - cargo clippy --all-targets --all-features --workspace --exclude ethrex-prover -- -D warnings + cargo clippy --all-targets --all-features --workspace --exclude ethereum_rust-prover -- -D warnings SPECTEST_VERSION := v3.0.0 SPECTEST_ARTIFACT := tests_$(SPECTEST_VERSION).tar.gz -SPECTEST_VECTORS_DIR := cmd/ef_tests/ethrex/vectors +SPECTEST_VECTORS_DIR := cmd/ef_tests/ethereum_rust/vectors CRATE ?= * test: $(SPECTEST_VECTORS_DIR) ## 🧪 Run each crate's tests - cargo test -p '$(CRATE)' --workspace --exclude ethrex-prover --exclude ethrex-levm --exclude ef_tests-levm -- --skip test_contract_compilation --skip testito + cargo test -p '$(CRATE)' --workspace --exclude ethereum_rust-prover --exclude ethereum_rust-levm --exclude ef_tests-levm -- --skip test_contract_compilation --skip testito clean: clean-vectors ## 🧹 Remove build artifacts cargo clean @@ -24,13 +24,13 @@ clean: clean-vectors ## 🧹 Remove build artifacts STAMP_FILE := .docker_build_stamp $(STAMP_FILE): $(shell find crates cmd -type f -name '*.rs') Cargo.toml Dockerfile - docker build -t ethrex . + docker build -t ethereum_rust . touch $(STAMP_FILE) build-image: $(STAMP_FILE) ## 🐳 Build the Docker image run-image: build-image ## 🏃 Run the Docker image - docker run --rm -p 127.0.0.1:8545:8545 ethrex --http.addr 0.0.0.0 + docker run --rm -p 127.0.0.1:8545:8545 ethereum_rust --http.addr 0.0.0.0 $(SPECTEST_ARTIFACT): rm -f tests_*.tar.gz # Delete older versions @@ -46,12 +46,12 @@ download-test-vectors: $(SPECTEST_VECTORS_DIR) ## 📥 Download test vectors clean-vectors: ## 🗑️ Clean test vectors rm -rf $(SPECTEST_VECTORS_DIR) -ETHEREUM_PACKAGE_REVISION := 5b49d02ee556232a73ea1e28000ec5b3fca1073f +ETHEREUM_PACKAGE_REVISION := c7952d75d72159d03aec423b46797df2ded11f99 # Shallow clones can't specify a single revision, but at least we avoid working # the whole history by making it shallow since a given date (one day before our # target revision). ethereum-package: - git clone --single-branch --branch ethrex-integration https://github.com/lambdaclass/ethereum-package + git clone --single-branch --branch ethereum-rust-integration https://github.com/lambdaclass/ethereum-package checkout-ethereum-package: ethereum-package ## 📦 Checkout specific Ethereum package revision cd ethereum-package && \ @@ -60,7 +60,7 @@ checkout-ethereum-package: ethereum-package ## 📦 Checkout specific Ethereum p localnet: stop-localnet-silent build-image checkout-ethereum-package ## 🌐 Start local network kurtosis run --enclave lambdanet ethereum-package --args-file test_data/network_params.yaml - docker logs -f $$(docker ps -q --filter ancestor=ethrex) + docker logs -f $$(docker ps -q --filter ancestor=ethereum_rust) stop-localnet: ## 🛑 Stop local network kurtosis enclave stop lambdanet @@ -71,7 +71,7 @@ stop-localnet-silent: @kurtosis enclave stop lambdanet >/dev/null 2>&1 || true @kurtosis enclave rm lambdanet --force >/dev/null 2>&1 || true -HIVE_REVISION := fc6ddec210095e2369019e7f4ab2f9f38e35a8e8 +HIVE_REVISION := 421852ec25e4e608fe5460656f4bf0637649619e # Shallow clones can't specify a single revision, but at least we avoid working # the whole history by making it shallow since a given date (one day before our # target revision). @@ -95,16 +95,13 @@ TEST_PATTERN ?= / # For example, to run the rpc-compat suites for eth_chainId & eth_blockNumber you should run: # `make run-hive SIMULATION=ethereum/rpc-compat TEST_PATTERN="/eth_chainId|eth_blockNumber"` run-hive: build-image setup-hive ## 🧪 Run Hive testing suite - cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" + cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" run-hive-on-latest: setup-hive ## 🧪 Run Hive testing suite with the latest docker image - cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" + cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" run-hive-debug: build-image setup-hive ## 🐞 Run Hive testing suite in debug mode - cd hive && ./hive --sim $(SIMULATION) --client ethrex --sim.limit "$(TEST_PATTERN)" --docker.output + cd hive && ./hive --sim $(SIMULATION) --client ethereumrust --sim.limit "$(TEST_PATTERN)" --docker.output clean-hive-logs: ## 🧹 Clean Hive logs rm -rf ./hive/workspace/logs - -loc: - cargo run -p loc diff --git a/README.md b/README.md index a5c10da9f..eb4c8fb2e 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -# ethrex +# Lambda Ethereum Rust [![Telegram Chat][tg-badge]][tg-url] -[![license](https://img.shields.io/github/license/lambdaclass/ethrex)](/LICENSE) +[![license](https://img.shields.io/github/license/lambdaclass/ethereum_rust)](/LICENSE) [tg-badge]: https://img.shields.io/endpoint?url=https%3A%2F%2Ftg.sumanjay.workers.dev%2Frust_ethereum%2F&logo=telegram&label=chat&color=neon [tg-url]: https://t.me/rust_ethereum @@ -13,7 +13,7 @@ This client supports running in two different modes: - As a regular Ethereum execution client - As a ZK-Rollup, where block execution is proven and the proof sent to an L1 network for verification, thus inheriting the L1's security. -We call the first one ethrex L1 and the second one ethrex L2. +We call the first one Lambda Ethereum Rust L1 and the second one Lambda Ethereum Rust L2. ## Philosophy @@ -33,7 +33,7 @@ Read more about our engineering philosophy [here](https://blog.lambdaclass.com/l - Prioritize code readability and maintainability over premature optimizations. - Avoid concurrency split all over the codebase. Concurrency adds complexity. Only use where strictly necessary. -# ethrex L1 +# Lambda Ethereum Rust L1 ## Roadmap @@ -47,7 +47,7 @@ An Ethereum execution client consists roughly of the following parts: - The Ethereum Wire Protocol (`ETH`), used for state synchronization and block/transaction propagation, among other things. This runs on top of `RLPx`. - The `SNAP` protocol, used for exchanging state snapshots. Mainly needed for **snap sync**, a more optimized way of doing state sync than the old fast sync (you can read more about it [here](https://blog.ethereum.org/2021/03/03/geth-v1-10-0)). - Block building and Fork choice management (i.e. logic to both build blocks so a validator can propose them and set where the head of the chain is currently at, according to what the consensus layer determines). This is essentially what our `blockchain` crate contains. -- The block execution logic itself, i.e., an EVM implementation. We are finishing an implementation of our own called [levm](https://github.com/lambdaclass/ethrex/tree/main/crates/vm/levm) (Lambda EVM). +- The block execution logic itself, i.e., an EVM implementation. We are finishing an implementation of our own called [levm](https://github.com/lambdaclass/ethereum_rust/tree/main/crates/vm/levm) (Lambda EVM). Because most of the milestones below do not overlap much, we are currently working on them in parallel. @@ -60,61 +60,61 @@ Implement the bare minimum required to: In a bit more detail: -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Add `libmdbx` bindings and basic API, create tables for state (blocks, transactions, etc) | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Add `libmdbx` bindings and basic API, create tables for state (blocks, transactions, etc) | ✅ | EVM wrapper for block execution | ✅ | | JSON RPC API server setup | ✅ | | RPC State-serving endpoints | 🏗️ (almost done, a few endpoints are left) | -| Basic Engine API implementation. Set new chain head (`forkchoiceUpdated`) and new block (`newPayload`). | ✅ +| Basic Engine API implementation. Set new chain head (`forkchoiceUpdated`) and new block (`newPayload`). | ✅ -See detailed issues and progress for this milestone [here](https://github.com/lambdaclass/ethrex/milestone/1). +See detailed issues and progress for this milestone [here](https://github.com/lambdaclass/ethereum_rust/milestone/1). ### Milestone 2: History & Reorgs Implement support for block reorganizations and historical state queries. This milestone involves persisting the state trie to enable efficient access to historical states and implementing a tree structure for the blockchain to manage multiple chain branches. It also involves a real implementation of the `engine_forkchoiceUpdated` Engine API when we do not have to build the block ourselves (i.e. when `payloadAttributes` is null). -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Persist data on an on-disk Merkle Patricia Tree using `libmdbx` | ✅ -| Engine API `forkchoiceUpdated` implementation (without `payloadAttributes`) | 🏗️ -| Support for RPC historical queries, i.e. queries (`eth_call`, `eth_getBalance`, etc) at any block | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Persist data on an on-disk Merkle Patricia Tree using `libmdbx` | ✅ +| Engine API `forkchoiceUpdated` implementation (without `payloadAttributes`) | 🏗️ +| Support for RPC historical queries, i.e. queries (`eth_call`, `eth_getBalance`, etc) at any block | ✅ -Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/4). +Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/4). ### Milestone 3: Block building Add the ability to build new payloads (blocks), so the consensus client can propose new blocks based on transactions received from the RPC endpoints. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| `engine_forkchoiceUpdated` implementation with a non-null `payloadAttributes` | 🏗️ -| `engine_getPayload` endpoint implementation that builds blocks. | 🏗️ -| Implement a mempool and the `eth_sendRawTransaction` endpoint where users can send transactions | ✅ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| `engine_forkchoiceUpdated` implementation with a non-null `payloadAttributes` | 🏗️ +| `engine_getPayload` endpoint implementation that builds blocks. | 🏗️ +| Implement a mempool and the `eth_sendRawTransaction` endpoint where users can send transactions | ✅ -Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/5). +Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/5). ### Milestone 4: P2P Network Implement the peer to peer networking stack, i.e. the DevP2P protocol. This includes `discv4`, `RLPx` and the `eth` capability. This will let us get and retrieve blocks and transactions from other nodes. We'll add the transactions we receive to the mempool. We'll also download blocks from other nodes when we get payloads where the parent isn't in our local chain. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Implement `discv4` for peer discovery | ✅ -| Implement the `RLPx` transport protocol | 🏗️ -| Implement the `eth` capability | 🏗️ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Implement `discv4` for peer discovery | ✅ +| Implement the `RLPx` transport protocol | 🏗️ +| Implement the `eth` capability | 🏗️ -Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/2). +Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/2). ### Milestone 5: State Sync Add support for the `SNAP` protocol, which lets us get a recent copy of the blockchain state instead of going through all blocks from genesis. This is used for used for snap sync. Since we don't support older versions of the spec by design, this is a prerequisite to being able to sync the node with public networks, including mainnet. -| Task Description | Status | -| --------- | --------------------------------------------------------------------------- | -| Implement `SNAP` protocol for snap syncing | ❌ +| Task Description | Status | +| --------- | --------------------------------------------------------------------------- | +| Implement `SNAP` protocol for snap syncing | ❌ -Detailed issues and progress [here](https://github.com/lambdaclass/ethrex/milestone/3). +Detailed issues and progress [here](https://github.com/lambdaclass/ethereum_rust/milestone/3). ## Quick Start (L1 localnet) @@ -160,12 +160,12 @@ make build ### Database Currently, the database is `libmdbx`, it will be set up when you start the client. The location of the db's files will depend on your OS: -- Mac: `~/Library/Application Support/ethrex` -- Linux: `~/.config/ethrex` +- Mac: `~/Library/Application Support/ethereum_rust` +- Linux: `~/.config/ethereum_rust` You can delete the db with: ```bash -cargo run --bin ethrex -- removedb +cargo run --bin ethereum_rust -- removedb ``` ### Test @@ -192,7 +192,7 @@ make test CRATE= ``` For example: ```bash -make test CRATE="ethrex-blockchain" +make test CRATE="ethereum_rust-blockchain" ``` ##### Hive Tests @@ -236,7 +236,7 @@ This example runs **every** test under rpc, with debug output Example run: ```bash -cargo run --bin ethrex -- --network test_data/genesis-kurtosis.json +cargo run --bin ethereum_rust -- --network test_data/genesis-kurtosis.json ``` The `network` argument is mandatory, as it defines the parameters of the chain. @@ -244,7 +244,7 @@ For more information about the different cli arguments check out the next sectio ### CLI Commands -ethrex supports the following command line arguments: +Ethereum Rust supports the following command line arguments: - `--network `: Receives a `Genesis` struct in json format. This is the only argument which is required. You can look at some example genesis files at `test_data/genesis*`. - `--datadir `: Receives the name of the directory where the Database is located. - `--import `: Receives an rlp encoded `Chain` object (aka a list of `Block`s). You can look at the example chain file at `test_data/chain.rlp`. @@ -260,11 +260,11 @@ ethrex supports the following command line arguments: - `--bootnodes `: Comma separated enode URLs for P2P discovery bootstrap. - `--log.level `: The verbosity level used for logs. Default value: info. possible values: info, debug, trace, warn, error -# ethrex L2 +# Lambda Ethereum Rust L2 -In this mode, the ethrex code is repurposed to run a rollup that settles on Ethereum as the L1. +In this mode, the Ethereum Rust code is repurposed to run a rollup that settles on Ethereum as the L1. -The main differences between this mode and regular ethrex are: +The main differences between this mode and regular Ethereum Rust are: - There is no consensus, the node is turned into a sequencer that proposes blocks for the network. - Block execution is proven using a RISC-V zkVM and its proofs are sent to L1 for verification. @@ -286,15 +286,14 @@ At a high level, the following new parts are added to the node: | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | | 0 | Users can deposit Eth in the L1 (Ethereum) and receive the corresponding funds on the L2. | ✅ | | 1 | The network supports basic L2 functionality, allowing users to deposit and withdraw funds to join and exit the network, while also interacting with the network as they do normally on the Ethereum network (deploying contracts, sending transactions, etc). | ✅ | -| 2 | The block execution is proven with a RISC-V zkVM and the proof is verified by the Verifier L1 contract. | ✅ | -| 3 | The network now commits to state diffs instead of the full state, lowering the commit transactions costs. These diffs are also submitted in compressed form, further reducing costs. It also supports EIP 4844 for L1 commit transactions, which means state diffs are sent as blob sidecars instead of calldata. | 🏗️ | -| 4 | Use our own EVM implementation | 🏗️ | -| 5 | The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. | ❌ | -| 6 | Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. | ❌ | -| 7 | The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. | ❌ | -| 8 | The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. | ❌ | -| 9 | The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. | ❌ | -| 10 | The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. | ❌ | +| 2 | The block execution is proven with a RISC-V zkVM and the proof is verified by the Verifier L1 contract. | 🏗️ | +| 3 | The network now commits to state diffs instead of the full state, lowering the commit transactions costs. These diffs are also submitted in compressed form, further reducing costs. It also supports EIP 4844 for L1 commit transactions, which means state diffs are sent as blob sidecars instead of calldata. | ❌ | +| 4 | The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. | ❌ | +| 5 | Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. | ❌ | +| 6 | The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. | ❌ | +| 7 | The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. | ❌ | +| 8 | The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. | ❌ | +| 9 | The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. | ❌ | ### Milestone 0 @@ -332,8 +331,8 @@ The L2's block execution is proven with a RISC-V zkVM and the proof is verified | Task Description | Status | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | | On the EVM, return all storage touched during block execution to pass to the prover as a witness | ✅ | -| Make the `onChainproposer` L1 contract verify the SNARK proof on the `verify` function. | ✅ | -| Add a `proverClient` binary that asks the sequencer for witness data to prove, generates proofs of execution and submits proofs to the `proverServer` component (see below) | ✅ | +| Make the `onChainproposer` L1 contract verify the SNARK proof on the `verify` function. | 🏗️ | +| Add a `proverClient` binary that asks the sequencer for witness data to prove, generates proofs of execution and submits proofs to the `proverServer` component (see below) | 🏗️ | | Add a `proverServer` component that feeds the `proverClient` with block witness data to be proven and delivers proofs to the `proposer` to send the L1 transaction for block verification | ✅ | ### Milestone 3: State diffs + Data compression + EIP 4844 (Blobs) @@ -346,27 +345,13 @@ It also supports EIP 4844 for L1 commit transactions, which means state diffs ar | Task Description | Status | | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | -| The sequencer sends state diffs to the prover instead of full transaction data. | ✅ | +| The sequencer sends state diffs to the prover instead of full transaction data. | ❌ | | On the prover, prove the state diffs compression | ❌ | -| On the `proposer`, send the state diffs through a blob in a EIP 4844 transaction. | ✅ | +| On the `proposer`, send the state diffs through a blob in a EIP 4844 transaction. | ❌ | | Adapt the prover to prove a KZG commitment to the state diff and use the point evaluation precompile to show that the blob sent to the L1 is indeed the correct one through a proof of equivalence protocol | ❌ | | Add a command to the CLI to reconstructing the full L2 state from all the blob data on the L1. | ❌ | -### Milestone 4: Own EVM Implementation - -Finish our own EVM implementation (`levm`) to be able to add custom behaviour to the network (like account abstraction below). - -#### Status - -| Task Description | Status | -| ---------------------------------------------------- | ------ | -| Implement all opcodes | ✅ | -| Pass all execution (non-precompile) EF tests | 🏗️ | -| Implement all precompiles | 🏗️ | -| Pass all execution EF tests | 🏗️ | -| Full Ethereum Rust Integration (pass all Hive tests) | 🏗️ | - -### Milestone 5: Account Abstraction +### Milestone 4: Account Abstraction The L2 supports native account abstraction following EIP 7702, allowing for custom transaction validation logic and paymaster flows. @@ -377,7 +362,7 @@ The L2 supports native account abstraction following EIP 7702, allowing for cust | Add support for `SET_CODE_TX_TYPE` transactions (i.e. implement EIP 7702). | ❌ | | Add examples of WebAuthn signing and paymaster flows using EIP 7702 | ❌ | -### Milestone 6: L2s interoperability +### Milestone 5: L2s interoperability Support multiple L2s sharing the same bridge contract on L1 for seamless interoperability. @@ -390,7 +375,7 @@ Support multiple L2s sharing the same bridge contract on L1 for seamless interop TODO: Expand on tasks about proper interoperability between chains (seamlessly bridging between chains, etc). -### Milestone 7: Custom Native token +### Milestone 6: Custom Native token The L2 can also be deployed using a custom native token, meaning that a certain ERC20 can be the common currency that's used for paying network fees. @@ -402,7 +387,7 @@ The L2 can also be deployed using a custom native token, meaning that a certain | On the `commonBridge`, for custom native token deposits, `msg.value` should always be zero, and the amount of the native token to mint should be a new `valueToMintOnL2` argument. The amount should be deducted from the caller thorugh a `transferFrom`. | ❌ | | On the CLI, add support for custom native token deposits and withdrawals | ❌ | -### Milestone 8: Security (TEEs and Multi Prover support) +### Milestone 7: Security (TEEs and Multi Prover support) The L2 has added security mechanisms in place, running on Trusted Execution Environments and Multi Prover setup where multiple guarantees (Execution on TEEs, zkVMs/proving systems) are required for settlement on the L1. This better protects against possible security bugs on implementations. @@ -414,7 +399,7 @@ The L2 has added security mechanisms in place, running on Trusted Execution Envi | Support verifying multiple different zkVM executions on the `onChainProposer` L1 contract. | ❌ | | Support running the operator on a TEE environment | ❌ | -### Milestone 9: Based Contestable Rollup +### Milestone 8: Based Contestable Rollup The network can be run as a Based Rollup, meaning sequencing is done by the Ethereum Validator set; transactions are sent to a private mempool and L1 Validators that opt into the L2 sequencing propose blocks for the L2 on every L1 block. @@ -426,7 +411,7 @@ The network can be run as a Based Rollup, meaning sequencing is done by the Ethe TODO: Expand on this. -### Milestone 10: Validium +### Milestone 9: Validium The L2 can be initialized in Validium Mode, meaning the Data Availability layer is no longer the L1, but rather a DA layer of the user's choice. @@ -458,7 +443,7 @@ The L2 can be initialized in Validium Mode, meaning the Data Availability layer make init ``` -This will setup a local Ethereum network as the L1, deploy all the needed contracts on it, then start an ethrex L2 node pointing to it. +This will setup a local Ethereum network as the L1, deploy all the needed contracts on it, then start an Ethereum Rust L2 node pointing to it. ### Restarting the network @@ -480,10 +465,10 @@ Most of them are [here](https://github.com/ethpandaops/ethereum-package/blob/mai } ``` -## ethrex L2 Docs +## Lambda Ethereum Rust L2 Docs -- [ethrex L2 Docs](./crates/l2/docs/README.md) -- [ethrex L2 CLI Docs](./cmd/ethrex_l2/README.md) +- [Ethereum Rust L2 Docs](./crates/l2/docs/README.md) +- [Ethereum Rust L2 CLI Docs](./cmd/ethereum_rust_l2/README.md) ## 📚 References and acknowledgements diff --git a/cmd/ef_tests/ethrex/Cargo.toml b/cmd/ef_tests/ethereum_rust/Cargo.toml similarity index 66% rename from cmd/ef_tests/ethrex/Cargo.toml rename to cmd/ef_tests/ethereum_rust/Cargo.toml index 4c77e6c6f..abd9c11c4 100644 --- a/cmd/ef_tests/ethrex/Cargo.toml +++ b/cmd/ef_tests/ethereum_rust/Cargo.toml @@ -1,13 +1,13 @@ [package] -name = "ef_tests-ethrex" +name = "ef_tests-ethereum_rust" version.workspace = true edition.workspace = true [dependencies] -ethrex-blockchain.workspace = true -ethrex-core.workspace = true -ethrex-storage.workspace = true -ethrex-rlp.workspace = true +ethereum_rust-blockchain.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-rlp.workspace = true serde.workspace = true serde_json.workspace = true bytes.workspace = true diff --git a/cmd/ef_tests/ethrex/ef_tests.rs b/cmd/ef_tests/ethereum_rust/ef_tests.rs similarity index 100% rename from cmd/ef_tests/ethrex/ef_tests.rs rename to cmd/ef_tests/ethereum_rust/ef_tests.rs diff --git a/cmd/ef_tests/ethrex/network.rs b/cmd/ef_tests/ethereum_rust/network.rs similarity index 97% rename from cmd/ef_tests/ethrex/network.rs rename to cmd/ef_tests/ethereum_rust/network.rs index 300ab4ee0..c69a5b2d5 100644 --- a/cmd/ef_tests/ethrex/network.rs +++ b/cmd/ef_tests/ethereum_rust/network.rs @@ -1,4 +1,4 @@ -use ethrex_core::types::ChainConfig; +use ethereum_rust_core::types::ChainConfig; use lazy_static::lazy_static; use serde::Deserialize; diff --git a/cmd/ef_tests/ethrex/test_runner.rs b/cmd/ef_tests/ethereum_rust/test_runner.rs similarity index 97% rename from cmd/ef_tests/ethrex/test_runner.rs rename to cmd/ef_tests/ethereum_rust/test_runner.rs index 7d1d13f03..2bfb66507 100644 --- a/cmd/ef_tests/ethrex/test_runner.rs +++ b/cmd/ef_tests/ethereum_rust/test_runner.rs @@ -1,12 +1,12 @@ use std::{collections::HashMap, path::Path}; use crate::types::{BlockWithRLP, TestUnit}; -use ethrex_blockchain::{add_block, fork_choice::apply_fork_choice}; -use ethrex_core::types::{ +use ethereum_rust_blockchain::{add_block, fork_choice::apply_fork_choice}; +use ethereum_rust_core::types::{ Account as CoreAccount, Block as CoreBlock, BlockHeader as CoreBlockHeader, }; -use ethrex_rlp::decode::RLPDecode; -use ethrex_storage::{EngineType, Store}; +use ethereum_rust_rlp::decode::RLPDecode; +use ethereum_rust_storage::{EngineType, Store}; pub fn run_ef_test(test_key: &str, test: &TestUnit) { // check that the decoded genesis block header matches the deserialized one diff --git a/cmd/ef_tests/ethrex/tests/cancun.rs b/cmd/ef_tests/ethereum_rust/tests/cancun.rs similarity index 91% rename from cmd/ef_tests/ethrex/tests/cancun.rs rename to cmd/ef_tests/ethereum_rust/tests/cancun.rs index 13ade9dd2..4ed4ad94d 100644 --- a/cmd/ef_tests/ethrex/tests/cancun.rs +++ b/cmd/ef_tests/ethereum_rust/tests/cancun.rs @@ -1,6 +1,6 @@ use std::path::Path; -use ef_tests_ethrex::test_runner::{parse_test_file, run_ef_test}; +use ef_tests_ethereum_rust::test_runner::{parse_test_file, run_ef_test}; fn parse_and_execute(path: &Path) -> datatest_stable::Result<()> { let tests = parse_test_file(path); diff --git a/cmd/ef_tests/ethrex/tests/shanghai.rs b/cmd/ef_tests/ethereum_rust/tests/shanghai.rs similarity index 88% rename from cmd/ef_tests/ethrex/tests/shanghai.rs rename to cmd/ef_tests/ethereum_rust/tests/shanghai.rs index 666eff873..d3689f939 100644 --- a/cmd/ef_tests/ethrex/tests/shanghai.rs +++ b/cmd/ef_tests/ethereum_rust/tests/shanghai.rs @@ -1,6 +1,6 @@ use std::path::Path; -use ef_tests_ethrex::test_runner::{parse_test_file, run_ef_test}; +use ef_tests_ethereum_rust::test_runner::{parse_test_file, run_ef_test}; fn parse_and_execute(path: &Path) -> datatest_stable::Result<()> { let tests = parse_test_file(path); diff --git a/cmd/ef_tests/ethrex/types.rs b/cmd/ef_tests/ethereum_rust/types.rs similarity index 91% rename from cmd/ef_tests/ethrex/types.rs rename to cmd/ef_tests/ethereum_rust/types.rs index e05ecc031..05a525475 100644 --- a/cmd/ef_tests/ethrex/types.rs +++ b/cmd/ef_tests/ethereum_rust/types.rs @@ -1,11 +1,11 @@ use bytes::Bytes; -use ethrex_core::types::{ - code_hash, Account as ethrexAccount, AccountInfo, Block as CoreBlock, BlockBody, +use ethereum_rust_core::types::{ + code_hash, Account as ethereum_rustAccount, AccountInfo, Block as CoreBlock, BlockBody, EIP1559Transaction, EIP2930Transaction, EIP4844Transaction, LegacyTransaction, - Transaction as ethrexTransaction, TxKind, + Transaction as ethereum_rustTransaction, TxKind, }; -use ethrex_core::types::{Genesis, GenesisAccount, Withdrawal}; -use ethrex_core::{types::BlockHeader, Address, Bloom, H256, H64, U256}; +use ethereum_rust_core::types::{Genesis, GenesisAccount, Withdrawal}; +use ethereum_rust_core::{types::BlockHeader, Address, Bloom, H256, H64, U256}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; @@ -18,7 +18,7 @@ pub struct TestUnit { pub info: Option, pub blocks: Vec, pub genesis_block_header: Header, - #[serde(rename = "genesisRLP", with = "ethrex_core::serde_utils::bytes")] + #[serde(rename = "genesisRLP", with = "ethereum_rust_core::serde_utils::bytes")] pub genesis_rlp: Bytes, pub lastblockhash: H256, pub network: Network, @@ -70,7 +70,7 @@ impl TestUnit { #[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)] pub struct Account { pub balance: U256, - #[serde(with = "ethrex_core::serde_utils::bytes")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub code: Bytes, pub nonce: U256, pub storage: HashMap, @@ -109,7 +109,7 @@ pub struct Header { pub bloom: Bloom, pub coinbase: Address, pub difficulty: U256, - #[serde(with = "ethrex_core::serde_utils::bytes")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub extra_data: Bytes, pub gas_limit: U256, pub gas_used: U256, @@ -134,7 +134,7 @@ pub struct Header { #[derive(Debug, PartialEq, Eq, Deserialize, Clone)] #[serde(rename_all = "camelCase")] pub struct BlockWithRLP { - #[serde(with = "ethrex_core::serde_utils::bytes")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub rlp: Bytes, #[serde(flatten)] inner: Option, @@ -191,7 +191,7 @@ impl From for CoreBlock { pub struct Transaction { #[serde(rename = "type")] pub transaction_type: Option, - #[serde(with = "ethrex_core::serde_utils::bytes")] + #[serde(with = "ethereum_rust_core::serde_utils::bytes")] pub data: Bytes, pub gas_limit: U256, pub gas_price: Option, @@ -211,7 +211,7 @@ pub struct Transaction { pub to: TxKind, } -// Conversions between EFtests & ethrex types +// Conversions between EFtests & ethereum_rust types impl From
for BlockHeader { fn from(val: Header) -> Self { BlockHeader { @@ -239,17 +239,17 @@ impl From
for BlockHeader { } } -impl From for ethrexTransaction { +impl From for ethereum_rustTransaction { fn from(val: Transaction) -> Self { match val.transaction_type { Some(tx_type) => match tx_type.as_u64() { - 0 => ethrexTransaction::LegacyTransaction(val.into()), - 1 => ethrexTransaction::EIP2930Transaction(val.into()), - 2 => ethrexTransaction::EIP1559Transaction(val.into()), - 3 => ethrexTransaction::EIP4844Transaction(val.into()), + 0 => ethereum_rustTransaction::LegacyTransaction(val.into()), + 1 => ethereum_rustTransaction::EIP2930Transaction(val.into()), + 2 => ethereum_rustTransaction::EIP1559Transaction(val.into()), + 3 => ethereum_rustTransaction::EIP4844Transaction(val.into()), _ => unimplemented!(), }, - None => ethrexTransaction::LegacyTransaction(val.into()), + None => ethereum_rustTransaction::LegacyTransaction(val.into()), } } } @@ -365,9 +365,9 @@ impl From for EIP2930Transaction { } } -impl From for ethrexAccount { +impl From for ethereum_rustAccount { fn from(val: Account) -> Self { - ethrexAccount { + ethereum_rustAccount { info: AccountInfo { code_hash: code_hash(&val.code), balance: val.balance, diff --git a/cmd/ef_tests/levm/Cargo.toml b/cmd/ef_tests/levm/Cargo.toml index 6a553046e..5e038b111 100644 --- a/cmd/ef_tests/levm/Cargo.toml +++ b/cmd/ef_tests/levm/Cargo.toml @@ -4,12 +4,12 @@ version.workspace = true edition.workspace = true [dependencies] -ethrex-blockchain = { workspace = true, features = ["levm"] } -ethrex-core.workspace = true -ethrex-storage.workspace = true -ethrex-rlp.workspace = true -ethrex-vm = { workspace = true, features = ["levm"] } -ethrex-levm = { path = "../../../crates/vm/levm" } +ethereum_rust-blockchain = { workspace = true, features = ["levm"] } +ethereum_rust-core.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-rlp.workspace = true +ethereum_rust-vm = { workspace = true, features = ["levm"] } +ethereum_rust-levm = { path = "../../../crates/vm/levm" } serde.workspace = true serde_json.workspace = true bytes.workspace = true diff --git a/cmd/ef_tests/levm/deserialize.rs b/cmd/ef_tests/levm/deserialize.rs index 612e17045..81590178f 100644 --- a/cmd/ef_tests/levm/deserialize.rs +++ b/cmd/ef_tests/levm/deserialize.rs @@ -1,6 +1,6 @@ use crate::types::{EFTest, EFTests}; use bytes::Bytes; -use ethrex_core::U256; +use ethereum_rust_core::U256; use serde::Deserialize; use std::{collections::HashMap, str::FromStr}; diff --git a/cmd/ef_tests/levm/report.rs b/cmd/ef_tests/levm/report.rs index 7c0a3d9e4..513c2c363 100644 --- a/cmd/ef_tests/levm/report.rs +++ b/cmd/ef_tests/levm/report.rs @@ -1,9 +1,9 @@ use crate::runner::EFTestRunnerError; use colored::Colorize; -use ethrex_core::Address; -use ethrex_levm::errors::{TransactionReport, TxResult, VMError}; -use ethrex_storage::AccountUpdate; -use ethrex_vm::SpecId; +use ethereum_rust_core::Address; +use ethereum_rust_levm::errors::{TransactionReport, TxResult, VMError}; +use ethereum_rust_storage::AccountUpdate; +use ethereum_rust_vm::SpecId; use revm::primitives::{ExecutionResult as RevmExecutionResult, HashSet}; use std::{ collections::HashMap, diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 46d003d3e..39b43127a 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -4,18 +4,18 @@ use crate::{ types::EFTest, utils, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{code_hash, AccountInfo}, H256, U256, }; -use ethrex_levm::{ +use ethereum_rust_levm::{ db::Cache, errors::{TransactionReport, VMError}, vm::VM, Environment, }; -use ethrex_storage::AccountUpdate; -use ethrex_vm::db::StoreWrapper; +use ethereum_rust_storage::AccountUpdate; +use ethereum_rust_vm::db::StoreWrapper; use keccak_hash::keccak; use std::{collections::HashMap, sync::Arc}; diff --git a/cmd/ef_tests/levm/runner/mod.rs b/cmd/ef_tests/levm/runner/mod.rs index c496ff9d5..63217ea9f 100644 --- a/cmd/ef_tests/levm/runner/mod.rs +++ b/cmd/ef_tests/levm/runner/mod.rs @@ -4,8 +4,8 @@ use crate::{ }; use clap::Parser; use colored::Colorize; -use ethrex_levm::errors::{TransactionReport, VMError}; -use ethrex_vm::SpecId; +use ethereum_rust_levm::errors::{TransactionReport, VMError}; +use ethereum_rust_vm::SpecId; use spinoff::{spinners::Dots, Color, Spinner}; pub mod levm_runner; diff --git a/cmd/ef_tests/levm/runner/revm_runner.rs b/cmd/ef_tests/levm/runner/revm_runner.rs index ea59ed28f..dc09a372c 100644 --- a/cmd/ef_tests/levm/runner/revm_runner.rs +++ b/cmd/ef_tests/levm/runner/revm_runner.rs @@ -4,10 +4,10 @@ use crate::{ types::EFTest, utils::load_initial_state, }; -use ethrex_core::{types::TxKind, Address}; -use ethrex_levm::errors::{TransactionReport, TxResult}; -use ethrex_storage::{error::StoreError, AccountUpdate}; -use ethrex_vm::{db::StoreWrapper, spec_id, EvmState, RevmAddress, RevmU256}; +use ethereum_rust_core::{types::TxKind, Address}; +use ethereum_rust_levm::errors::{TransactionReport, TxResult}; +use ethereum_rust_storage::{error::StoreError, AccountUpdate}; +use ethereum_rust_vm::{db::StoreWrapper, spec_id, EvmState, RevmAddress, RevmU256}; use revm::{ db::State, inspectors::TracerEip3155 as RevmTracerEip3155, @@ -230,7 +230,7 @@ pub fn ensure_post_state( // We only want to compare account updates when no exception is expected. None => { let levm_account_updates = levm_runner::get_state_transitions(levm_execution_report); - let revm_account_updates = ethrex_vm::get_state_transitions(revm_state); + let revm_account_updates = ethereum_rust_vm::get_state_transitions(revm_state); let account_updates_report = compare_levm_revm_account_updates(&levm_account_updates, &revm_account_updates); re_run_report.register_account_updates_report(*vector, account_updates_report); diff --git a/cmd/ef_tests/levm/types.rs b/cmd/ef_tests/levm/types.rs index be399ca5e..ebca565db 100644 --- a/cmd/ef_tests/levm/types.rs +++ b/cmd/ef_tests/levm/types.rs @@ -7,11 +7,11 @@ use crate::{ report::TestVector, }; use bytes::Bytes; -use ethrex_core::{ +use ethereum_rust_core::{ types::{Genesis, GenesisAccount, TxKind}, Address, H256, U256, }; -use ethrex_vm::SpecId; +use ethereum_rust_vm::SpecId; use serde::Deserialize; use std::collections::HashMap; diff --git a/cmd/ef_tests/levm/utils.rs b/cmd/ef_tests/levm/utils.rs index b09944b93..3e4dbe329 100644 --- a/cmd/ef_tests/levm/utils.rs +++ b/cmd/ef_tests/levm/utils.rs @@ -1,7 +1,7 @@ use crate::types::EFTest; -use ethrex_core::{types::Genesis, H256}; -use ethrex_storage::{EngineType, Store}; -use ethrex_vm::{evm_state, EvmState}; +use ethereum_rust_core::{types::Genesis, H256}; +use ethereum_rust_storage::{EngineType, Store}; +use ethereum_rust_vm::{evm_state, EvmState}; pub fn load_initial_state(test: &EFTest) -> (EvmState, H256) { let genesis = Genesis::from(test); diff --git a/cmd/ethereum_rust/Cargo.toml b/cmd/ethereum_rust/Cargo.toml new file mode 100644 index 000000000..69c87285e --- /dev/null +++ b/cmd/ethereum_rust/Cargo.toml @@ -0,0 +1,44 @@ +[package] +name = "ethereum_rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +ethereum_rust-blockchain.workspace = true +ethereum_rust-rpc.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-net.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-vm.workspace = true +ethereum_rust-rlp.workspace = true +ethereum_rust-l2.workspace = true + +bytes.workspace = true +hex.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true +clap = { version = "4.5.4", features = ["cargo"] } +directories = "5.0.1" +serde_json.workspace = true +tokio = { version = "1.38.0", features = ["full"] } +anyhow = "1.0.86" +rand = "0.8.5" +k256 = { version = "0.13.3", features = ["ecdh"] } +local-ip-address = "0.6" +tokio-util.workspace = true + +cfg-if = "1.0.0" + +ethereum_rust-dev = { path = "../../crates/blockchain/dev", optional = true } + +[[bin]] +name = "ethereum_rust" +path = "./ethereum_rust.rs" + +[features] +default = [] +dev = ["dep:ethereum_rust-dev"] +l2 = ["ethereum_rust-vm/l2"] +levm = ["ethereum_rust-vm/levm", "ethereum_rust-blockchain/levm"] diff --git a/cmd/ethrex/cli.rs b/cmd/ethereum_rust/cli.rs similarity index 92% rename from cmd/ethrex/cli.rs rename to cmd/ethereum_rust/cli.rs index b85e48548..727de4f38 100644 --- a/cmd/ethrex/cli.rs +++ b/cmd/ethereum_rust/cli.rs @@ -1,10 +1,10 @@ use clap::{Arg, ArgAction, Command}; -use ethrex_net::bootnode::BootNode; +use ethereum_rust_net::bootnode::BootNode; use tracing::Level; pub fn cli() -> Command { - Command::new("ethrex") - .about("ethrex Execution client") + Command::new("ethereum_rust") + .about("Ethereum Rust Execution client") .author("Lambdaclass") .arg( Arg::new("http.addr") @@ -104,12 +104,6 @@ pub fn cli() -> Command { .required(false) .value_name("CHAIN_RLP_PATH"), ) - .arg( - Arg::new("import_dir") - .long("import_dir") - .required(false) - .value_name("BLOCKS_DIR_PATH"), - ) .subcommand( Command::new("removedb").about("Remove the database").arg( Arg::new("datadir") diff --git a/cmd/ethrex/decode.rs b/cmd/ethereum_rust/decode.rs similarity index 94% rename from cmd/ethrex/decode.rs rename to cmd/ethereum_rust/decode.rs index 01bb32647..a3a01a48d 100644 --- a/cmd/ethrex/decode.rs +++ b/cmd/ethereum_rust/decode.rs @@ -1,7 +1,7 @@ use anyhow::Error; use bytes::Bytes; -use ethrex_core::types::{Block, Genesis}; -use ethrex_rlp::decode::RLPDecode as _; +use ethereum_rust_core::types::{Block, Genesis}; +use ethereum_rust_rlp::decode::RLPDecode as _; use std::{ fs::File, io::{BufReader, Read as _}, @@ -38,7 +38,7 @@ pub fn genesis_file(file: File) -> Result { #[cfg(test)] mod tests { use crate::decode::chain_file; - use ethrex_core::H256; + use ethereum_rust_core::H256; use std::{fs::File, str::FromStr as _}; #[test] diff --git a/cmd/ethrex/ethrex.rs b/cmd/ethereum_rust/ethereum_rust.rs similarity index 72% rename from cmd/ethrex/ethrex.rs rename to cmd/ethereum_rust/ethereum_rust.rs index 0d905fc8e..67e208dba 100644 --- a/cmd/ethrex/ethrex.rs +++ b/cmd/ethereum_rust/ethereum_rust.rs @@ -1,18 +1,15 @@ use bytes::Bytes; use directories::ProjectDirs; - -use ethrex_blockchain::add_block; -use ethrex_blockchain::fork_choice::apply_fork_choice; -use ethrex_core::types::{Block, Genesis}; -use ethrex_core::H256; -use ethrex_net::bootnode::BootNode; -use ethrex_net::node_id_from_signing_key; -use ethrex_net::types::Node; -use ethrex_rlp::decode::RLPDecode; -use ethrex_storage::{EngineType, Store}; +use ethereum_rust_blockchain::add_block; +use ethereum_rust_blockchain::fork_choice::apply_fork_choice; +use ethereum_rust_core::types::{Block, Genesis}; +use ethereum_rust_core::H256; +use ethereum_rust_net::bootnode::BootNode; +use ethereum_rust_net::node_id_from_signing_key; +use ethereum_rust_net::types::Node; +use ethereum_rust_storage::{EngineType, Store}; use k256::ecdsa::SigningKey; use local_ip_address::local_ip; -use std::fs; use std::future::IntoFuture; use std::path::Path; use std::str::FromStr as _; @@ -29,7 +26,7 @@ use tracing_subscriber::{EnvFilter, FmtSubscriber}; mod cli; mod decode; -const DEFAULT_DATADIR: &str = "ethrex"; +const DEFAULT_DATADIR: &str = "ethereum_rust"; #[tokio::main] async fn main() { let matches = cli::cli().get_matches(); @@ -125,34 +122,46 @@ async fn main() { .expect("Failed to create genesis block"); if let Some(chain_rlp_path) = matches.get_one::("import") { - info!("Importing blocks from chain file: {}", chain_rlp_path); let blocks = read_chain_file(chain_rlp_path); - import_blocks(&store, &blocks); - } - - if let Some(blocks_path) = matches.get_one::("import_dir") { - info!( - "Importing blocks from individual block files in directory: {}", - blocks_path - ); - let mut blocks = vec![]; - let dir_reader = fs::read_dir(blocks_path).expect("Failed to read blocks directory"); - for file_res in dir_reader { - let file = file_res.expect("Failed to open file in directory"); - let path = file.path(); - let s = path - .to_str() - .expect("Path could not be converted into string"); - blocks.push(read_block_file(s)); + let size = blocks.len(); + for block in &blocks { + let hash = block.hash(); + info!( + "Adding block {} with hash {:#x}.", + block.header.number, hash + ); + let result = add_block(block, &store); + if let Some(error) = result.err() { + warn!( + "Failed to add block {} with hash {:#x}: {}.", + block.header.number, hash, error + ); + } + if store + .update_latest_block_number(block.header.number) + .is_err() + { + error!("Fatal: added block {} but could not update the block number -- aborting block import", block.header.number); + break; + }; + if store + .set_canonical_block(block.header.number, hash) + .is_err() + { + error!("Fatal: added block {} but could not set it as canonical -- aborting block import", block.header.number); + break; + }; } - - import_blocks(&store, &blocks); + if let Some(last_block) = blocks.last() { + let hash = last_block.hash(); + apply_fork_choice(&store, hash, hash, hash).unwrap(); + } + info!("Added {} blocks to blockchain", size); } - let jwt_secret = read_jwtsecret_file(authrpc_jwtsecret); // TODO Learn how should the key be created - // https://github.com/lambdaclass/lambda_ethrex/issues/836 + // https://github.com/lambdaclass/lambda_ethereum_rust/issues/836 //let signer = SigningKey::random(&mut OsRng); let key_bytes = H256::from_str("577d8278cc7748fad214b5378669b420f8221afb45ce930b7f22da49cbc545f3").unwrap(); @@ -176,7 +185,7 @@ async fn main() { // TODO: Check every module starts properly. let tracker = TaskTracker::new(); - let rpc_api = ethrex_rpc::start_api( + let rpc_api = ethereum_rust_rpc::start_api( http_socket_addr, authrpc_socket_addr, store.clone(), @@ -186,7 +195,7 @@ async fn main() { .into_future(); // TODO Find a proper place to show node information - // https://github.com/lambdaclass/lambda_ethrex/issues/836 + // https://github.com/lambdaclass/lambda_ethereum_rust/issues/836 let enode = local_p2p_node.enode_url(); info!("Node: {enode}"); @@ -195,10 +204,10 @@ async fn main() { // We do not want to start the networking module if the l2 feature is enabled. cfg_if::cfg_if! { if #[cfg(feature = "l2")] { - let l2_proposer = ethrex_l2::start_proposer(store).into_future(); + let l2_proposer = ethereum_rust_l2::start_proposer(store).into_future(); tracker.spawn(l2_proposer); } else if #[cfg(feature = "dev")] { - use ethrex_dev; + use ethereum_rust_dev; let authrpc_jwtsecret = std::fs::read(authrpc_jwtsecret).expect("Failed to read JWT secret"); let head_block_hash = { @@ -207,10 +216,10 @@ async fn main() { }; let max_tries = 3; let url = format!("http://{authrpc_socket_addr}"); - let block_producer_engine = ethrex_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethrex_core::Address::default()); + let block_producer_engine = ethereum_rust_dev::block_producer::start_block_producer(url, authrpc_jwtsecret.into(), head_block_hash, max_tries, 1000, ethereum_rust_core::Address::default()); tracker.spawn(block_producer_engine); } else { - let networking = ethrex_net::start_network( + let networking = ethereum_rust_net::start_network( udp_socket_addr, tcp_socket_addr, bootnodes, @@ -259,13 +268,6 @@ fn read_chain_file(chain_rlp_path: &str) -> Vec { decode::chain_file(chain_file).expect("Failed to decode chain rlp file") } -fn read_block_file(block_file_path: &str) -> Block { - let encoded_block = std::fs::read(block_file_path) - .unwrap_or_else(|_| panic!("Failed to read block file with path {}", block_file_path)); - Block::decode(&encoded_block) - .unwrap_or_else(|_| panic!("Failed to decode block file {}", block_file_path)) -} - fn read_genesis_file(genesis_file_path: &str) -> Genesis { let genesis_file = std::fs::File::open(genesis_file_path).expect("Failed to open genesis file"); decode::genesis_file(genesis_file).expect("Failed to decode genesis file") @@ -290,43 +292,3 @@ fn set_datadir(datadir: &str) -> String { .expect("invalid data directory") .to_owned() } - -fn import_blocks(store: &Store, blocks: &Vec) { - let size = blocks.len(); - for block in blocks { - let hash = block.hash(); - info!( - "Adding block {} with hash {:#x}.", - block.header.number, hash - ); - let result = add_block(block, store); - if let Some(error) = result.err() { - warn!( - "Failed to add block {} with hash {:#x}: {}.", - block.header.number, hash, error - ); - } - if store - .update_latest_block_number(block.header.number) - .is_err() - { - error!("Fatal: added block {} but could not update the block number -- aborting block import", block.header.number); - break; - }; - if store - .set_canonical_block(block.header.number, hash) - .is_err() - { - error!( - "Fatal: added block {} but could not set it as canonical -- aborting block import", - block.header.number - ); - break; - }; - } - if let Some(last_block) = blocks.last() { - let hash = last_block.hash(); - apply_fork_choice(store, hash, hash, hash).unwrap(); - } - info!("Added {} blocks to blockchain", size); -} diff --git a/cmd/ethrex_l2/Cargo.toml b/cmd/ethereum_rust_l2/Cargo.toml similarity index 71% rename from cmd/ethrex_l2/Cargo.toml rename to cmd/ethereum_rust_l2/Cargo.toml index 5b969aa03..0cbcd346e 100644 --- a/cmd/ethrex_l2/Cargo.toml +++ b/cmd/ethereum_rust_l2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex_l2_cli" +name = "ethereum_rust_l2_cli" version.workspace = true edition.workspace = true @@ -26,12 +26,12 @@ strum = "0.26.3" secp256k1.workspace = true keccak-hash = "0.10.0" -ethrex-l2.workspace = true -ethrex-core.workspace = true -ethrex-blockchain.workspace = true -ethrex-prover.workspace = true -ethrex-rlp.workspace = true -ethrex-rpc.workspace = true +ethereum_rust-l2.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-blockchain.workspace = true +ethereum_rust-prover.workspace = true +ethereum_rust-rlp.workspace = true +ethereum_rust-rpc.workspace = true [[bin]] name = "l2" diff --git a/cmd/ethrex_l2/README.md b/cmd/ethereum_rust_l2/README.md similarity index 74% rename from cmd/ethrex_l2/README.md rename to cmd/ethereum_rust_l2/README.md index 7441580c1..6aefbbb92 100644 --- a/cmd/ethrex_l2/README.md +++ b/cmd/ethereum_rust_l2/README.md @@ -1,28 +1,27 @@ -# ethrex L2 CLI +# Ethereum Rust L2 CLI ## Table of Contents -- [ethrex L2 CLI](#ethrex-l2-cli) - - [Table of Contents](#table-of-contents) - - [How to install](#how-to-install) - - [Commands](#commands) +- [Introduction](#introduction) +- [How to install](#how-to-install) +- [Commands](#commands) + - [`config`](#config) + - [`stack`](#stack) + - [`wallet`](#wallet) + - [`autocomplete`](#autocomplete) +- [Examples](#examples) - [`config`](#config) + - [Adding a configuration](#adding-a-configuration) + - [Editing exiting configuration interactively](#editing-exiting-configuration-interactively) + - [Deleting existing configuration interactively](#deleting-existing-configuration-interactively) + - [Setting a configuration interactively](#setting-a-configuration-interactively) - [`stack`](#stack) - - [`wallet`](#wallet) - - [`autocomplete`](#autocomplete) - - [Examples](#examples) - - [`config`](#config-1) - - [Adding a configuration](#adding-a-configuration) - - [Editing exiting configuration interactively](#editing-exiting-configuration-interactively) - - [Deleting existing configuration interactively](#deleting-existing-configuration-interactively) - - [Setting a configuration interactively](#setting-a-configuration-interactively) - - [`stack`](#stack-1) - - [Initializing the stack](#initializing-the-stack) - - [Restarting the stack](#restarting-the-stack) + - [Initializing the stack](#initializing-the-stack) + - [Restarting the stack](#restarting-the-stack) ## How to install -Running the command below will install the `ethrex_l2` binary in your system. +Running the command below will install the `ethereum_rust_l2` binary in your system. ``` cargo install --path . @@ -31,7 +30,7 @@ cargo install --path . ## Commands ``` -Usage: ethrex_l2 +Usage: ethereum_rust_l2 Commands: stack Stack related commands. @@ -45,7 +44,7 @@ Options: -V, --version Print version ``` -> [!IMPORTANT] +> [!IMPORTANT] > Most of the CLI interaction needs a configuration to be set. You can set a configuration with the `config` command. ### `config` @@ -53,7 +52,7 @@ Options: ``` CLI config commands. -Usage: ethrex_l2 config +Usage: ethereum_rust_l2 config Commands: edit Edit an existing config. @@ -73,7 +72,7 @@ Options: ``` Stack related commands. -Usage: ethrex_l2 stack +Usage: ethereum_rust_l2 stack Commands: init Initializes the L2 network in the provided L1. [aliases: i] @@ -95,7 +94,7 @@ Options: ``` Wallet interaction commands. The configured wallet could operate both with the L1 and L2 networks. -Usage: ethrex_l2 wallet +Usage: ethereum_rust_l2 wallet Commands: balance Get the balance of the wallet. @@ -116,7 +115,7 @@ Options: ``` Generate shell completion scripts. -Usage: ethrex_l2 autocomplete +Usage: ethereum_rust_l2 autocomplete Commands: generate Generate autocomplete shell script. diff --git a/cmd/ethrex_l2/assets/config_create.cast.gif b/cmd/ethereum_rust_l2/assets/config_create.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/config_create.cast.gif rename to cmd/ethereum_rust_l2/assets/config_create.cast.gif diff --git a/cmd/ethrex_l2/assets/config_delete.cast.gif b/cmd/ethereum_rust_l2/assets/config_delete.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/config_delete.cast.gif rename to cmd/ethereum_rust_l2/assets/config_delete.cast.gif diff --git a/cmd/ethrex_l2/assets/config_edit.cast.gif b/cmd/ethereum_rust_l2/assets/config_edit.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/config_edit.cast.gif rename to cmd/ethereum_rust_l2/assets/config_edit.cast.gif diff --git a/cmd/ethrex_l2/assets/config_set.cast.gif b/cmd/ethereum_rust_l2/assets/config_set.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/config_set.cast.gif rename to cmd/ethereum_rust_l2/assets/config_set.cast.gif diff --git a/cmd/ethrex_l2/assets/stack_init.cast.gif b/cmd/ethereum_rust_l2/assets/stack_init.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/stack_init.cast.gif rename to cmd/ethereum_rust_l2/assets/stack_init.cast.gif diff --git a/cmd/ethrex_l2/assets/stack_restart.cast.gif b/cmd/ethereum_rust_l2/assets/stack_restart.cast.gif similarity index 100% rename from cmd/ethrex_l2/assets/stack_restart.cast.gif rename to cmd/ethereum_rust_l2/assets/stack_restart.cast.gif diff --git a/cmd/ethrex_l2/src/cli.rs b/cmd/ethereum_rust_l2/src/cli.rs similarity index 62% rename from cmd/ethrex_l2/src/cli.rs rename to cmd/ethereum_rust_l2/src/cli.rs index e4d7570a7..6933a1dfc 100644 --- a/cmd/ethrex_l2/src/cli.rs +++ b/cmd/ethereum_rust_l2/src/cli.rs @@ -7,14 +7,14 @@ use clap::{Parser, Subcommand}; pub const VERSION_STRING: &str = env!("CARGO_PKG_VERSION"); #[derive(Parser)] -#[command(name="Ethrex_l2_cli", author, version=VERSION_STRING, about, long_about = None)] -pub struct EthrexL2CLI { +#[command(name="ethereum_rust_l2_cli", author, version=VERSION_STRING, about, long_about = None)] +pub struct EthereumRustL2CLI { #[command(subcommand)] - command: EthrexL2Command, + command: EthereumRustL2Command, } #[derive(Subcommand)] -enum EthrexL2Command { +enum EthereumRustL2Command { #[clap(subcommand, about = "Stack related commands.")] Stack(stack::Command), #[clap( @@ -38,18 +38,18 @@ enum EthrexL2Command { } pub async fn start() -> eyre::Result<()> { - let EthrexL2CLI { command } = EthrexL2CLI::parse(); - if let EthrexL2Command::Config(cmd) = command { + let EthereumRustL2CLI { command } = EthereumRustL2CLI::parse(); + if let EthereumRustL2Command::Config(cmd) = command { return cmd.run().await; } let cfg = load_selected_config().await?; match command { - EthrexL2Command::Stack(cmd) => cmd.run(cfg).await?, - EthrexL2Command::Wallet(cmd) => cmd.run(cfg).await?, - EthrexL2Command::Utils(cmd) => cmd.run().await?, - EthrexL2Command::Autocomplete(cmd) => cmd.run()?, - EthrexL2Command::Config(_) => unreachable!(), - EthrexL2Command::Test(cmd) => cmd.run(cfg).await?, + EthereumRustL2Command::Stack(cmd) => cmd.run(cfg).await?, + EthereumRustL2Command::Wallet(cmd) => cmd.run(cfg).await?, + EthereumRustL2Command::Utils(cmd) => cmd.run().await?, + EthereumRustL2Command::Autocomplete(cmd) => cmd.run()?, + EthereumRustL2Command::Config(_) => unreachable!(), + EthereumRustL2Command::Test(cmd) => cmd.run(cfg).await?, }; Ok(()) } diff --git a/cmd/ethrex_l2/src/commands/autocomplete.rs b/cmd/ethereum_rust_l2/src/commands/autocomplete.rs similarity index 85% rename from cmd/ethrex_l2/src/commands/autocomplete.rs rename to cmd/ethereum_rust_l2/src/commands/autocomplete.rs index c98fbc1c7..a227d75f7 100644 --- a/cmd/ethrex_l2/src/commands/autocomplete.rs +++ b/cmd/ethereum_rust_l2/src/commands/autocomplete.rs @@ -1,4 +1,4 @@ -use crate::cli::EthrexL2CLI; +use crate::cli::EthereumRustL2CLI; use clap::{CommandFactory, Subcommand, ValueEnum}; use clap_complete::{aot::Shell, generate}; use std::fs::{File, OpenOptions}; @@ -58,8 +58,8 @@ fn generate_bash_script(shell_arg: Option) -> eyre::Result<()> { let shell = get_shell(shell_arg)?; generate( shell, - &mut EthrexL2CLI::command(), - "ethrex_l2", + &mut EthereumRustL2CLI::command(), + "ethereum_rust_l2", &mut io::stdout(), ); Ok(()) @@ -67,9 +67,9 @@ fn generate_bash_script(shell_arg: Option) -> eyre::Result<()> { fn shellrc_command_exists(shellrc_path: &std::path::Path, shell: Shell) -> eyre::Result { let expected_string = if shell == Shell::Elvish { - "-source $HOME/.ethrex-l2-completion" + "-source $HOME/.ethereum-rust-l2-completion" } else { - ". $HOME/.ethrex-l2-completion" + ". $HOME/.ethereum-rust-l2-completion" }; let file = File::open(shellrc_path)?; @@ -90,9 +90,14 @@ fn install_bash_script(shell_arg: Option) -> eyre::Result<()> { let file_path = dirs::home_dir() .ok_or(eyre::eyre!("Cannot find home directory."))? - .join(".ethrex-l2-completion"); + .join(".ethereum-rust-l2-completion"); let mut file = File::create(file_path)?; - generate(shell, &mut EthrexL2CLI::command(), "ethrex_l2", &mut file); + generate( + shell, + &mut EthereumRustL2CLI::command(), + "ethereum_rust_l2", + &mut file, + ); file.flush()?; let shellrc_path = dirs::home_dir() @@ -102,9 +107,9 @@ fn install_bash_script(shell_arg: Option) -> eyre::Result<()> { if !shellrc_command_exists(&shellrc_path, shell)? { let mut file = OpenOptions::new().append(true).open(shellrc_path)?; if shell == Shell::Elvish { - file.write_all(b"\n-source $HOME/.ethrex-l2-completion\n")?; + file.write_all(b"\n-source $HOME/.ethereum-rust-l2-completion\n")?; } else { - file.write_all(b"\n. $HOME/.ethrex-l2-completion\n")?; + file.write_all(b"\n. $HOME/.ethereum-rust-l2-completion\n")?; } file.flush()?; } diff --git a/cmd/ethrex_l2/src/commands/config.rs b/cmd/ethereum_rust_l2/src/commands/config.rs similarity index 100% rename from cmd/ethrex_l2/src/commands/config.rs rename to cmd/ethereum_rust_l2/src/commands/config.rs diff --git a/cmd/ethrex_l2/src/commands/mod.rs b/cmd/ethereum_rust_l2/src/commands/mod.rs similarity index 100% rename from cmd/ethrex_l2/src/commands/mod.rs rename to cmd/ethereum_rust_l2/src/commands/mod.rs diff --git a/cmd/ethrex_l2/src/commands/stack.rs b/cmd/ethereum_rust_l2/src/commands/stack.rs similarity index 89% rename from cmd/ethrex_l2/src/commands/stack.rs rename to cmd/ethereum_rust_l2/src/commands/stack.rs index 567708e02..13285c474 100644 --- a/cmd/ethrex_l2/src/commands/stack.rs +++ b/cmd/ethereum_rust_l2/src/commands/stack.rs @@ -1,4 +1,4 @@ -use crate::{config::EthrexL2Config, utils::config::confirm}; +use crate::{config::EthereumRustL2Config, utils::config::confirm}; use clap::Subcommand; use eyre::ContextCompat; use secp256k1::SecretKey; @@ -68,13 +68,13 @@ pub(crate) enum Command { } impl Command { - pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { let root = std::path::Path::new(CARGO_MANIFEST_DIR) .parent() .map(std::path::Path::parent) .context("Failed to get parent")? .context("Failed to get grandparent")?; - let ethrex_dev_path = root.join("crates/blockchain/dev"); + let ethereum_rust_dev_path = root.join("crates/blockchain/dev"); let l2_crate_path = root.join("crates/l2"); let contracts_path = l2_crate_path.join("contracts"); @@ -90,7 +90,7 @@ impl Command { // or in a testnet. If the L1 RPC URL is localhost, then it is // a local environment and the local node needs to be started. if l1_rpc_url.contains("localhost") { - start_l1(&l2_crate_path, ðrex_dev_path).await?; + start_l1(&l2_crate_path, ðereum_rust_dev_path).await?; tokio::time::sleep(std::time::Duration::from_secs(1)).await; } if !skip_l1_deployment { @@ -101,7 +101,7 @@ impl Command { Command::Shutdown { l1, l2, force } => { if force || (l1 && confirm("Are you sure you want to shutdown the local L1 node?")?) { - shutdown_l1(ðrex_dev_path)?; + shutdown_l1(ðereum_rust_dev_path)?; } if force || (l2 && confirm("Are you sure you want to shutdown the L2 node?")?) { shutdown_l2()?; @@ -114,7 +114,7 @@ impl Command { start_prover, } => { if force || l1 { - start_l1(&l2_crate_path, ðrex_dev_path).await?; + start_l1(&l2_crate_path, ðereum_rust_dev_path).await?; } if force || l2 { start_l2(root.to_path_buf(), &l2_rpc_url, start_prover).await?; @@ -200,14 +200,14 @@ fn deploy_l1( Ok(()) } -fn shutdown_l1(ethrex_dev_path: &Path) -> eyre::Result<()> { - let local_l1_docker_compose_path = ethrex_dev_path.join("docker-compose-dev.yaml"); +fn shutdown_l1(ethereum_rust_dev_path: &Path) -> eyre::Result<()> { + let local_l1_docker_compose_path = ethereum_rust_dev_path.join("docker-compose-dev.yaml"); let cmd = std::process::Command::new("docker") .arg("compose") .arg("-f") .arg(local_l1_docker_compose_path) .arg("down") - .current_dir(ethrex_dev_path) + .current_dir(ethereum_rust_dev_path) .spawn()? .wait()?; if !cmd.success() { @@ -219,15 +219,15 @@ fn shutdown_l1(ethrex_dev_path: &Path) -> eyre::Result<()> { fn shutdown_l2() -> eyre::Result<()> { std::process::Command::new("pkill") .arg("-f") - .arg("ethrex") + .arg("ethereum_rust") .spawn()? .wait()?; Ok(()) } -async fn start_l1(l2_crate_path: &Path, ethrex_dev_path: &Path) -> eyre::Result<()> { +async fn start_l1(l2_crate_path: &Path, ethereum_rust_dev_path: &Path) -> eyre::Result<()> { create_volumes(l2_crate_path)?; - docker_compose_l2_up(ethrex_dev_path)?; + docker_compose_l2_up(ethereum_rust_dev_path)?; Ok(()) } @@ -237,15 +237,15 @@ fn create_volumes(l2_crate_path: &Path) -> eyre::Result<()> { Ok(()) } -fn docker_compose_l2_up(ethrex_dev_path: &Path) -> eyre::Result<()> { - let local_l1_docker_compose_path = ethrex_dev_path.join("docker-compose-dev.yaml"); +fn docker_compose_l2_up(ethereum_rust_dev_path: &Path) -> eyre::Result<()> { + let local_l1_docker_compose_path = ethereum_rust_dev_path.join("docker-compose-dev.yaml"); let cmd = std::process::Command::new("docker") .arg("compose") .arg("-f") .arg(local_l1_docker_compose_path) .arg("up") .arg("-d") - .current_dir(ethrex_dev_path) + .current_dir(ethereum_rust_dev_path) .spawn()? .wait()?; if !cmd.success() { @@ -264,7 +264,7 @@ async fn start_l2(root: PathBuf, l2_rpc_url: &str, start_prover: bool) -> eyre:: .arg("run") .arg("--release") .arg("--bin") - .arg("ethrex") + .arg("ethereum_rust") .arg("--features") .arg("l2") .arg("--") @@ -293,7 +293,7 @@ async fn start_l2(root: PathBuf, l2_rpc_url: &str, start_prover: bool) -> eyre:: .arg("--features") .arg("build_zkvm") .arg("--bin") - .arg("ethrex_prover") + .arg("ethereum_rust_prover") .current_dir(root_clone) .status(); diff --git a/cmd/ethrex_l2/src/commands/test.rs b/cmd/ethereum_rust_l2/src/commands/test.rs similarity index 93% rename from cmd/ethrex_l2/src/commands/test.rs rename to cmd/ethereum_rust_l2/src/commands/test.rs index ae8e90ef0..3e78b1631 100644 --- a/cmd/ethrex_l2/src/commands/test.rs +++ b/cmd/ethereum_rust_l2/src/commands/test.rs @@ -1,9 +1,9 @@ -use crate::config::EthrexL2Config; +use crate::config::EthereumRustL2Config; use bytes::Bytes; use clap::Subcommand; +use ethereum_rust_blockchain::constants::TX_GAS_COST; +use ethereum_rust_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use ethereum_types::{Address, H160, H256, U256}; -use ethrex_blockchain::constants::TX_GAS_COST; -use ethrex_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use keccak_hash::keccak; use secp256k1::SecretKey; use std::{ @@ -67,7 +67,7 @@ async fn transfer_from( value: U256, iterations: u64, verbose: bool, - cfg: EthrexL2Config, + cfg: EthereumRustL2Config, ) -> u64 { let client = EthClient::new(&cfg.network.l2_rpc_url); let private_key = SecretKey::from_slice(pk.parse::().unwrap().as_bytes()).unwrap(); @@ -117,7 +117,7 @@ async fn transfer_from( } impl Command { - pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { match self { Command::Load { path, diff --git a/cmd/ethrex_l2/src/commands/utils.rs b/cmd/ethereum_rust_l2/src/commands/utils.rs similarity index 100% rename from cmd/ethrex_l2/src/commands/utils.rs rename to cmd/ethereum_rust_l2/src/commands/utils.rs diff --git a/cmd/ethrex_l2/src/commands/wallet.rs b/cmd/ethereum_rust_l2/src/commands/wallet.rs similarity index 98% rename from cmd/ethrex_l2/src/commands/wallet.rs rename to cmd/ethereum_rust_l2/src/commands/wallet.rs index fdd6f0c6d..d9de3f43e 100644 --- a/cmd/ethrex_l2/src/commands/wallet.rs +++ b/cmd/ethereum_rust_l2/src/commands/wallet.rs @@ -1,13 +1,13 @@ -use crate::{commands::utils::encode_calldata, config::EthrexL2Config}; +use crate::{commands::utils::encode_calldata, config::EthereumRustL2Config}; use bytes::Bytes; use clap::Subcommand; -use ethereum_types::{Address, H256, U256}; -use ethrex_core::types::{PrivilegedTxType, Transaction}; -use ethrex_l2::utils::{ +use ethereum_rust_core::types::{PrivilegedTxType, Transaction}; +use ethereum_rust_l2::utils::{ eth_client::{eth_sender::Overrides, EthClient}, merkle_tree::merkle_proof, }; -use ethrex_rpc::types::block::BlockBodyWrapper; +use ethereum_rust_rpc::types::block::BlockBodyWrapper; +use ethereum_types::{Address, H256, U256}; use eyre::OptionExt; use hex::FromHexError; use itertools::Itertools; @@ -251,7 +251,7 @@ async fn get_withdraw_merkle_proof( } impl Command { - pub async fn run(self, cfg: EthrexL2Config) -> eyre::Result<()> { + pub async fn run(self, cfg: EthereumRustL2Config) -> eyre::Result<()> { let eth_client = EthClient::new(&cfg.network.l1_rpc_url); let rollup_client = EthClient::new(&cfg.network.l2_rpc_url); let from = cfg.wallet.address; diff --git a/cmd/ethrex_l2/src/config.rs b/cmd/ethereum_rust_l2/src/config.rs similarity index 87% rename from cmd/ethrex_l2/src/config.rs rename to cmd/ethereum_rust_l2/src/config.rs index c568c2877..523febdcf 100644 --- a/cmd/ethrex_l2/src/config.rs +++ b/cmd/ethereum_rust_l2/src/config.rs @@ -11,7 +11,7 @@ use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone)] -pub struct EthrexL2Config { +pub struct EthereumRustL2Config { pub network: NetworkConfig, pub wallet: WalletConfig, pub contracts: ContractsConfig, @@ -31,8 +31,8 @@ pub struct NetworkConfig { pub struct WalletConfig { pub address: Address, #[serde( - serialize_with = "ethrex_l2::utils::secret_key_serializer", - deserialize_with = "ethrex_l2::utils::secret_key_deserializer" + serialize_with = "ethereum_rust_l2::utils::secret_key_serializer", + deserialize_with = "ethereum_rust_l2::utils::secret_key_deserializer" )] pub private_key: SecretKey, } @@ -42,7 +42,7 @@ pub struct ContractsConfig { pub common_bridge: Address, } -pub async fn try_load_selected_config() -> eyre::Result> { +pub async fn try_load_selected_config() -> eyre::Result> { let config_path = selected_config_path()?; if !config_path.exists() { return Ok(None); @@ -53,7 +53,7 @@ pub async fn try_load_selected_config() -> eyre::Result> .map(Some) } -pub async fn load_selected_config() -> eyre::Result { +pub async fn load_selected_config() -> eyre::Result { let config_path = selected_config_path()?; if !config_path.exists() { println!("No config set, please select a config to set"); diff --git a/cmd/ethrex_l2/src/lib.rs b/cmd/ethereum_rust_l2/src/lib.rs similarity index 100% rename from cmd/ethrex_l2/src/lib.rs rename to cmd/ethereum_rust_l2/src/lib.rs diff --git a/cmd/ethrex_l2/src/main.rs b/cmd/ethereum_rust_l2/src/main.rs similarity index 90% rename from cmd/ethrex_l2/src/main.rs rename to cmd/ethereum_rust_l2/src/main.rs index bc50c9ae8..c8ea06604 100644 --- a/cmd/ethrex_l2/src/main.rs +++ b/cmd/ethereum_rust_l2/src/main.rs @@ -1,4 +1,4 @@ -use ethrex_l2_cli::cli; +use ethereum_rust_l2_cli::cli; #[tokio::main] async fn main() { diff --git a/cmd/ethrex_l2/src/utils/config/default_values.rs b/cmd/ethereum_rust_l2/src/utils/config/default_values.rs similarity index 100% rename from cmd/ethrex_l2/src/utils/config/default_values.rs rename to cmd/ethereum_rust_l2/src/utils/config/default_values.rs diff --git a/cmd/ethrex_l2/src/utils/config/mod.rs b/cmd/ethereum_rust_l2/src/utils/config/mod.rs similarity index 90% rename from cmd/ethrex_l2/src/utils/config/mod.rs rename to cmd/ethereum_rust_l2/src/utils/config/mod.rs index e02e1ea1b..9177ee225 100644 --- a/cmd/ethrex_l2/src/utils/config/mod.rs +++ b/cmd/ethereum_rust_l2/src/utils/config/mod.rs @@ -1,6 +1,6 @@ use crate::{ commands::{self, config::EditConfigOpts}, - config::{ContractsConfig, EthrexL2Config, NetworkConfig, WalletConfig}, + config::{ContractsConfig, EthereumRustL2Config, NetworkConfig, WalletConfig}, utils::messages::{ ADDRESS_PROMPT_MSG, CONFIG_CREATE_PROMPT_MSG, CONFIG_EDIT_PROMPT_MSG, L1_CHAIN_ID_PROMPT_MSG, L1_EXPLORER_URL_PROMPT_MSG, L1_RPC_URL_PROMPT_MSG, @@ -28,7 +28,7 @@ pub const SELECTED_CONFIG_FILE_NAME: &str = ".selected"; pub fn configs_dir_path() -> eyre::Result { let configs_dir_path = dirs::config_dir() .ok_or_else(|| eyre::eyre!("Could not find user's config directory"))? - .join("Ethrex-l2-cli") + .join("ethereum-rust-l2-cli") .join("configs"); if !configs_dir_path.exists() { std::fs::create_dir_all(&configs_dir_path)?; @@ -95,8 +95,8 @@ pub fn config_path_interactive_selection(prompt: &str) -> eyre::Result config_path(configs.get(selection).context("No config selected")?) } -pub fn prompt_config() -> eyre::Result { - let prompted_config = EthrexL2Config { +pub fn prompt_config() -> eyre::Result { + let prompted_config = EthereumRustL2Config { network: NetworkConfig { l1_rpc_url: prompt(L1_RPC_URL_PROMPT_MSG, DEFAULT_L1_RPC_URL.into())?, l1_chain_id: prompt(L1_CHAIN_ID_PROMPT_MSG, DEFAULT_L1_CHAIN_ID)?, @@ -149,8 +149,11 @@ pub fn selected_config_path() -> eyre::Result { Ok(configs_dir_path()?.join(SELECTED_CONFIG_FILE_NAME)) } -pub fn edit_config_by_name_interactively(config_path: &PathBuf) -> eyre::Result { - let existing_config: EthrexL2Config = toml::from_str(&std::fs::read_to_string(config_path)?)?; +pub fn edit_config_by_name_interactively( + config_path: &PathBuf, +) -> eyre::Result { + let existing_config: EthereumRustL2Config = + toml::from_str(&std::fs::read_to_string(config_path)?)?; let new_config = edit_existing_config_interactively(existing_config)?; Ok(new_config) } @@ -158,15 +161,16 @@ pub fn edit_config_by_name_interactively(config_path: &PathBuf) -> eyre::Result< pub fn edit_config_by_name_with_args( config_path: &PathBuf, opts: EditConfigOpts, -) -> eyre::Result { - let existing_config: EthrexL2Config = toml::from_str(&std::fs::read_to_string(config_path)?)?; +) -> eyre::Result { + let existing_config: EthereumRustL2Config = + toml::from_str(&std::fs::read_to_string(config_path)?)?; let new_config = edit_existing_config_non_interactively(existing_config, opts)?; Ok(new_config) } -pub fn edit_config_interactively() -> eyre::Result<(EthrexL2Config, PathBuf)> { +pub fn edit_config_interactively() -> eyre::Result<(EthereumRustL2Config, PathBuf)> { let config_path = config_path_interactive_selection(CONFIG_EDIT_PROMPT_MSG)?; - let existing_config: EthrexL2Config = + let existing_config: EthereumRustL2Config = toml::from_str(&std::fs::read_to_string(config_path.clone())?)?; let new_config = edit_existing_config_interactively(existing_config)?; Ok((new_config, config_path)) @@ -191,9 +195,9 @@ pub async fn set_new_config(config_path: PathBuf) -> eyre::Result<()> { } pub fn edit_existing_config_interactively( - existing_config: EthrexL2Config, -) -> eyre::Result { - let config = EthrexL2Config { + existing_config: EthereumRustL2Config, +) -> eyre::Result { + let config = EthereumRustL2Config { network: NetworkConfig { l1_rpc_url: prompt(L1_RPC_URL_PROMPT_MSG, existing_config.network.l1_rpc_url)?, l1_chain_id: prompt(L1_CHAIN_ID_PROMPT_MSG, existing_config.network.l1_chain_id)?, @@ -232,10 +236,10 @@ pub fn edit_existing_config_interactively( } pub fn edit_existing_config_non_interactively( - existing_config: EthrexL2Config, + existing_config: EthereumRustL2Config, opts: EditConfigOpts, -) -> eyre::Result { - let config = EthrexL2Config { +) -> eyre::Result { + let config = EthereumRustL2Config { network: NetworkConfig { l1_rpc_url: opts .l1_rpc_url diff --git a/cmd/ethrex_l2/src/utils/messages.rs b/cmd/ethereum_rust_l2/src/utils/messages.rs similarity index 97% rename from cmd/ethrex_l2/src/utils/messages.rs rename to cmd/ethereum_rust_l2/src/utils/messages.rs index ee86c3653..5893a69a0 100644 --- a/cmd/ethrex_l2/src/utils/messages.rs +++ b/cmd/ethereum_rust_l2/src/utils/messages.rs @@ -1,4 +1,4 @@ -// `ethrex_l2 config` messages +// `ethereum_rust_l2 config` messages pub const CONFIG_OVERRIDE_PROMPT_MSG: &str = "Config already exists. Do you want to overwrite it?"; pub const CONFIG_CREATE_PROMPT_MSG: &str = "This config does not exist. Do you want to create it?"; pub const CONFIG_CREATE_NAME_PROMPT_MSG: &str = "What do you want to name the new config?"; diff --git a/cmd/ethrex_l2/src/utils/mod.rs b/cmd/ethereum_rust_l2/src/utils/mod.rs similarity index 100% rename from cmd/ethrex_l2/src/utils/mod.rs rename to cmd/ethereum_rust_l2/src/utils/mod.rs diff --git a/cmd/ethrex/Cargo.toml b/cmd/ethrex/Cargo.toml deleted file mode 100644 index eeec636c1..000000000 --- a/cmd/ethrex/Cargo.toml +++ /dev/null @@ -1,44 +0,0 @@ -[package] -name = "ethrex" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -ethrex-blockchain.workspace = true -ethrex-rpc.workspace = true -ethrex-core.workspace = true -ethrex-net.workspace = true -ethrex-storage.workspace = true -ethrex-vm.workspace = true -ethrex-rlp.workspace = true -ethrex-l2.workspace = true - -bytes.workspace = true -hex.workspace = true -tracing.workspace = true -tracing-subscriber.workspace = true -k256.workspace = true -clap = { version = "4.5.4", features = ["cargo"] } -directories = "5.0.1" -serde_json.workspace = true -tokio = { version = "1.38.0", features = ["full"] } -anyhow = "1.0.86" -rand = "0.8.5" -local-ip-address = "0.6" -tokio-util.workspace = true - -cfg-if = "1.0.0" - -ethrex-dev = { path = "../../crates/blockchain/dev", optional = true } - -[[bin]] -name = "ethrex" -path = "./ethrex.rs" - -[features] -default = [] -dev = ["dep:ethrex-dev"] -l2 = ["ethrex-vm/l2"] -levm = ["ethrex-vm/levm", "ethrex-blockchain/levm"] diff --git a/cmd/hive_report/Cargo.toml b/cmd/hive_report/Cargo.toml deleted file mode 100644 index 30ac15e18..000000000 --- a/cmd/hive_report/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "hive_report" -version.workspace = true -edition.workspace = true - -[dependencies] -serde_json.workspace = true -serde.workspace = true diff --git a/cmd/hive_report/src/main.rs b/cmd/hive_report/src/main.rs deleted file mode 100644 index b367aa597..000000000 --- a/cmd/hive_report/src/main.rs +++ /dev/null @@ -1,69 +0,0 @@ -use serde::Deserialize; -use std::fs::{self, File}; -use std::io::BufReader; - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct TestCase { - summary_result: SummaryResult, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct SummaryResult { - pass: bool, -} - -#[derive(Debug, Deserialize)] -#[serde(rename_all = "camelCase")] -struct JsonFile { - name: String, - test_cases: std::collections::HashMap, -} - -fn main() -> Result<(), Box> { - let mut results = Vec::new(); - - for entry in fs::read_dir("hive/workspace/logs")? { - let entry = entry?; - let path = entry.path(); - - if path.is_file() - && path.extension().and_then(|s| s.to_str()) == Some("json") - && path.file_name().and_then(|s| s.to_str()) != Some("hive.json") - { - let file_name = path - .file_name() - .and_then(|s| s.to_str()) - .expect("Path should be a valid string"); - let file = File::open(&path)?; - let reader = BufReader::new(file); - - let json_data: JsonFile = match serde_json::from_reader(reader) { - Ok(data) => data, - Err(_) => { - eprintln!("Error processing file: {}", file_name); - continue; - } - }; - - let total_tests = json_data.test_cases.len(); - let passed_tests = json_data - .test_cases - .values() - .filter(|test_case| test_case.summary_result.pass) - .count(); - - results.push((json_data.name, passed_tests, total_tests)); - } - } - - // Sort by file name. - results.sort_by(|a, b| a.0.cmp(&b.0)); - - for (file_name, passed, total) in results { - println!("{}: {}/{}", file_name, passed, total); - } - - Ok(()) -} diff --git a/cmd/loc/Cargo.toml b/cmd/loc/Cargo.toml deleted file mode 100644 index 55addc9d9..000000000 --- a/cmd/loc/Cargo.toml +++ /dev/null @@ -1,8 +0,0 @@ -[package] -name = "loc" -version.workspace = true -edition.workspace = true - -[dependencies] -tokei = "12.1.2" -colored = "2.1.0" diff --git a/cmd/loc/src/main.rs b/cmd/loc/src/main.rs deleted file mode 100644 index c2a8eb30f..000000000 --- a/cmd/loc/src/main.rs +++ /dev/null @@ -1,36 +0,0 @@ -use colored::Colorize; -use std::path::PathBuf; -use tokei::{Config, LanguageType, Languages}; - -const CARGO_MANIFEST_DIR: &str = std::env!("CARGO_MANIFEST_DIR"); - -fn main() { - let ethrex = PathBuf::from(CARGO_MANIFEST_DIR).join("../../"); - let levm = PathBuf::from(CARGO_MANIFEST_DIR).join("../../crates/vm"); - let ethrex_l2 = PathBuf::from(CARGO_MANIFEST_DIR).join("../../crates/l2"); - - let config = Config::default(); - - let mut languages = Languages::new(); - languages.get_statistics(&[ethrex.clone()], &[], &config); - let ethrex_loc = &languages.get(&LanguageType::Rust).unwrap(); - - let mut languages = Languages::new(); - languages.get_statistics(&[levm], &[], &config); - let levm_loc = &languages.get(&LanguageType::Rust).unwrap(); - - let mut languages = Languages::new(); - languages.get_statistics(&[ethrex_l2], &[], &config); - let ethrex_l2_loc = &languages.get(&LanguageType::Rust).unwrap(); - - println!("{}", "ethrex loc summary".bold()); - println!("{}", "====================".bold()); - println!( - "{}: {:?}", - "ethrex L1".bold(), - ethrex_loc.code - ethrex_l2_loc.code - levm_loc.code - ); - println!("{}: {:?}", "ethrex L2".bold(), ethrex_l2_loc.code); - println!("{}: {:?}", "levm".bold(), levm_loc.code); - println!("{}: {:?}", "ethrex (total)".bold(), ethrex_loc.code); -} diff --git a/crates/blockchain/Cargo.toml b/crates/blockchain/Cargo.toml index 1c81c4533..952472caa 100644 --- a/crates/blockchain/Cargo.toml +++ b/crates/blockchain/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex-blockchain" +name = "ethereum_rust-blockchain" version = "0.1.0" edition = "2021" @@ -12,10 +12,10 @@ tracing.workspace = true bytes.workspace = true cfg-if = "1.0.0" -ethrex-rlp.workspace = true -ethrex-core = { path = "../common", default-features = false } -ethrex-storage = { path = "../storage/store", default-features = false } -ethrex-vm = { path = "../vm", default-features = false } +ethereum_rust-rlp.workspace = true +ethereum_rust-core = { path = "../common", default-features = false } +ethereum_rust-storage = { path = "../storage/store", default-features = false } +ethereum_rust-vm = { path = "../vm", default-features = false } k256 = { version = "0.13.3", features = ["ecdh"] } @@ -27,11 +27,10 @@ hex = "0.4.3" path = "./blockchain.rs" [features] -default = ["libmdbx", "c-kzg"] +default = ["libmdbx"] libmdbx = [ - "ethrex-core/libmdbx", - "ethrex-storage/default", - "ethrex-vm/libmdbx", + "ethereum_rust-core/libmdbx", + "ethereum_rust-storage/default", + "ethereum_rust-vm/libmdbx", ] -levm = ["ethrex-vm/levm"] -c-kzg =["ethrex-core/c-kzg"] +levm = ["ethereum_rust-vm/levm"] diff --git a/crates/blockchain/blockchain.rs b/crates/blockchain/blockchain.rs index 6b1aab8f6..6f85420a1 100644 --- a/crates/blockchain/blockchain.rs +++ b/crates/blockchain/blockchain.rs @@ -7,15 +7,15 @@ mod smoke_test; use constants::{GAS_PER_BLOB, MAX_BLOB_GAS_PER_BLOCK, MAX_BLOB_NUMBER_PER_BLOCK}; use error::{ChainError, InvalidBlockError}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ validate_block_header, validate_cancun_header_fields, validate_no_cancun_header_fields, Block, BlockHash, BlockHeader, BlockNumber, EIP4844Transaction, Receipt, Transaction, }; -use ethrex_core::H256; +use ethereum_rust_core::H256; -use ethrex_storage::error::StoreError; -use ethrex_storage::Store; -use ethrex_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; +use ethereum_rust_storage::error::StoreError; +use ethereum_rust_storage::Store; +use ethereum_rust_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; //TODO: Implement a struct Chain or BlockChain to encapsulate //functionality and canonical chain state and config @@ -27,7 +27,7 @@ use ethrex_vm::{evm_state, execute_block, spec_id, EvmState, SpecId}; /// Performs pre and post execution validation, and updates the database with the post state. #[cfg(not(feature = "levm"))] pub fn add_block(block: &Block, storage: &Store) -> Result<(), ChainError> { - use ethrex_vm::get_state_transitions; + use ethereum_rust_vm::get_state_transitions; let block_hash = block.header.compute_block_hash(); diff --git a/crates/blockchain/constants.rs b/crates/blockchain/constants.rs index 651c0b038..0b19af442 100644 --- a/crates/blockchain/constants.rs +++ b/crates/blockchain/constants.rs @@ -59,3 +59,5 @@ pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; pub const GAS_LIMIT_BOUND_DIVISOR: u64 = 1024; pub const MIN_GAS_LIMIT: u64 = 5000; + +pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; diff --git a/crates/blockchain/dev/Cargo.toml b/crates/blockchain/dev/Cargo.toml index 919488b9e..a4601cd16 100644 --- a/crates/blockchain/dev/Cargo.toml +++ b/crates/blockchain/dev/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "ethrex-dev" +name = "ethereum_rust-dev" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethrex-rpc.workspace = true +ethereum_rust-rpc.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/crates/blockchain/dev/Dockerfile b/crates/blockchain/dev/Dockerfile index 246c958a3..af091f266 100644 --- a/crates/blockchain/dev/Dockerfile +++ b/crates/blockchain/dev/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethrex +WORKDIR /ethereum_rust FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethrex/recipe.json recipe.json +COPY --from=planner /ethereum_rust/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,6 +27,6 @@ RUN cargo build --release --features dev FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethrex/target/release/ethrex . +COPY --from=builder ethereum_rust/target/release/ethereum_rust . EXPOSE 8545 -ENTRYPOINT [ "./ethrex" ] +ENTRYPOINT [ "./ethereum_rust" ] diff --git a/crates/blockchain/dev/block_producer.rs b/crates/blockchain/dev/block_producer.rs index 7cbe818e7..f9ef46853 100644 --- a/crates/blockchain/dev/block_producer.rs +++ b/crates/blockchain/dev/block_producer.rs @@ -1,7 +1,7 @@ use crate::utils::engine_client::{errors::EngineClientError, EngineClient}; use bytes::Bytes; +use ethereum_rust_rpc::types::fork_choice::{ForkChoiceState, PayloadAttributesV3}; use ethereum_types::{Address, H256}; -use ethrex_rpc::types::fork_choice::{ForkChoiceState, PayloadAttributesV3}; use sha2::{Digest, Sha256}; use std::time::{SystemTime, UNIX_EPOCH}; diff --git a/crates/blockchain/dev/docker-compose-dev.yaml b/crates/blockchain/dev/docker-compose-dev.yaml index b95dce07c..de952a73e 100644 --- a/crates/blockchain/dev/docker-compose-dev.yaml +++ b/crates/blockchain/dev/docker-compose-dev.yaml @@ -1,8 +1,8 @@ services: - ethrex: + ethereum_rust: restart: always - container_name: ethrex_l1 - image: "ethrex_dev" + container_name: ethereum_rust_l1 + image: "ethereum_rust_dev" build: context: ../../../ dockerfile: ./crates/blockchain/dev/Dockerfile diff --git a/crates/blockchain/dev/utils/engine_client/mod.rs b/crates/blockchain/dev/utils/engine_client/mod.rs index 47dd69f55..551bdb286 100644 --- a/crates/blockchain/dev/utils/engine_client/mod.rs +++ b/crates/blockchain/dev/utils/engine_client/mod.rs @@ -3,8 +3,7 @@ use errors::{ EngineClientError, ExchangeCapabilitiesError, ForkChoiceUpdateError, GetPayloadError, NewPayloadError, }; -use ethereum_types::H256; -use ethrex_rpc::{ +use ethereum_rust_rpc::{ engine::{ fork_choice::ForkChoiceUpdatedV3, payload::{GetPayloadV3Request, NewPayloadV3Request}, @@ -16,6 +15,7 @@ use ethrex_rpc::{ }, utils::{RpcErrorResponse, RpcRequest, RpcSuccessResponse}, }; +use ethereum_types::H256; use reqwest::Client; use serde::Deserialize; use serde_json::json; diff --git a/crates/blockchain/error.rs b/crates/blockchain/error.rs index 8a16ab224..050064e25 100644 --- a/crates/blockchain/error.rs +++ b/crates/blockchain/error.rs @@ -1,6 +1,6 @@ -use ethrex_core::types::{BlobsBundleError, InvalidBlockHeaderError}; -use ethrex_storage::error::StoreError; -use ethrex_vm::EvmError; +use ethereum_rust_core::types::InvalidBlockHeaderError; +use ethereum_rust_storage::error::StoreError; +use ethereum_rust_vm::EvmError; #[derive(Debug, thiserror::Error)] pub enum ChainError { @@ -42,8 +42,6 @@ pub enum MempoolError { NoBlockHeaderError, #[error("DB error: {0}")] StoreError(#[from] StoreError), - #[error("BlobsBundle error: {0}")] - BlobsBundleError(#[from] BlobsBundleError), #[error("Transaction max init code size exceeded")] TxMaxInitCodeSizeError, #[error("Transaction gas limit exceeded")] @@ -58,6 +56,8 @@ pub enum MempoolError { TxBlobBaseFeeTooLowError, #[error("Blob transaction submited without blobs bundle")] BlobTxNoBlobsBundle, + #[error("Mismatch between blob versioned hashes and blobs bundle content length")] + BlobsBundleWrongLen, #[error("Nonce for account too low")] InvalidNonce, #[error("Transaction chain id mismatch, expected chain id: {0}")] @@ -66,6 +66,8 @@ pub enum MempoolError { NotEnoughBalance, #[error("Transaction gas fields are invalid")] InvalidTxGasvalues, + #[error("Blob versioned hashes do not match the blob commitments")] + BlobVersionedHashesIncorrectError, } #[derive(Debug)] diff --git a/crates/blockchain/fork_choice.rs b/crates/blockchain/fork_choice.rs index 46ba0ed95..3514d35cb 100644 --- a/crates/blockchain/fork_choice.rs +++ b/crates/blockchain/fork_choice.rs @@ -1,8 +1,8 @@ -use ethrex_core::{ +use ethereum_rust_core::{ types::{Block, BlockHash, BlockHeader, BlockNumber}, H256, }; -use ethrex_storage::{error::StoreError, Store}; +use ethereum_rust_storage::{error::StoreError, Store}; use crate::{ error::{self, InvalidForkChoice}, diff --git a/crates/blockchain/mempool.rs b/crates/blockchain/mempool.rs index aa566f239..485b9b283 100644 --- a/crates/blockchain/mempool.rs +++ b/crates/blockchain/mempool.rs @@ -5,27 +5,26 @@ use crate::{ MAX_INITCODE_SIZE, MIN_BASE_FEE_PER_BLOB_GAS, TX_ACCESS_LIST_ADDRESS_GAS, TX_ACCESS_LIST_STORAGE_KEY_GAS, TX_CREATE_GAS_COST, TX_DATA_NON_ZERO_GAS, TX_DATA_NON_ZERO_GAS_EIP2028, TX_DATA_ZERO_GAS_COST, TX_GAS_COST, - TX_INIT_CODE_WORD_GAS_COST, + TX_INIT_CODE_WORD_GAS_COST, VERSIONED_HASH_VERSION_KZG, }, error::MempoolError, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{ BlobsBundle, BlockHeader, ChainConfig, EIP4844Transaction, MempoolTransaction, Transaction, }, Address, H256, U256, }; -use ethrex_storage::{error::StoreError, Store}; +use ethereum_rust_storage::{error::StoreError, Store}; /// Add a blob transaction and its blobs bundle to the mempool -#[cfg(feature = "c-kzg")] pub fn add_blob_transaction( transaction: EIP4844Transaction, blobs_bundle: BlobsBundle, store: Store, ) -> Result { // Validate blobs bundle - blobs_bundle.validate(&transaction)?; + validate_blobs_bundle(&transaction, &blobs_bundle)?; // Validate transaction let transaction = Transaction::EIP4844Transaction(transaction); @@ -82,7 +81,7 @@ pub fn filter_transactions( return false; } // This is a temporary fix to avoid invalid transactions to be included. - // This should be removed once https://github.com/lambdaclass/ethrex/issues/680 + // This should be removed once https://github.com/lambdaclass/ethereum_rust/issues/680 // is addressed. } else if tx.effective_gas_tip(filter.base_fee).is_none() { return false; @@ -142,16 +141,16 @@ Stateless validations 3. Transaction's encoded size is smaller than maximum allowed -> I think that this is not in the spec, but it may be a good idea 4. Make sure the transaction is signed properly -5. Ensure a Blob Transaction comes with its sidecar (Done! - All blob validations have been moved to `common/types/blobs_bundle.rs`): - 1. Validate number of BlobHashes is positive (Done!) +5. Ensure a Blob Transaction comes with its sidecar (Done!): + 1. Validate number of BlobHashes is positive 2. Validate number of BlobHashes is less than the maximum allowed per block, which may be computed as `maxBlobGasPerBlock / blobTxBlobGasPerBlob` 3. Ensure number of BlobHashes is equal to: - The number of blobs (Done!) - The number of commitments (Done!) - The number of proofs (Done!) - 4. Validate that the hashes matches with the commitments, performing a `kzg4844` hash. (Done!) - 5. Verify the blob proofs with the `kzg4844` (Done!) + 4. Validate that the hashes matches with the commitments, performing a `kzg4844` hash. + 5. Verify the blob proofs with the `kzg4844` Stateful validations 1. Ensure transaction nonce is higher than the `from` address stored nonce 2. Certain pools do not allow for nonce gaps. Ensure a gap is not produced (that is, the transaction nonce is exactly the following of the stored one) @@ -201,11 +200,11 @@ fn validate_transaction(tx: &Transaction, store: Store) -> Result<(), MempoolErr // Check that the specified blob gas fee is above the minimum value if let Some(fee) = tx.max_fee_per_blob_gas() { - // Blob tx fee checks + // Blob tx if fee < MIN_BASE_FEE_PER_BLOB_GAS.into() { return Err(MempoolError::TxBlobBaseFeeTooLowError); } - }; + } let maybe_sender_acc_info = store.get_account_info(header_no, tx.sender())?; @@ -235,6 +234,40 @@ fn validate_transaction(tx: &Transaction, store: Store) -> Result<(), MempoolErr Ok(()) } +fn validate_blobs_bundle( + tx: &EIP4844Transaction, + blobs_bundle: &BlobsBundle, +) -> Result<(), MempoolError> { + let tx_blob_count = tx.blob_versioned_hashes.len(); + + if tx_blob_count != blobs_bundle.blobs.len() + || tx_blob_count != blobs_bundle.commitments.len() + || tx_blob_count != blobs_bundle.proofs.len() + { + return Err(MempoolError::BlobsBundleWrongLen); + }; + + // return error early if any commitment doesn't match it's blob versioned hash + for (commitment, blob_versioned_hash) in blobs_bundle + .commitments + .iter() + .zip(tx.blob_versioned_hashes.iter()) + { + if *blob_versioned_hash != kzg_to_versioned_hash(commitment) { + return Err(MempoolError::BlobVersionedHashesIncorrectError); + } + } + + Ok(()) +} + +pub fn kzg_to_versioned_hash(data: &[u8]) -> H256 { + use k256::sha2::Digest; + let mut versioned_hash: [u8; 32] = k256::sha2::Sha256::digest(data).into(); + versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; + versioned_hash.into() +} + fn transaction_intrinsic_gas( tx: &Transaction, header: &BlockHeader, @@ -304,13 +337,15 @@ mod tests { TX_DATA_ZERO_GAS_COST, TX_GAS_COST, TX_INIT_CODE_WORD_GAS_COST, }; - use super::{transaction_intrinsic_gas, validate_transaction}; - use ethrex_core::types::{ - BlockHeader, ChainConfig, EIP1559Transaction, EIP4844Transaction, Transaction, TxKind, + use super::{transaction_intrinsic_gas, validate_blobs_bundle, validate_transaction}; + use ethereum_rust_core::types::{ + BlobsBundle, BlockHeader, ChainConfig, EIP1559Transaction, EIP4844Transaction, Transaction, + TxKind, BYTES_PER_BLOB, }; - use ethrex_core::{Address, Bytes, H256, U256}; - use ethrex_storage::EngineType; - use ethrex_storage::{error::StoreError, Store}; + use ethereum_rust_core::{Address, Bytes, H256, U256}; + use ethereum_rust_storage::EngineType; + use ethereum_rust_storage::{error::StoreError, Store}; + use hex; fn setup_storage(config: ChainConfig, header: BlockHeader) -> Result { let store = Store::new("test", EngineType::InMemory)?; @@ -652,4 +687,116 @@ mod tests { Err(MempoolError::TxBlobBaseFeeTooLowError) )); } + + #[test] + fn transaction_with_correct_blobs_should_pass() { + let convert_str_to_bytes48 = |s| { + let bytes = hex::decode(s).expect("Invalid hex string"); + let mut array = [0u8; 48]; + array.copy_from_slice(&bytes[..48]); + array + }; + + // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs + + let blobs_bundle = BlobsBundle { + blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], + commitments: vec!["b90289aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", + "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] + .into_iter() + .map(|s| { + convert_str_to_bytes48(s) + }) + .collect(), + proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", + "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] + .into_iter() + .map(|s| { + convert_str_to_bytes48(s) + }) + .collect() + }; + + let tx = EIP4844Transaction { + nonce: 3, + max_priority_fee_per_gas: 0, + max_fee_per_gas: 0, + max_fee_per_blob_gas: 0.into(), + gas: 15_000_000, + to: Address::from_low_u64_be(1), // Normal tx + value: U256::zero(), // Value zero + data: Bytes::default(), // No data + access_list: Default::default(), // No access list + blob_versioned_hashes: vec![ + "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", + "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", + ] + .into_iter() + .map(|b| { + let bytes = hex::decode(b).expect("Invalid hex string"); + H256::from_slice(&bytes) + }) + .collect::>(), + ..Default::default() + }; + + assert!(matches!(validate_blobs_bundle(&tx, &blobs_bundle), Ok(()))); + } + + #[test] + fn transaction_with_incorrect_blobs_should_fail() { + let convert_str_to_bytes48 = |s| { + let bytes = hex::decode(s).expect("Invalid hex string"); + let mut array = [0u8; 48]; + array.copy_from_slice(&bytes[..48]); + array + }; + + // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs + let blobs_bundle = BlobsBundle { + blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], + commitments: vec!["dead89aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", + "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] + .into_iter() + .map(|s| { + convert_str_to_bytes48(s) + }) + .collect(), + proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", + "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] + .into_iter() + .map(|s| { + convert_str_to_bytes48(s) + }) + .collect() + }; + + let tx = EIP4844Transaction { + nonce: 3, + max_priority_fee_per_gas: 0, + max_fee_per_gas: 0, + max_fee_per_blob_gas: 0.into(), + gas: 15_000_000, + to: Address::from_low_u64_be(1), // Normal tx + value: U256::zero(), // Value zero + data: Bytes::default(), // No data + access_list: Default::default(), // No access list + blob_versioned_hashes: vec![ + "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", + "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", + ] + .into_iter() + .map(|b| { + let bytes = hex::decode(b).expect("Invalid hex string"); + H256::from_slice(&bytes) + }) + .collect::>(), + ..Default::default() + }; + + assert!(matches!( + validate_blobs_bundle(&tx, &blobs_bundle), + Err(MempoolError::BlobVersionedHashesIncorrectError) + )); + } } diff --git a/crates/blockchain/payload.rs b/crates/blockchain/payload.rs index a9e841fb8..e6fc48a9c 100644 --- a/crates/blockchain/payload.rs +++ b/crates/blockchain/payload.rs @@ -3,7 +3,7 @@ use std::{ collections::HashMap, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{ calculate_base_fee_per_blob_gas, calculate_base_fee_per_gas, compute_receipts_root, compute_transactions_root, compute_withdrawals_root, BlobsBundle, Block, BlockBody, @@ -12,9 +12,9 @@ use ethrex_core::{ }, Address, Bloom, Bytes, H256, U256, }; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::{error::StoreError, Store}; -use ethrex_vm::{ +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_storage::{error::StoreError, Store}; +use ethereum_rust_vm::{ beacon_root_contract_call, evm_state, execute_tx, get_state_transitions, process_withdrawals, spec_id, EvmError, EvmState, SpecId, }; @@ -224,7 +224,7 @@ fn fetch_mempool_transactions( context: &mut PayloadBuildContext, ) -> Result<(TransactionQueue, TransactionQueue), ChainError> { let tx_filter = PendingTxFilter { - /*TODO(https://github.com/lambdaclass/ethrex/issues/680): add tip filter */ + /*TODO(https://github.com/lambdaclass/ethereum_rust/issues/680): add tip filter */ base_fee: context.base_fee_per_gas(), blob_fee: Some(context.base_fee_per_blob_gas), ..Default::default() diff --git a/crates/blockchain/smoke_test.rs b/crates/blockchain/smoke_test.rs index c9fbe7498..f2897b7ff 100644 --- a/crates/blockchain/smoke_test.rs +++ b/crates/blockchain/smoke_test.rs @@ -10,11 +10,11 @@ mod blockchain_integration_test { payload::{build_payload, create_payload, BuildPayloadArgs}, }; - use ethrex_core::{ + use ethereum_rust_core::{ types::{Block, BlockHeader}, H160, H256, }; - use ethrex_storage::{EngineType, Store}; + use ethereum_rust_storage::{EngineType, Store}; #[test] fn test_small_to_long_reorg() { diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml index 57523b005..5aa9a95cb 100644 --- a/crates/common/Cargo.toml +++ b/crates/common/Cargo.toml @@ -1,22 +1,19 @@ [package] -name = "ethrex-core" +name = "ethereum_rust-core" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethrex-rlp.workspace = true -ethrex-trie = { path = "../storage/trie", default-features = false } +ethereum_rust-rlp.workspace = true +ethereum_rust-trie = { path = "../storage/trie", default-features = false } tinyvec = "1.6.0" ethereum-types.workspace = true serde.workspace = true serde_json.workspace = true thiserror.workspace = true -k256.workspace = true -# TODO(#1102): Move to Lambdaworks in the future -c-kzg = { version = "^1.0.3", optional = true } keccak-hash = "0.10.0" sha3.workspace = true secp256k1.workspace = true @@ -30,9 +27,8 @@ lazy_static.workspace = true hex-literal.workspace = true [features] -default = ["libmdbx", "c-kzg"] -libmdbx = ["ethrex-trie/libmdbx"] -c-kzg = ["dep:c-kzg"] +default = ["libmdbx"] +libmdbx = ["ethereum_rust-trie/libmdbx"] [lib] path = "./core.rs" diff --git a/crates/common/rlp/Cargo.toml b/crates/common/rlp/Cargo.toml index 34c6a9680..b3737055b 100644 --- a/crates/common/rlp/Cargo.toml +++ b/crates/common/rlp/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex-rlp" +name = "ethereum_rust-rlp" version.workspace = true edition.workspace = true diff --git a/crates/common/rlp/decode.rs b/crates/common/rlp/decode.rs index 6418e693a..0b04ed67d 100644 --- a/crates/common/rlp/decode.rs +++ b/crates/common/rlp/decode.rs @@ -522,9 +522,6 @@ pub(crate) fn static_left_pad(data: &[u8]) -> Result<[u8; N], RL if data[0] == 0 { return Err(RLPDecodeError::MalformedData); } - if data.len() > N { - return Err(RLPDecodeError::InvalidLength); - } let data_start_index = N.saturating_sub(data.len()); result .get_mut(data_start_index..) diff --git a/crates/common/rlp/structs.rs b/crates/common/rlp/structs.rs index 161e443fd..e497cb26b 100644 --- a/crates/common/rlp/structs.rs +++ b/crates/common/rlp/structs.rs @@ -16,9 +16,9 @@ use bytes::Bytes; /// # Examples /// /// ``` -/// # use ethrex_rlp::structs::Decoder; -/// # use ethrex_rlp::error::RLPDecodeError; -/// # use ethrex_rlp::decode::RLPDecode; +/// # use ethereum_rust_rlp::structs::Decoder; +/// # use ethereum_rust_rlp::error::RLPDecodeError; +/// # use ethereum_rust_rlp::decode::RLPDecode; /// #[derive(Debug, PartialEq, Eq)] /// struct Simple { /// pub a: u8, @@ -131,8 +131,8 @@ fn field_decode_error(field_name: &str, err: RLPDecodeError) -> RLPDecodeErro /// # Examples /// /// ``` -/// # use ethrex_rlp::structs::Encoder; -/// # use ethrex_rlp::encode::RLPEncode; +/// # use ethereum_rust_rlp::structs::Encoder; +/// # use ethereum_rust_rlp::encode::RLPEncode; /// # use bytes::BufMut; /// #[derive(Debug, PartialEq, Eq)] /// struct Simple { diff --git a/crates/common/types/account.rs b/crates/common/types/account.rs index 378419199..5b4910835 100644 --- a/crates/common/types/account.rs +++ b/crates/common/types/account.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; use bytes::Bytes; +use ethereum_rust_trie::Trie; use ethereum_types::{H256, U256}; -use ethrex_trie::Trie; use serde::{Deserialize, Serialize}; use sha3::{Digest as _, Keccak256}; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ constants::RLP_NULL, decode::RLPDecode, encode::RLPEncode, diff --git a/crates/common/types/blobs_bundle.rs b/crates/common/types/blobs_bundle.rs index 974892476..8df27665b 100644 --- a/crates/common/types/blobs_bundle.rs +++ b/crates/common/types/blobs_bundle.rs @@ -1,17 +1,7 @@ -#[cfg(feature = "c-kzg")] -use lazy_static::lazy_static; use std::ops::AddAssign; use crate::serde_utils; -use crate::{ - types::{constants::VERSIONED_HASH_VERSION_KZG, transaction::EIP4844Transaction}, - Bytes, H256, -}; - -#[cfg(feature = "c-kzg")] -use c_kzg::{ethereum_kzg_settings, KzgCommitment, KzgProof, KzgSettings}; - -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -26,11 +16,6 @@ pub type Blob = [u8; BYTES_PER_BLOB]; pub type Commitment = Bytes48; pub type Proof = Bytes48; -#[cfg(feature = "c-kzg")] -lazy_static! { - static ref KZG_SETTINGS: &'static KzgSettings = ethereum_kzg_settings(); -} - #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "camelCase")] /// Struct containing all the blobs for a blob transaction, along with the corresponding commitments and proofs @@ -43,134 +28,6 @@ pub struct BlobsBundle { pub proofs: Vec, } -pub fn blob_from_bytes(bytes: Bytes) -> Result { - // This functions moved from `l2/utils/eth_client/transaction.rs` - // We set the first byte of every 32-bytes chunk to 0x00 - // so it's always under the field module. - if bytes.len() > BYTES_PER_BLOB * 31 / 32 { - return Err(BlobsBundleError::BlobDataInvalidBytesLength); - } - - let mut buf = [0u8; BYTES_PER_BLOB]; - buf[..(bytes.len() * 32).div_ceil(31)].copy_from_slice( - &bytes - .chunks(31) - .map(|x| [&[0x00], x].concat()) - .collect::>() - .concat(), - ); - - Ok(buf) -} - -fn kzg_commitment_to_versioned_hash(data: &Commitment) -> H256 { - use k256::sha2::Digest; - let mut versioned_hash: [u8; 32] = k256::sha2::Sha256::digest(data).into(); - versioned_hash[0] = VERSIONED_HASH_VERSION_KZG; - versioned_hash.into() -} - -#[cfg(feature = "c-kzg")] -fn blob_to_kzg_commitment_and_proof(blob: &Blob) -> Result<(Commitment, Proof), BlobsBundleError> { - let blob: c_kzg::Blob = (*blob).into(); - - let commitment = KzgCommitment::blob_to_kzg_commitment(&blob, &KZG_SETTINGS) - .or(Err(BlobsBundleError::BlobToCommitmentAndProofError))?; - - let commitment_bytes = commitment.to_bytes(); - - let proof = KzgProof::compute_blob_kzg_proof(&blob, &commitment_bytes, &KZG_SETTINGS) - .or(Err(BlobsBundleError::BlobToCommitmentAndProofError))?; - - let proof_bytes = proof.to_bytes(); - - Ok((commitment_bytes.into_inner(), proof_bytes.into_inner())) -} - -#[cfg(feature = "c-kzg")] -fn verify_blob_kzg_proof( - blob: Blob, - commitment: Commitment, - proof: Proof, -) -> Result { - let blob: c_kzg::Blob = blob.into(); - let commitment: c_kzg::Bytes48 = commitment.into(); - let proof: c_kzg::Bytes48 = proof.into(); - - KzgProof::verify_blob_kzg_proof(&blob, &commitment, &proof, &KZG_SETTINGS) - .or(Err(BlobsBundleError::BlobToCommitmentAndProofError)) -} - -impl BlobsBundle { - // In the future we might want to provide a new method that calculates the commitments and proofs using the following. - #[cfg(feature = "c-kzg")] - pub fn create_from_blobs(blobs: &Vec) -> Result { - let mut commitments = Vec::new(); - let mut proofs = Vec::new(); - - // Populate the commitments and proofs - for blob in blobs { - let (commitment, proof) = blob_to_kzg_commitment_and_proof(blob)?; - commitments.push(commitment); - proofs.push(proof); - } - - Ok(Self { - blobs: blobs.clone(), - commitments, - proofs, - }) - } - - pub fn generate_versioned_hashes(&self) -> Vec { - self.commitments - .iter() - .map(kzg_commitment_to_versioned_hash) - .collect() - } - - #[cfg(feature = "c-kzg")] - pub fn validate(&self, tx: &EIP4844Transaction) -> Result<(), BlobsBundleError> { - let blob_count = self.blobs.len(); - - // Check if the blob bundle is empty - if blob_count == 0 { - return Err(BlobsBundleError::BlobBundleEmptyError); - } - - // Check if the blob versioned hashes and blobs bundle content length mismatch - if blob_count != self.commitments.len() - || blob_count != self.proofs.len() - || blob_count != tx.blob_versioned_hashes.len() - { - return Err(BlobsBundleError::BlobsBundleWrongLen); - }; - - // Check versioned hashes match the tx - for (commitment, blob_versioned_hash) in - self.commitments.iter().zip(tx.blob_versioned_hashes.iter()) - { - if *blob_versioned_hash != kzg_commitment_to_versioned_hash(commitment) { - return Err(BlobsBundleError::BlobVersionedHashesError); - } - } - - // Validate the blobs with the commitments and proofs - for ((blob, commitment), proof) in self - .blobs - .iter() - .zip(self.commitments.iter()) - .zip(self.proofs.iter()) - { - if !verify_blob_kzg_proof(*blob, *commitment, *proof)? { - return Err(BlobsBundleError::BlobToCommitmentAndProofError); - } - } - - Ok(()) - } -} - impl RLPEncode for BlobsBundle { fn encode(&self, buf: &mut dyn bytes::BufMut) { let encoder = Encoder::new(buf); @@ -206,163 +63,3 @@ impl AddAssign for BlobsBundle { self.proofs.extend_from_slice(&rhs.proofs); } } - -#[derive(Debug, thiserror::Error)] -pub enum BlobsBundleError { - #[error("Blob data has an invalid length")] - BlobDataInvalidBytesLength, - #[error("Blob bundle is empty")] - BlobBundleEmptyError, - #[error("Blob versioned hashes and blobs bundle content length mismatch")] - BlobsBundleWrongLen, - #[error("Blob versioned hashes are incorrect")] - BlobVersionedHashesError, - #[error("Blob to commitment and proof generation error")] - BlobToCommitmentAndProofError, -} - -#[cfg(test)] - -mod tests { - use super::*; - use crate::{ - types::{blobs_bundle, transaction::EIP4844Transaction}, - Address, Bytes, U256, - }; - mod shared { - pub fn convert_str_to_bytes48(s: &str) -> [u8; 48] { - let bytes = hex::decode(s).expect("Invalid hex string"); - let mut array = [0u8; 48]; - array.copy_from_slice(&bytes[..48]); - array - } - } - - #[test] - fn transaction_with_valid_blobs_should_pass() { - let blobs = vec!["Hello, world!".as_bytes(), "Goodbye, world!".as_bytes()] - .into_iter() - .map(|data| blobs_bundle::blob_from_bytes(data.into()).expect("Failed to create blob")) - .collect(); - - let blobs_bundle = - BlobsBundle::create_from_blobs(&blobs).expect("Failed to create blobs bundle"); - - let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); - - let tx = EIP4844Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - max_fee_per_blob_gas: 0.into(), - gas: 15_000_000, - to: Address::from_low_u64_be(1), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::default(), // No data - access_list: Default::default(), // No access list - blob_versioned_hashes, - ..Default::default() - }; - - assert!(matches!(blobs_bundle.validate(&tx), Ok(()))); - } - #[test] - fn transaction_with_invalid_proofs_should_fail() { - // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs, but with 0 size blobs - let blobs_bundle = BlobsBundle { - blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], - commitments: vec!["b90289aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", - "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] - .into_iter() - .map(|s| { - shared::convert_str_to_bytes48(s) - }) - .collect(), - proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", - "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] - .into_iter() - .map(|s| { - shared::convert_str_to_bytes48(s) - }) - .collect() - }; - - let tx = EIP4844Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - max_fee_per_blob_gas: 0.into(), - gas: 15_000_000, - to: Address::from_low_u64_be(1), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::default(), // No data - access_list: Default::default(), // No access list - blob_versioned_hashes: vec![ - "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", - "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", - ] - .into_iter() - .map(|b| { - let bytes = hex::decode(b).expect("Invalid hex string"); - H256::from_slice(&bytes) - }) - .collect::>(), - ..Default::default() - }; - - assert!(matches!( - blobs_bundle.validate(&tx), - Err(BlobsBundleError::BlobToCommitmentAndProofError) - )); - } - - #[test] - fn transaction_with_incorrect_blobs_should_fail() { - // blob data taken from: https://etherscan.io/tx/0x02a623925c05c540a7633ffa4eb78474df826497faa81035c4168695656801a2#blobs - let blobs_bundle = BlobsBundle { - blobs: vec![[0; BYTES_PER_BLOB], [0; BYTES_PER_BLOB]], - commitments: vec!["dead89aabe0fcfb8db20a76b863ba90912d1d4d040cb7a156427d1c8cd5825b4d95eaeb221124782cc216960a3d01ec5", - "91189a03ce1fe1225fc5de41d502c3911c2b19596f9011ea5fca4bf311424e5f853c9c46fe026038036c766197af96a0"] - .into_iter() - .map(|s| { - shared::convert_str_to_bytes48(s) - }) - .collect(), - proofs: vec!["b502263fc5e75b3587f4fb418e61c5d0f0c18980b4e00179326a65d082539a50c063507a0b028e2db10c55814acbe4e9", - "a29c43f6d05b7f15ab6f3e5004bd5f6b190165dc17e3d51fd06179b1e42c7aef50c145750d7c1cd1cd28357593bc7658"] - .into_iter() - .map(|s| { - shared::convert_str_to_bytes48(s) - }) - .collect() - }; - - let tx = EIP4844Transaction { - nonce: 3, - max_priority_fee_per_gas: 0, - max_fee_per_gas: 0, - max_fee_per_blob_gas: 0.into(), - gas: 15_000_000, - to: Address::from_low_u64_be(1), // Normal tx - value: U256::zero(), // Value zero - data: Bytes::default(), // No data - access_list: Default::default(), // No access list - blob_versioned_hashes: vec![ - "01ec8054d05bfec80f49231c6e90528bbb826ccd1464c255f38004099c8918d9", - "0180cb2dee9e6e016fabb5da4fb208555f5145c32895ccd13b26266d558cd77d", - ] - .into_iter() - .map(|b| { - let bytes = hex::decode(b).expect("Invalid hex string"); - H256::from_slice(&bytes) - }) - .collect::>(), - ..Default::default() - }; - - assert!(matches!( - blobs_bundle.validate(&tx), - Err(BlobsBundleError::BlobVersionedHashesError) - )); - } -} diff --git a/crates/common/types/block.rs b/crates/common/types/block.rs index bbd2acd3b..ba6261314 100644 --- a/crates/common/types/block.rs +++ b/crates/common/types/block.rs @@ -7,14 +7,14 @@ use crate::{ Address, H256, U256, }; use bytes::Bytes; -use ethereum_types::Bloom; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, structs::{Decoder, Encoder}, }; -use ethrex_trie::Trie; +use ethereum_rust_trie::Trie; +use ethereum_types::Bloom; use keccak_hash::keccak; use serde::{Deserialize, Serialize}; diff --git a/crates/common/types/constants.rs b/crates/common/types/constants.rs index 0e195cb38..52e5034ee 100644 --- a/crates/common/types/constants.rs +++ b/crates/common/types/constants.rs @@ -7,7 +7,6 @@ pub const GWEI_TO_WEI: u64 = 1_000_000_000; pub const INITIAL_BASE_FEE: u64 = 1_000_000_000; //Initial base fee as defined in [EIP-1559](https://eips.ethereum.org/EIPS/eip-1559) pub const MIN_BASE_FEE_PER_BLOB_GAS: u64 = 1; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) pub const BLOB_BASE_FEE_UPDATE_FRACTION: u64 = 3338477; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) -pub const VERSIONED_HASH_VERSION_KZG: u8 = 0x01; // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) // Blob size related // Defined in [EIP-4844](https://eips.ethereum.org/EIPS/eip-4844) diff --git a/crates/common/types/fork_id.rs b/crates/common/types/fork_id.rs index ec3945612..a44795b35 100644 --- a/crates/common/types/fork_id.rs +++ b/crates/common/types/fork_id.rs @@ -1,5 +1,5 @@ use crc32fast::Hasher; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -10,7 +10,7 @@ use ethereum_types::H32; use super::{BlockHash, BlockNumber, ChainConfig}; -#[derive(Debug, PartialEq)] +#[derive(Debug)] pub struct ForkId { fork_hash: H32, fork_next: BlockNumber, diff --git a/crates/common/types/genesis.rs b/crates/common/types/genesis.rs index 21ed59b0e..f729f63b1 100644 --- a/crates/common/types/genesis.rs +++ b/crates/common/types/genesis.rs @@ -1,11 +1,11 @@ use bytes::Bytes; +use ethereum_rust_trie::Trie; use ethereum_types::{Address, Bloom, H256, U256}; -use ethrex_trie::Trie; use serde::{Deserialize, Serialize}; use sha3::{Digest, Keccak256}; use std::collections::HashMap; -use ethrex_rlp::encode::RLPEncode; +use ethereum_rust_rlp::encode::RLPEncode; use super::{ compute_receipts_root, compute_transactions_root, compute_withdrawals_root, AccountState, diff --git a/crates/common/types/mod.rs b/crates/common/types/mod.rs index 736de7a16..e244999d6 100644 --- a/crates/common/types/mod.rs +++ b/crates/common/types/mod.rs @@ -1,5 +1,5 @@ mod account; -pub mod blobs_bundle; +mod blobs_bundle; mod block; mod constants; mod fork_id; diff --git a/crates/common/types/receipt.rs b/crates/common/types/receipt.rs index e0e888244..d70d5bad9 100644 --- a/crates/common/types/receipt.rs +++ b/crates/common/types/receipt.rs @@ -1,11 +1,11 @@ use bytes::Bytes; -use ethereum_types::{Address, Bloom, BloomInput, H256}; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, structs::{Decoder, Encoder}, }; +use ethereum_types::{Address, Bloom, BloomInput, H256}; use serde::{Deserialize, Serialize}; use super::TxType; diff --git a/crates/common/types/transaction.rs b/crates/common/types/transaction.rs index 95fea476f..e1ea3fefb 100644 --- a/crates/common/types/transaction.rs +++ b/crates/common/types/transaction.rs @@ -9,7 +9,7 @@ use serde::{ser::SerializeStruct, Deserialize, Serialize}; pub use serde_impl::{AccessListEntry, GenericTransaction}; use sha3::{Digest, Keccak256}; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ constants::RLP_NULL, decode::{get_rlp_bytes_item_payload, is_encoded_as_bytes, RLPDecode}, encode::{PayloadRLPEncode, RLPEncode}, diff --git a/crates/l2/.env.example b/crates/l2/.env.example index 865056bcb..54b5f9ba4 100644 --- a/crates/l2/.env.example +++ b/crates/l2/.env.example @@ -13,6 +13,7 @@ L1_WATCHER_TOPICS=0x6f65d68a35457dd88c1f8641be5da191aa122bc76de22ab0789dcc71929d L1_WATCHER_CHECK_INTERVAL_MS=1000 L1_WATCHER_MAX_BLOCK_STEP=5000 L1_WATCHER_L2_PROPOSER_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 +L1_WATCHER_L2_PROPOSER_ADDRESS=0x3d1e15a1a55578f7c920884a9943b3b35d0d885b ENGINE_API_RPC_URL=http://localhost:8551 ENGINE_API_JWT_PATH=./jwt.hex PROVER_SERVER_LISTEN_IP=127.0.0.1 diff --git a/crates/l2/Cargo.toml b/crates/l2/Cargo.toml index 408198c35..2a08fea5e 100644 --- a/crates/l2/Cargo.toml +++ b/crates/l2/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex-l2" +name = "ethereum_rust-l2" version = "0.1.0" edition = "2021" @@ -13,13 +13,13 @@ tracing.workspace = true serde.workspace = true serde_json.workspace = true ethereum-types.workspace = true -ethrex-core.workspace = true -ethrex-rlp.workspace = true -ethrex-rpc.workspace = true -ethrex-blockchain.workspace = true -ethrex-storage.workspace = true -ethrex-vm.workspace = true -ethrex-dev = { path = "../../crates/blockchain/dev" } +ethereum_rust-core.workspace = true +ethereum_rust-rlp.workspace = true +ethereum_rust-rpc.workspace = true +ethereum_rust-blockchain.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-vm.workspace = true +ethereum_rust-dev = { path = "../../crates/blockchain/dev" } hex.workspace = true bytes.workspace = true jsonwebtoken.workspace = true @@ -27,12 +27,14 @@ secp256k1.workspace = true keccak-hash = "0.10.0" envy = "0.4.2" thiserror.workspace = true +sha2 = "0.10.8" +c-kzg = "^1.0.3" # risc0 risc0-zkvm = { version = "1.1.2" } [dev-dependencies] -ethrex-sdk = { path = "./sdk" } +ethereum_rust-sdk = { path = "./sdk" } rand = "0.8.5" diff --git a/crates/l2/Dockerfile b/crates/l2/Dockerfile index 52da2edad..6aae7e37c 100644 --- a/crates/l2/Dockerfile +++ b/crates/l2/Dockerfile @@ -9,7 +9,7 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethrex +WORKDIR /ethereum_rust FROM chef AS planner COPY . . @@ -17,7 +17,7 @@ COPY . . RUN cargo chef prepare --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethrex/recipe.json recipe.json +COPY --from=planner /ethereum_rust/recipe.json recipe.json # Build dependencies only, these remained cached RUN cargo chef cook --release --recipe-path recipe.json @@ -27,6 +27,6 @@ RUN cargo build --release --features l2 FROM ubuntu:24.04 WORKDIR /usr/local/bin -COPY --from=builder ethrex/target/release/ethrex . +COPY --from=builder ethereum_rust/target/release/ethereum_rust . EXPOSE 1729 -ENTRYPOINT [ "./ethrex" ] +ENTRYPOINT [ "./ethereum_rust" ] diff --git a/crates/l2/Makefile b/crates/l2/Makefile index 54f011bd1..3542fd899 100644 --- a/crates/l2/Makefile +++ b/crates/l2/Makefile @@ -8,7 +8,7 @@ L1_GENESIS_FILE_PATH=../../test_data/genesis-l1.json help: ## 📚 Show help for each of the Makefile recipes @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' -init: init-local-l1 deploy-l1 init-l2 ## 🚀 Initializes a localnet with Lambda ethrex client as both L1 and L2 +init: init-local-l1 deploy-l1 init-l2 ## 🚀 Initializes a localnet with Lambda Ethereum Rust client as both L1 and L2 down: down-local-l1 down-l2 ## 🛑 Shuts down the localnet @@ -16,22 +16,22 @@ clean: clean-contract-deps ## 🧹 Cleans the localnet restart: restart-local-l1 deploy-l1 restart-l2 ## 🔄 Restarts the localnet -cli: ## 🛠️ Installs the L2 Lambda ethrex CLI - cargo install --path ${ethrex_PATH}/cmd/ethrex_l2/ --force +cli: ## 🛠️ Installs the L2 Lambda Ethereum Rust CLI + cargo install --path ${ETHEREUM_RUST_PATH}/cmd/ethereum_rust_l2/ --force # Variables -ethrex_PATH=$(shell pwd)/../.. -ethrex_BIN_PATH=$(ethrex_PATH)/target/release/ethrex -ethrex_DEV_DOCKER_COMPOSE_PATH=$(ethrex_PATH)/crates/blockchain/dev/docker-compose-dev.yaml -ethrex_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml +ETHEREUM_RUST_PATH=$(shell pwd)/../.. +ETHEREUM_RUST_BIN_PATH=$(ETHEREUM_RUST_PATH)/target/release/ethereum_rust +ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH=$(ETHEREUM_RUST_PATH)/crates/blockchain/dev/docker-compose-dev.yaml +ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH=./docker-compose-l2.yaml -ethrex_L2_CONTRACTS_PATH=./contracts +ETHEREUM_RUST_L2_CONTRACTS_PATH=./contracts L1_RPC_URL=http://localhost:8545 L1_PRIVATE_KEY=0x385c546456b6a603a1cfcaa9ec9494ba4832da08dd6bcf4de9a71e4a01b74924 -ethrex_L2_DEV_LIBMDBX=dev_ethrex_l2 -ethrex_L1_DEV_LIBMDBX=dev_ethrex_l1 +ETHEREUM_RUST_L2_DEV_LIBMDBX=dev_ethereum_rust_l2 +ETHEREUM_RUST_L1_DEV_LIBMDBX=dev_ethereum_rust_l1 L1_PORT=8545 L2_PORT=1729 L1_AUTH_PORT=8551 @@ -40,25 +40,25 @@ L2_AUTH_PORT=8552 # Local L1 -init-local-l1: ## 🚀 Initializes an L1 Lambda ethrex Client with Docker (Used with make init) - docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} up -d - -init-l1: ## 🚀 Initializes an L1 Lambda ethrex Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features dev -- \ +init-local-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client with Docker (Used with make init) + docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} up -d + +init-l1: ## 🚀 Initializes an L1 Lambda Ethereum Rust Client + cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features dev -- \ --network ${L1_GENESIS_FILE_PATH} \ --http.port ${L1_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L1_AUTH_PORT} \ - --datadir ${ethrex_L1_DEV_LIBMDBX} + --datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX} -down-local-l1: ## 🛑 Shuts down the L1 Lambda ethrex Client - docker compose -f ${ethrex_DEV_DOCKER_COMPOSE_PATH} down +down-local-l1: ## 🛑 Shuts down the L1 Lambda Ethereum Rust Client + docker compose -f ${ETHEREUM_RUST_DEV_DOCKER_COMPOSE_PATH} down docker compose -f docker-compose-l2.yaml down -restart-local-l1: down-local-l1 init-local-l1 ## 🔄 Restarts the L1 Lambda ethrex Client +restart-local-l1: down-local-l1 init-local-l1 ## 🔄 Restarts the L1 Lambda Ethereum Rust Client rm_dev_libmdbx_l1: ## 🛑 Removes the Libmdbx DB used by the L1 - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex -- removedb --datadir ${ethrex_L1_DEV_LIBMDBX} + cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L1_DEV_LIBMDBX} # Contracts @@ -69,37 +69,37 @@ clean-contract-deps: ## 🧹 Cleans the dependencies for the L1 contracts. restart-contract-deps: clean-contract-deps ## 🔄 Restarts the dependencies for the L1 contracts. deploy-l1: ## 📜 Deploys the L1 contracts - DEPLOYER_CONTRACTS_PATH=contracts cargo run --release --bin ethrex_l2_l1_deployer --manifest-path ${ethrex_L2_CONTRACTS_PATH}/Cargo.toml + DEPLOYER_CONTRACTS_PATH=contracts cargo run --release --bin ethereum_rust_l2_l1_deployer --manifest-path ${ETHEREUM_RUST_L2_CONTRACTS_PATH}/Cargo.toml # L2 -init-l2: ## 🚀 Initializes an L2 Lambda ethrex Client - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex --features l2 -- \ +init-l2: ## 🚀 Initializes an L2 Lambda Ethereum Rust Client + cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust --features l2 -- \ --network ${L2_GENESIS_FILE_PATH} \ --http.port ${L2_PORT} \ --http.addr 0.0.0.0 \ --authrpc.port ${L2_AUTH_PORT} \ - --datadir ${ethrex_L2_DEV_LIBMDBX} + --datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX} -down-l2: ## 🛑 Shuts down the L2 Lambda ethrex Client - pkill -f ethrex || exit 0 +down-l2: ## 🛑 Shuts down the L2 Lambda Ethereum Rust Client + pkill -f ethereum_rust || exit 0 -restart-l2: down-l2 init-l2 ## 🔄 Restarts the L2 Lambda ethrex Client +restart-l2: down-l2 init-l2 ## 🔄 Restarts the L2 Lambda Ethereum Rust Client init-l2-prover: ## 🚀 Initializes the Prover - cargo run --release --features build_zkvm --manifest-path ../../Cargo.toml --bin ethrex_prover + cargo run --release --features build_zkvm --manifest-path ../../Cargo.toml --bin ethereum_rust_prover init-l2-prover-gpu: ## 🚀 Initializes the Prover with GPU support - cargo run --release --features "build_zkvm,gpu" --manifest-path ../../Cargo.toml --bin ethrex_prover + cargo run --release --features "build_zkvm,gpu" --manifest-path ../../Cargo.toml --bin ethereum_rust_prover rm_dev_libmdbx_l2: ## 🛑 Removes the Libmdbx DB used by the L2 - cargo run --release --manifest-path ../../Cargo.toml --bin ethrex -- removedb --datadir ${ethrex_L2_DEV_LIBMDBX} + cargo run --release --manifest-path ../../Cargo.toml --bin ethereum_rust -- removedb --datadir ${ETHEREUM_RUST_L2_DEV_LIBMDBX} # CI Testing test: - docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down - docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} up -d --build + docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} down + docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} up -d --build BRIDGE_ADDRESS=$$(grep 'L1_WATCHER_BRIDGE_ADDRESS' .env | cut -d= -f2) ON_CHAIN_PROPOSER_ADDRESS=$$(grep 'COMMITTER_ON_CHAIN_PROPOSER_ADDRESS' .env | cut -d= -f2) cargo test --release testito -- --nocapture - docker compose -f ${ethrex_L2_DOCKER_COMPOSE_PATH} down + docker compose -f ${ETHEREUM_RUST_L2_DOCKER_COMPOSE_PATH} down diff --git a/crates/l2/contracts/Cargo.toml b/crates/l2/contracts/Cargo.toml index ea5a65510..76df9717f 100644 --- a/crates/l2/contracts/Cargo.toml +++ b/crates/l2/contracts/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex_l2-deployer" +name = "ethereum_rust_l2-deployer" version = "0.1.0" edition = "2021" @@ -17,9 +17,9 @@ colored = "2.1.0" lazy_static = "1.5.0" tracing.workspace = true -ethrex-l2 = { path = "../../l2" } -ethrex-core = { path = "../../common" } +ethereum_rust-l2 = { path = "../../l2" } +ethereum_rust-core = { path = "../../common" } [[bin]] -name = "ethrex_l2_l1_deployer" +name = "ethereum_rust_l2_l1_deployer" path = "./deployer.rs" diff --git a/crates/l2/contracts/Dockerfile b/crates/l2/contracts/Dockerfile index d743f0d2e..2334fd52b 100644 --- a/crates/l2/contracts/Dockerfile +++ b/crates/l2/contracts/Dockerfile @@ -9,18 +9,18 @@ RUN apt-get update && apt-get install -y \ && rm -rf /var/lib/apt/lists/* RUN cargo install cargo-chef -WORKDIR /ethrex +WORKDIR /ethereum_rust FROM chef AS planner COPY . . # Determine the crates that need to be built from dependencies -RUN cargo chef prepare --bin ethrex_l2_l1_deployer --recipe-path recipe.json +RUN cargo chef prepare --bin ethereum_rust_l2_l1_deployer --recipe-path recipe.json FROM chef AS builder -COPY --from=planner /ethrex/recipe.json recipe.json +COPY --from=planner /ethereum_rust/recipe.json recipe.json # Build dependencies only, these remained cached -RUN cargo chef cook --release --recipe-path recipe.json --manifest-path crates/l2/contracts/Cargo.toml --bin ethrex_l2_l1_deployer +RUN cargo chef cook --release --recipe-path recipe.json --manifest-path crates/l2/contracts/Cargo.toml --bin ethereum_rust_l2_l1_deployer COPY . . RUN cargo build --release --manifest-path crates/l2/contracts/Cargo.toml @@ -32,7 +32,7 @@ RUN apt-get update && apt-get -y install git gnupg software-properties-common RUN add-apt-repository ppa:ethereum/ethereum RUN apt-get update && apt-get -y install solc -COPY --from=builder ethrex/target/release/ethrex_l2_l1_deployer . +COPY --from=builder ethereum_rust/target/release/ethereum_rust_l2_l1_deployer . EXPOSE 1729 -ENTRYPOINT [ "./ethrex_l2_l1_deployer" ] +ENTRYPOINT [ "./ethereum_rust_l2_l1_deployer" ] diff --git a/crates/l2/contracts/deployer.rs b/crates/l2/contracts/deployer.rs index 66de5adf3..602bd21b3 100644 --- a/crates/l2/contracts/deployer.rs +++ b/crates/l2/contracts/deployer.rs @@ -1,11 +1,11 @@ use bytes::Bytes; use colored::Colorize; -use ethereum_types::{Address, H160, H256}; -use ethrex_core::types::{GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM}; -use ethrex_l2::utils::{ +use ethereum_rust_core::types::{GAS_LIMIT_ADJUSTMENT_FACTOR, GAS_LIMIT_MINIMUM}; +use ethereum_rust_l2::utils::{ config::{read_env_as_lines, read_env_file, write_env}, eth_client::{eth_sender::Overrides, EthClient}, }; +use ethereum_types::{Address, H160, H256}; use keccak_hash::keccak; use secp256k1::SecretKey; use spinoff::{spinner, spinners, Color, Spinner}; diff --git a/crates/l2/contracts/src/l1/CommonBridge.sol b/crates/l2/contracts/src/l1/CommonBridge.sol index 59ef19e5f..93afb58fb 100644 --- a/crates/l2/contracts/src/l1/CommonBridge.sol +++ b/crates/l2/contracts/src/l1/CommonBridge.sol @@ -55,11 +55,6 @@ contract CommonBridge is ICommonBridge, Ownable, ReentrancyGuard { ON_CHAIN_PROPOSER = onChainProposer; } - /// @inheritdoc ICommonBridge - function getDepositLogs() public view returns (bytes32[] memory) { - return depositLogs; - } - /// @inheritdoc ICommonBridge function deposit(address to) public payable { require(msg.value > 0, "CommonBridge: amount to deposit is zero"); diff --git a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol index 4958eafaa..92f6f6721 100644 --- a/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol +++ b/crates/l2/contracts/src/l1/interfaces/ICommonBridge.sol @@ -41,11 +41,6 @@ interface ICommonBridge { uint256 indexed claimedAmount ); - /// @notice Method to retrieve all the deposit logs hashes. - /// @dev This method is used by the L2 L1_Watcher to get the remaining - /// deposit logs to be processed. - function getDepositLogs() external view returns (bytes32[] memory); - /// @notice Initializes the contract. /// @dev This method is called only once after the contract is deployed. /// @dev It sets the OnChainProposer address. diff --git a/crates/l2/docker-compose-l2.yaml b/crates/l2/docker-compose-l2.yaml index a0871823c..a434e3ab3 100644 --- a/crates/l2/docker-compose-l2.yaml +++ b/crates/l2/docker-compose-l2.yaml @@ -5,7 +5,7 @@ services: contract_deployer: platform: linux/amd64 container_name: contract_deployer - image: ethrex_l2_contract_deployer + image: ethereum_rust_l2_contract_deployer build: context: ../../ args: @@ -17,17 +17,17 @@ services: env_file: - .env environment: - - ETH_RPC_URL=http://ethrex_l1:8545 + - ETH_RPC_URL=http://ethereum_rust_l1:8545 - DEPLOYER_CONTRACTS_PATH=/contracts - ENV_FILE=/.env depends_on: - - ethrex + - ethereum_rust restart: on-failure:3 - ethrex_l2: + ethereum_rust_l2: restart: always - container_name: ethrex_l2 - image: "ethrex_l2" + container_name: ethereum_rust_l2 + image: "ethereum_rust_l2" build: context: ../../ dockerfile: ./crates/l2/Dockerfile @@ -35,7 +35,7 @@ services: - 127.0.0.1:1729:1729 - 3000:3000 environment: - - ETH_RPC_URL=http://ethrex_l1:8545 + - ETH_RPC_URL=http://ethereum_rust_l1:8545 - ENV_FILE=/.env volumes: - ../../test_data/genesis-l2.json:/genesis-l2.json diff --git a/crates/l2/docs/README.md b/crates/l2/docs/README.md index d92f09df6..e1ae9f833 100644 --- a/crates/l2/docs/README.md +++ b/crates/l2/docs/README.md @@ -1,4 +1,4 @@ -# ethrex L2 Docs +# Ethereum Rust L2 Docs For a high level overview of the L2: @@ -19,7 +19,7 @@ Configuration is done through env vars. A detailed list is available in each par Load tests are available via L2 CLI. The test take a list of private keys and send a bunch of transactions from each of them to some address. To run them, use the following command: ```bash -cargo run --bin ethrex_l2 -- test load --path +cargo run --bin ethereum_rust_l2 -- test load --path ``` The path should point to a plain text file containing a list of private keys, one per line. Those account must be funded on the L2 network. Use `--help` to see more available options. diff --git a/crates/l2/docs/contracts.md b/crates/l2/docs/contracts.md index 1bcc926c0..1301e427b 100644 --- a/crates/l2/docs/contracts.md +++ b/crates/l2/docs/contracts.md @@ -1,15 +1,14 @@ -# ethrex L2 Contracts +# Ethereum Rust L2 Contracts ## ToC -- [ethrex L2 Contracts](#ethrex-l2-contracts) - - [ToC](#toc) - - [L1 side](#l1-side) - - [`CommonBridge`](#commonbridge) - - [`OnChainOperator`](#onchainoperator) - - [`Verifier`](#verifier) - - [L2 side](#l2-side) - - [`L1MessageSender`](#l1messagesender) +- [ToC](#toc) +- [L1 side](#l1-side) + - [`CommonBridge`](#commonbridge) + - [`OnChainOperator`](#onchainoperator) + - [`Verifier`](#verifier) +- [L2 side](#l2-side) + - [`L1MessageSender`](#l1messagesender) ## L1 side diff --git a/crates/l2/docs/overview.md b/crates/l2/docs/overview.md index 47bb2bdf0..7fd5cf705 100644 --- a/crates/l2/docs/overview.md +++ b/crates/l2/docs/overview.md @@ -1,6 +1,6 @@ # High Level Docs -This document aims to explain how the Lambda ethrex L2 and all its moving parts work. +This document aims to explain how the Lambda Ethereum Rust L2 and all its moving parts work. ## Intro @@ -23,7 +23,7 @@ Below some answers to these questions, along with an overview of all the moving Now that general purpose `zkVM`s exist, most people have little trouble with the idea that you can prove execution. Just take the usual EVM code you wrote in Rust, compile to some `zkVM` target instead and you're mostly done. You can now prove it. -What's usually less clear is how you prove state. Let's say we want to prove a new L2 block that was just built. Running the `ethrex` `execute_block` function on a Rust `zkVM` does the trick, but that only proves that you ran the VM correctly on **some** previous state/block. How do you know it was the actual previous state of the L2 and not some other, modified one? +What's usually less clear is how you prove state. Let's say we want to prove a new L2 block that was just built. Running the `ethereum_rust` `execute_block` function on a Rust `zkVM` does the trick, but that only proves that you ran the VM correctly on **some** previous state/block. How do you know it was the actual previous state of the L2 and not some other, modified one? In other words, how do you ensure that: @@ -36,7 +36,7 @@ To solve this, we do what we always do: instead of having the actual previous st The flow for the prover is then roughly as follows: - Take as public input the previous block commitment and the next (output) block commitment. -- Execute the current block to prove its execution is valid. Here "execution" means more than just transaction execution; there's also header validation, transaction validation, etc. (essentially all the logic `ethrex` needs to follow when executing and adding a new block to the chain). +- Execute the current block to prove its execution is valid. Here "execution" means more than just transaction execution; there's also header validation, transaction validation, etc. (essentially all the logic `ethereum_rust` needs to follow when executing and adding a new block to the chain). - For every storage slot read, present and verify a merkle path from it to the previous state root (i.e. previous block commitment). - For every storage slot written, present and verify a merkle path from it to the next state root (i.e. next block commitment). @@ -65,7 +65,7 @@ Detailed documentation on the state diffs spec [here](./state_diffs.md). ### How do we prevent the sequencer from publishing the wrong state diffs? -Once again, state diffs have to be part of the public input. With them, the prover can show that they are equal to the ones returned by the VM after executing the block. As always, the actual state diffs are not part of the public input, but **their hash** is, so the size is a fixed 32 bytes. This hash is then part of the block commitment. The prover then assures us that the given state diff hash is correct (i.e. it exactly corresponds to the changes in state of the executed block). +Once again, state diffs have to be part of the public input. With them, the prover can show that they are equal to the ones returned by the VM after executing the block. As always, the actual state diffs are not part of the public input, but **their hash** is, so the size is a fixed 32 bytes. This hash is then part of the block commitment. The prover then assures us that the given state diff hash is correct (i.e. it exactly corresponds to the changes in state of the executed block). There's still a problem however: the L1 contract needs to have the actual state diff for data availability, not just the hash. This is sent as part of calldata of the `commit` transaction (actually later as a blob, we'll get to that), so the sequencer could in theory send the wrong state diff. To make sure this can't happen, the L1 contract hashes it to make sure that it matches the actual state diff hash that is included as part of the public input. @@ -85,7 +85,7 @@ This is important. If you recall, the way the L1 ensured that the state diff pub The solution is through a [proof of equivalence](https://ethresear.ch/t/easy-proof-of-equivalence-between-multiple-polynomial-commitment-schemes-to-the-same-data/8188) between polynomial commitment schemes. The idea is as follows: proofs of equivalence allow you to show that two (polynomial) commitments point to the same underlying data. In our case, we have two commitments: -- The state diff commitment calculated by the sequencer/prover. +- The state diff commitment calculated by the sequencer/prover. - The KZG commitment of the blob sent on the commit transaction (recall that the blob should just be the state diff). If we turn the first one into a polynomial commitment, we can take a random evaluation point through Fiat Shamir and prove that it evaluates to the same value as the KZG blob commitment at that point. The `commit` transaction then sends the blob commitment and, through the point evaluation precompile, verifies that the given blob evaluates to that same value. If it does, the underlying blob is indeed the correct state diff. diff --git a/crates/l2/docs/proposer.md b/crates/l2/docs/proposer.md index 64e12362e..6e6635a5e 100644 --- a/crates/l2/docs/proposer.md +++ b/crates/l2/docs/proposer.md @@ -1,14 +1,13 @@ -# ethrex L2 Proposer +# Ethereum Rust L2 Proposer ## ToC -- [ethrex L2 Proposer](#ethrex-l2-proposer) - - [ToC](#toc) - - [Components](#components) - - [L1 Watcher](#l1-watcher) - - [L1 Transaction Sender](#l1-transaction-sender) - - [Prover Server](#prover-server) - - [Configuration](#configuration) +- [ToC](#toc) +- [Components](#components) + - [L1 Watcher](#l1-watcher) + - [L1 Transaction Sender](#l1-transaction-sender) + - [Prover Server](#prover-server) +- [Configuration](#configuration) ## Components diff --git a/crates/l2/docs/prover.md b/crates/l2/docs/prover.md index 35d70a117..9726c88a3 100644 --- a/crates/l2/docs/prover.md +++ b/crates/l2/docs/prover.md @@ -1,26 +1,25 @@ -# ethrex L2 Prover +# Ethereum Rust L2 Prover ## ToC -- [ethrex L2 Prover](#ethrex-l2-prover) - - [ToC](#toc) - - [What](#what) - - [Workflow](#workflow) - - [How](#how) - - [Dev Mode](#dev-mode) - - [Quick Test](#quick-test) - - [Run the whole system with the prover](#run-the-whole-system-with-the-prover) - - [GPU mode](#gpu-mode) - - [Proving Process Test](#proving-process-test) - - [Run the whole system with the prover in Sepolia](#run-the-whole-system-with-the-prover-in-sepolia) - - [Configuration](#configuration) +- [ToC](#toc) +- [What](#what) +- [Workflow](#workflow) +- [How](#how) + - [Dev Mode](#dev-mode) + - [Quick Test](#quick-test) + - [Run the whole system with the prover](#run-the-whole-system-with-the-prover) + - [GPU mode](#gpu-mode) + - [Proving Process Test](#proving-process-test) + - [Run the whole system with the prover in Sepolia](#run-the-whole-system-with-the-prover-in-sepolia) +- [Configuration](#configuration) >[!NOTE] > The shipping/deploying process and the `Prover` itself are under development. ## What -The prover consists of two main components: handling incoming proving data from the `L2 proposer`, specifically the `prover_server` component, and the `zkVM`. The `prover_client` is responsible for this first part, while the `zkVM` serves as a RISC-V emulator executing code specified in `crates/l2/prover/zkvm/interface/guest/src`. +The prover consists of two main components: handling incoming proving data from the `L2 proposer`, specifically the `prover_server` component, and the `zkVM`. The `prover_client` is responsible for this first part, while the `zkVM` serves as a RISC-V emulator executing code specified in `crates/l2/prover/zkvm/interface/guest/src`. Before the `zkVM` code (or guest), there is a directory called `interface`, which indicates that we access the `zkVM` through the "interface" crate. In summary, the `prover_client` manages the inputs from the `prover_server` and then "calls" the `zkVM` to perform the proving process and generate the `groth16` ZK proof. @@ -116,7 +115,7 @@ make perf_gpu Two servers are required: one for the `prover` and another for the `proposer`. If you run both components on the same machine, the `prover` may consume all available resources, leading to potential stuttering or performance issues for the `proposer`/`node`. 1. `prover`/`zkvm` → prover with gpu, make sure to have all the required dependencies described at the beginning of [Gpu Mode](#gpu-mode) section. - 1. `cd lambda_ethrex/crates/l2` + 1. `cd lambda_ethereum_rust/crates/l2` 2. `cp .example.env` and change the `PROVER_CLIENT_PROVER_SERVER_ENDPOINT` with the ip of the other server. The env variables needed are: @@ -131,7 +130,7 @@ Finally, to start the `prover_client`/`zkvm`, run: - `make init-l2-prover-gpu` 2.  `proposer` → this server just needs rust installed. - 1. `cd lambda_ethrex/crates/l2` + 1. `cd lambda_ethereum_rust/crates/l2` 2. `cp .example.env` and change the addresses and the following fields: - `PROVER_SERVER_LISTEN_IP=0.0.0.0` → used to handle the tcp communication with the other server. - The `COMMITTER` and `PROVER_SERVER_VERIFIER` must be different accounts, the `DEPLOYER_ADDRESS` as well as the `L1_WATCHER` may be the same account used by the `COMMITTER` diff --git a/crates/l2/proposer/errors.rs b/crates/l2/proposer/errors.rs index bf469f701..df37f122c 100644 --- a/crates/l2/proposer/errors.rs +++ b/crates/l2/proposer/errors.rs @@ -1,9 +1,8 @@ use crate::utils::{config::errors::ConfigError, eth_client::errors::EthClientError}; +use ethereum_rust_dev::utils::engine_client::errors::EngineClientError; +use ethereum_rust_storage::error::StoreError; +use ethereum_rust_vm::EvmError; use ethereum_types::FromStrRadixErr; -use ethrex_core::types::BlobsBundleError; -use ethrex_dev::utils::engine_client::errors::EngineClientError; -use ethrex_storage::error::StoreError; -use ethrex_vm::EvmError; #[derive(Debug, thiserror::Error)] pub enum L1WatcherError { @@ -49,8 +48,6 @@ pub enum CommitterError { FailedToParseLastCommittedBlock(#[from] FromStrRadixErr), #[error("Committer failed retrieve block from storage: {0}")] FailedToRetrieveBlockFromStorage(#[from] StoreError), - #[error("Committer failed to generate blobs bundle: {0}")] - FailedToGenerateBlobsBundle(#[from] BlobsBundleError), #[error("Committer failed to get information from storage")] FailedToGetInformationFromStorage(String), #[error("Committer failed to encode state diff: {0}")] @@ -59,6 +56,8 @@ pub enum CommitterError { FailedToOpenPointsFile(#[from] std::io::Error), #[error("Committer failed to re-execute block: {0}")] FailedToReExecuteBlock(#[from] EvmError), + #[error("Committer failed to make KZG operations: {0}")] + KZGError(#[from] c_kzg::Error), #[error("Committer failed to send transaction: {0}")] FailedToSendCommitment(String), } diff --git a/crates/l2/proposer/l1_committer.rs b/crates/l2/proposer/l1_committer.rs index 384cecda9..1a08733b3 100644 --- a/crates/l2/proposer/l1_committer.rs +++ b/crates/l2/proposer/l1_committer.rs @@ -5,24 +5,28 @@ use crate::{ }, utils::{ config::{committer::CommitterConfig, eth::EthConfig}, - eth_client::{errors::EthClientError, eth_sender::Overrides, EthClient}, + eth_client::{ + errors::EthClientError, eth_sender::Overrides, transaction::blob_from_bytes, EthClient, + }, merkle_tree::merkelize, }, }; use bytes::Bytes; -use ethrex_blockchain::constants::TX_GAS_COST; -use ethrex_core::{ +use c_kzg::{Bytes48, KzgSettings}; +use ethereum_rust_blockchain::constants::TX_GAS_COST; +use ethereum_rust_core::{ types::{ - blobs_bundle, BlobsBundle, Block, EIP1559Transaction, GenericTransaction, - PrivilegedL2Transaction, PrivilegedTxType, Transaction, TxKind, + BlobsBundle, Block, EIP1559Transaction, GenericTransaction, PrivilegedL2Transaction, + PrivilegedTxType, Transaction, TxKind, BYTES_PER_BLOB, }, Address, H256, U256, }; -use ethrex_rpc::types::transaction::WrappedEIP4844Transaction; -use ethrex_storage::Store; -use ethrex_vm::{evm_state, execute_block, get_state_transitions}; +use ethereum_rust_rpc::types::transaction::WrappedEIP4844Transaction; +use ethereum_rust_storage::Store; +use ethereum_rust_vm::{evm_state, execute_block, get_state_transitions}; use keccak_hash::keccak; use secp256k1::SecretKey; +use sha2::{Digest, Sha256}; use std::ops::Div; use std::{collections::HashMap, time::Duration}; use tokio::time::sleep; @@ -37,6 +41,7 @@ pub struct Committer { l1_address: Address, l1_private_key: SecretKey, interval_ms: u64, + kzg_settings: &'static KzgSettings, } pub async fn start_l1_commiter(store: Store) { @@ -59,6 +64,7 @@ impl Committer { l1_address: committer_config.l1_address, l1_private_key: committer_config.l1_private_key, interval_ms: committer_config.interval_ms, + kzg_settings: c_kzg::ethereum_kzg_settings(), } } @@ -117,7 +123,8 @@ impl Committer { deposits, )?; - let blobs_bundle = self.generate_blobs_bundle(state_diff.clone())?; + let (blob_commitment, blob_proof) = + self.prepare_blob_commitment(state_diff.clone())?; let head_block_hash = block_to_commit.hash(); match self @@ -125,7 +132,9 @@ impl Committer { block_to_commit.header.number, withdrawal_logs_merkle_root, deposit_logs_hash, - blobs_bundle, + blob_commitment, + blob_proof, + state_diff.encode()?, ) .await { @@ -273,16 +282,30 @@ impl Committer { Ok(state_diff) } - /// Generate the blob bundle necessary for the EIP-4844 transaction. - pub fn generate_blobs_bundle( + /// Generate the KZG commitment and proof for the blob. This commitment can then be used + /// to calculate the blob versioned hash, necessary for the EIP-4844 transaction. + pub fn prepare_blob_commitment( &self, state_diff: StateDiff, - ) -> Result { + ) -> Result<([u8; 48], [u8; 48]), CommitterError> { let blob_data = state_diff.encode().map_err(CommitterError::from)?; - let blob = blobs_bundle::blob_from_bytes(blob_data).map_err(CommitterError::from)?; + let blob = blob_from_bytes(blob_data).map_err(CommitterError::from)?; - BlobsBundle::create_from_blobs(&vec![blob]).map_err(CommitterError::from) + let commitment = c_kzg::KzgCommitment::blob_to_kzg_commitment(&blob, self.kzg_settings) + .map_err(CommitterError::from)?; + let commitment_bytes = + Bytes48::from_bytes(commitment.as_slice()).map_err(CommitterError::from)?; + let proof = + c_kzg::KzgProof::compute_blob_kzg_proof(&blob, &commitment_bytes, self.kzg_settings) + .map_err(CommitterError::from)?; + + let mut commitment_bytes = [0u8; 48]; + commitment_bytes.copy_from_slice(commitment.as_slice()); + let mut proof_bytes = [0u8; 48]; + proof_bytes.copy_from_slice(proof.as_slice()); + + Ok((commitment_bytes, proof_bytes)) } pub async fn send_commitment( @@ -290,26 +313,39 @@ impl Committer { block_number: u64, withdrawal_logs_merkle_root: H256, deposit_logs_hash: H256, - blobs_bundle: BlobsBundle, + commitment: [u8; 48], + proof: [u8; 48], + blob_data: Bytes, ) -> Result { info!("Sending commitment for block {block_number}"); + let mut hasher = Sha256::new(); + hasher.update(commitment); + let mut blob_versioned_hash = hasher.finalize(); + blob_versioned_hash[0] = 0x01; // EIP-4844 versioning + let mut calldata = Vec::with_capacity(132); calldata.extend(COMMIT_FUNCTION_SELECTOR); let mut block_number_bytes = [0_u8; 32]; U256::from(block_number).to_big_endian(&mut block_number_bytes); calldata.extend(block_number_bytes); - - let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); - // We only actually support one versioned hash on the onChainProposer for now, - // but eventually this should work if we start sending multiple blobs per commit operation. - for blob_versioned_hash in blob_versioned_hashes { - let blob_versioned_hash_bytes = blob_versioned_hash.to_fixed_bytes(); - calldata.extend(blob_versioned_hash_bytes); - } + calldata.extend(blob_versioned_hash); calldata.extend(withdrawal_logs_merkle_root.0); calldata.extend(deposit_logs_hash.0); + let mut buf = [0u8; BYTES_PER_BLOB]; + buf.copy_from_slice( + blob_from_bytes(blob_data) + .map_err(CommitterError::from)? + .iter() + .as_slice(), + ); + + let blobs_bundle = BlobsBundle { + blobs: vec![buf], + commitments: vec![commitment], + proofs: vec![proof], + }; let wrapped_tx = self .eth_client .build_eip4844_transaction( diff --git a/crates/l2/proposer/l1_watcher.rs b/crates/l2/proposer/l1_watcher.rs index 73d7290e8..ed3aaa8fe 100644 --- a/crates/l2/proposer/l1_watcher.rs +++ b/crates/l2/proposer/l1_watcher.rs @@ -6,13 +6,12 @@ use crate::{ }, }; use bytes::Bytes; +use ethereum_rust_blockchain::{constants::TX_GAS_COST, mempool}; +use ethereum_rust_core::types::PrivilegedTxType; +use ethereum_rust_core::types::{Signable, Transaction}; +use ethereum_rust_rpc::types::receipt::RpcLog; +use ethereum_rust_storage::Store; use ethereum_types::{Address, BigEndianHash, H256, U256}; -use ethrex_blockchain::{constants::TX_GAS_COST, mempool}; -use ethrex_core::types::PrivilegedTxType; -use ethrex_core::types::{Signable, Transaction}; -use ethrex_rpc::types::receipt::RpcLog; -use ethrex_storage::Store; -use keccak_hash::keccak; use secp256k1::SecretKey; use std::{cmp::min, ops::Mul, time::Duration}; use tokio::time::sleep; @@ -21,31 +20,11 @@ use tracing::{debug, info, warn}; pub async fn start_l1_watcher(store: Store) { let eth_config = EthConfig::from_env().expect("EthConfig::from_env()"); let watcher_config = L1WatcherConfig::from_env().expect("L1WatcherConfig::from_env()"); - let sleep_duration = Duration::from_millis(watcher_config.check_interval_ms); let mut l1_watcher = L1Watcher::new_from_config(watcher_config, eth_config); loop { - sleep(sleep_duration).await; - - let logs = match l1_watcher.get_logs().await { - Ok(logs) => logs, - Err(error) => { - warn!("Error when getting logs from L1: {}", error); - continue; - } - }; - if logs.is_empty() { - continue; - } - - let pending_deposits_logs = match l1_watcher.get_pending_deposit_logs().await { - Ok(logs) => logs, - Err(error) => { - warn!("Error when getting L1 pending deposit logs: {}", error); - continue; - } - }; + let logs = l1_watcher.get_logs().await.expect("l1_watcher.get_logs()"); let _deposit_txs = l1_watcher - .process_logs(logs, &pending_deposits_logs, &store) + .process_logs(logs, &store) .await .expect("l1_watcher.process_logs()"); } @@ -55,9 +34,11 @@ pub struct L1Watcher { eth_client: EthClient, address: Address, topics: Vec, + check_interval: Duration, max_block_step: U256, last_block_fetched: U256, l2_proposer_pk: SecretKey, + l2_proposer_address: Address, } impl L1Watcher { @@ -66,32 +47,14 @@ impl L1Watcher { eth_client: EthClient::new_from_config(eth_config), address: watcher_config.bridge_address, topics: watcher_config.topics, + check_interval: Duration::from_millis(watcher_config.check_interval_ms), max_block_step: watcher_config.max_block_step, last_block_fetched: U256::zero(), l2_proposer_pk: watcher_config.l2_proposer_private_key, + l2_proposer_address: watcher_config.l2_proposer_address, } } - pub async fn get_pending_deposit_logs(&self) -> Result, L1WatcherError> { - Ok(hex::decode( - &self - .eth_client - .call( - self.address, - Bytes::copy_from_slice(&[0x35, 0x6d, 0xa2, 0x49]), - Overrides::default(), - ) - .await?[2..], - ) - .map_err(|_| L1WatcherError::FailedToDeserializeLog("Not a valid hex string".to_string()))? - .chunks(32) - .map(H256::from_slice) - .collect::>() - .split_at(2) // Two first words are index and length abi encode - .1 - .to_vec()) - } - pub async fn get_logs(&mut self) -> Result, L1WatcherError> { let current_block = self.eth_client.get_block_number().await?; @@ -128,25 +91,25 @@ impl L1Watcher { self.last_block_fetched = new_last_block; + sleep(self.check_interval).await; + Ok(logs) } pub async fn process_logs( &self, logs: Vec, - l1_deposit_logs: &[H256], store: &Store, ) -> Result, L1WatcherError> { + if logs.is_empty() { + return Ok(Vec::new()); + } + let mut deposit_txs = Vec::new(); let mut operator_nonce = store .get_account_info( - store - .get_latest_block_number() - .map_err(|e| L1WatcherError::FailedToRetrieveChainConfig(e.to_string()))? - .ok_or(L1WatcherError::FailedToRetrieveChainConfig( - "Last block is None".to_string(), - ))?, - Address::zero(), + self.eth_client.get_block_number().await?.as_u64(), + self.l2_proposer_address, ) .map_err(|e| L1WatcherError::FailedToRetrieveDepositorAccountInfo(e.to_string()))? .map(|info| info.nonce) @@ -168,13 +131,6 @@ impl L1Watcher { )) })?; - let mut value_bytes = [0u8; 32]; - mint_value.to_big_endian(&mut value_bytes); - if !l1_deposit_logs.contains(&keccak([beneficiary.as_bytes(), &value_bytes].concat())) { - warn!("Deposit already processed (to: {beneficiary:#x}, value: {mint_value}), skipping."); - continue; - } - info!("Initiating mint transaction for {beneficiary:#x} with value {mint_value:#x}",); let mut mint_transaction = self diff --git a/crates/l2/proposer/mod.rs b/crates/l2/proposer/mod.rs index 5c6c435c9..e146eaeb7 100644 --- a/crates/l2/proposer/mod.rs +++ b/crates/l2/proposer/mod.rs @@ -1,8 +1,8 @@ use crate::utils::config::{proposer::ProposerConfig, read_env_file}; use errors::ProposerError; +use ethereum_rust_dev::utils::engine_client::{config::EngineApiConfig, errors::EngineClientError}; +use ethereum_rust_storage::Store; use ethereum_types::{Address, H256}; -use ethrex_dev::utils::engine_client::{config::EngineApiConfig, errors::EngineClientError}; -use ethrex_storage::Store; use tracing::{info, warn}; pub mod l1_committer; @@ -64,7 +64,7 @@ impl Proposer { } pub async fn start(&self, head_block_hash: H256) -> Result<(), ProposerError> { - ethrex_dev::block_producer::start_block_producer( + ethereum_rust_dev::block_producer::start_block_producer( self.engine_config.rpc_url.clone(), std::fs::read(&self.engine_config.jwt_path).unwrap().into(), head_block_hash, diff --git a/crates/l2/proposer/prover_server.rs b/crates/l2/proposer/prover_server.rs index ffc2a18ed..0190dd7ef 100644 --- a/crates/l2/proposer/prover_server.rs +++ b/crates/l2/proposer/prover_server.rs @@ -1,5 +1,5 @@ -use ethrex_storage::Store; -use ethrex_vm::execution_db::ExecutionDB; +use ethereum_rust_storage::Store; +use ethereum_rust_vm::execution_db::ExecutionDB; use keccak_hash::keccak; use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; @@ -16,7 +16,7 @@ use tokio::{ }; use tracing::{debug, error, info, warn}; -use ethrex_core::{ +use ethereum_rust_core::{ types::{Block, BlockHeader, EIP1559Transaction}, Address, H256, }; @@ -105,9 +105,6 @@ pub async fn start_prover_server(store: Store) { .await .unwrap(); - info!("Sending verify transaction with tx hash: {tx_hash:#x}"); - - let mut retries = 1; while eth_client .get_transaction_receipt(tx_hash) .await @@ -115,17 +112,9 @@ pub async fn start_prover_server(store: Store) { .is_none() { thread::sleep(Duration::from_secs(1)); - retries += 1; - if retries > 10 { - error!("Couldn't find receipt for transaction {tx_hash:#x}"); - panic!("Couldn't find receipt for transaction {tx_hash:#x}"); - } } - info!( - "Mocked verify transaction sent for block {}", - last_verified_block + 1 - ); + info!("Mocked verify transaction sent"); } } else { let mut prover_server = ProverServer::new_from_config( diff --git a/crates/l2/prover/Cargo.toml b/crates/l2/prover/Cargo.toml index e3274f200..3c93e46b5 100644 --- a/crates/l2/prover/Cargo.toml +++ b/crates/l2/prover/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex-prover" +name = "ethereum_rust-prover" version.workspace = true edition.workspace = true @@ -14,19 +14,19 @@ tracing-subscriber = { workspace = true, features = ["env-filter"] } tracing.workspace = true hex.workspace = true -# ethrex -ethrex-core.workspace = true -ethrex-vm.workspace = true -ethrex-rlp.workspace = true +# ethereum_rust +ethereum_rust-core.workspace = true +ethereum_rust-vm.workspace = true +ethereum_rust-rlp.workspace = true # l2 -ethrex-l2.workspace = true +ethereum_rust-l2.workspace = true # risc0 zkvm_interface = { path = "./zkvm/interface", default-features = false } risc0-zkvm = { version = "1.1.2" } -# revm (this dep is temporary, should be replaced with ethrex-vm after ExecutionDB is completely integrated into the L1) +# revm (this dep is temporary, should be replaced with ethereum_rust-vm after ExecutionDB is completely integrated into the L1) revm = { version = "14.0.3", features = [ "std", "serde", @@ -34,16 +34,16 @@ revm = { version = "14.0.3", features = [ ], default-features = false } [dev-dependencies] -ethrex-vm.workspace = true -ethrex-storage.workspace = true -ethrex-blockchain.workspace = true +ethereum_rust-vm.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-blockchain.workspace = true [lib] -name = "ethrex_prover_lib" +name = "ethereum_rust_prover_lib" path = "src/lib.rs" [[bin]] -name = "ethrex_prover" +name = "ethereum_rust_prover" path = "src/main.rs" [features] diff --git a/crates/l2/prover/src/lib.rs b/crates/l2/prover/src/lib.rs index ee528391a..8e81668f6 100644 --- a/crates/l2/prover/src/lib.rs +++ b/crates/l2/prover/src/lib.rs @@ -1,7 +1,7 @@ pub mod prover; pub mod prover_client; -use ethrex_l2::utils::config::prover_client::ProverClientConfig; +use ethereum_rust_l2::utils::config::prover_client::ProverClientConfig; use tracing::warn; pub async fn init_client(config: ProverClientConfig) { diff --git a/crates/l2/prover/src/main.rs b/crates/l2/prover/src/main.rs index 7c99f88bb..403c65d04 100644 --- a/crates/l2/prover/src/main.rs +++ b/crates/l2/prover/src/main.rs @@ -1,5 +1,5 @@ -use ethrex_l2::utils::config::{prover_client::ProverClientConfig, read_env_file}; -use ethrex_prover_lib::init_client; +use ethereum_rust_l2::utils::config::{prover_client::ProverClientConfig, read_env_file}; +use ethereum_rust_prover_lib::init_client; use tracing::{self, debug, warn, Level}; diff --git a/crates/l2/prover/src/prover.rs b/crates/l2/prover/src/prover.rs index 45328188d..05e8fc90b 100644 --- a/crates/l2/prover/src/prover.rs +++ b/crates/l2/prover/src/prover.rs @@ -6,12 +6,12 @@ use zkvm_interface::methods::{ZKVM_PROGRAM_ELF, ZKVM_PROGRAM_ID}; use risc0_zkvm::{default_prover, ExecutorEnv, ExecutorEnvBuilder, ProverOpts}; -use ethrex_core::types::Receipt; -use ethrex_l2::{ +use ethereum_rust_core::types::Receipt; +use ethereum_rust_l2::{ proposer::prover_server::ProverInputData, utils::config::prover_client::ProverClientConfig, }; -use ethrex_rlp::encode::RLPEncode; -use ethrex_vm::execution_db::ExecutionDB; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_vm::execution_db::ExecutionDB; // The order of variables in this structure should match the order in which they were // committed in the zkVM, with each variable represented by a field. diff --git a/crates/l2/prover/src/prover_client.rs b/crates/l2/prover/src/prover_client.rs index 8c297109c..21626b9ac 100644 --- a/crates/l2/prover/src/prover_client.rs +++ b/crates/l2/prover/src/prover_client.rs @@ -7,7 +7,7 @@ use std::{ use tokio::time::sleep; use tracing::{debug, error, info, warn}; -use ethrex_l2::{ +use ethereum_rust_l2::{ proposer::prover_server::{ProofData, ProverInputData}, utils::config::prover_client::ProverClientConfig, }; diff --git a/crates/l2/prover/tests/perf_zkvm.rs b/crates/l2/prover/tests/perf_zkvm.rs index c4ac54cd1..7fdec57d3 100644 --- a/crates/l2/prover/tests/perf_zkvm.rs +++ b/crates/l2/prover/tests/perf_zkvm.rs @@ -1,30 +1,37 @@ -use std::path::Path; +use std::path::PathBuf; use tracing::info; -use ethrex_blockchain::add_block; -use ethrex_l2::proposer::prover_server::ProverInputData; -use ethrex_prover_lib::prover::Prover; -use ethrex_storage::{EngineType, Store}; -use ethrex_vm::execution_db::ExecutionDB; +use ethereum_rust_blockchain::add_block; +use ethereum_rust_l2::proposer::prover_server::ProverInputData; +use ethereum_rust_prover_lib::prover::Prover; +use ethereum_rust_storage::{EngineType, Store}; +use ethereum_rust_vm::execution_db::ExecutionDB; #[tokio::test] async fn test_performance_zkvm() { tracing_subscriber::fmt::init(); - let path = Path::new(concat!(env!("CARGO_MANIFEST_DIR"), "/../../../test_data")); + let mut path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + // Go back 3 levels (Go to the root of the project) + for _ in 0..3 { + path.pop(); + } + path.push("test_data"); // Another use is genesis-execution-api.json in conjunction with chain.rlp(20 blocks not too loaded). - let genesis_file_path = path.join("genesis-l2-old.json"); + let genesis_file_path = path.join("genesis-l2.json"); // l2-loadtest.rlp has blocks with many txs. let chain_file_path = path.join("l2-loadtest.rlp"); let store = Store::new("memory", EngineType::InMemory).expect("Failed to create Store"); - let genesis = - ethrex_l2::utils::test_data_io::read_genesis_file(genesis_file_path.to_str().unwrap()); + let genesis = ethereum_rust_l2::utils::test_data_io::read_genesis_file( + genesis_file_path.to_str().unwrap(), + ); store.add_initial_state(genesis.clone()).unwrap(); - let blocks = ethrex_l2::utils::test_data_io::read_chain_file(chain_file_path.to_str().unwrap()); + let blocks = + ethereum_rust_l2::utils::test_data_io::read_chain_file(chain_file_path.to_str().unwrap()); info!("Number of blocks to insert: {}", blocks.len()); for block in &blocks { diff --git a/crates/l2/prover/zkvm/interface/Cargo.toml b/crates/l2/prover/zkvm/interface/Cargo.toml index 3e89bdf33..2516c1005 100644 --- a/crates/l2/prover/zkvm/interface/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" serde = { version = "1.0", default-features = false, features = ["derive"] } thiserror = "1.0.64" -ethrex-storage = { path = "../../../../storage/store" } +ethereum_rust-storage = { path = "../../../../storage/store" } # revm revm = { version = "14.0.3", features = [ diff --git a/crates/l2/prover/zkvm/interface/guest/Cargo.toml b/crates/l2/prover/zkvm/interface/guest/Cargo.toml index 19a0c440a..1d1b5616f 100644 --- a/crates/l2/prover/zkvm/interface/guest/Cargo.toml +++ b/crates/l2/prover/zkvm/interface/guest/Cargo.toml @@ -8,10 +8,10 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "1.1.2", default-features = false, features = ["std"] } -ethrex-core = { path = "../../../../../common", default-features = false } -ethrex-rlp = { path = "../../../../../common/rlp" } -ethrex-vm = { path = "../../../../../vm", default-features = false } -ethrex-blockchain = { path = "../../../../../blockchain", default-features = false } +ethereum_rust-core = { path = "../../../../../common", default-features = false } +ethereum_rust-rlp = { path = "../../../../../common/rlp" } +ethereum_rust-vm = { path = "../../../../../vm", default-features = false } +ethereum_rust-blockchain = { path = "../../../../../blockchain", default-features = false } [build-dependencies] ## cc version ^1.1.31 breaks the compilation. diff --git a/crates/l2/prover/zkvm/interface/guest/src/main.rs b/crates/l2/prover/zkvm/interface/guest/src/main.rs index 425729ff7..12f608134 100644 --- a/crates/l2/prover/zkvm/interface/guest/src/main.rs +++ b/crates/l2/prover/zkvm/interface/guest/src/main.rs @@ -1,9 +1,9 @@ -use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError}; +use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError}; use risc0_zkvm::guest::env; -use ethrex_blockchain::{validate_block, validate_gas_used}; -use ethrex_core::types::{Block, BlockHeader}; -use ethrex_vm::{execute_block, execution_db::ExecutionDB, get_state_transitions, EvmState}; +use ethereum_rust_blockchain::{validate_block, validate_gas_used}; +use ethereum_rust_core::types::{Block, BlockHeader}; +use ethereum_rust_vm::{execute_block, execution_db::ExecutionDB, get_state_transitions, EvmState}; fn main() { let (block, execution_db, parent_header) = read_inputs().expect("failed to read inputs"); diff --git a/crates/l2/sdk/Cargo.toml b/crates/l2/sdk/Cargo.toml index c4c937e09..76f61c4a8 100644 --- a/crates/l2/sdk/Cargo.toml +++ b/crates/l2/sdk/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "ethrex-sdk" +name = "ethereum_rust-sdk" version.workspace = true edition.workspace = true [dependencies] -ethrex-l2.workspace = true -ethrex-core.workspace = true -ethrex-rpc.workspace = true +ethereum_rust-l2.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-rpc.workspace = true ethereum-types.workspace = true tokio.workspace = true @@ -16,5 +16,5 @@ secp256k1.workspace = true itertools = "0.13.0" [lib] -name = "ethrex_l2_sdk" +name = "ethereum_rust_l2_sdk" path = "src/sdk.rs" diff --git a/crates/l2/sdk/src/sdk.rs b/crates/l2/sdk/src/sdk.rs index ee7d472a5..145a9cf47 100644 --- a/crates/l2/sdk/src/sdk.rs +++ b/crates/l2/sdk/src/sdk.rs @@ -1,6 +1,5 @@ -use ethereum_types::{Address, H160, H256, U256}; -use ethrex_core::types::{PrivilegedTxType, Transaction}; -use ethrex_l2::utils::{ +use ethereum_rust_core::types::{PrivilegedTxType, Transaction}; +use ethereum_rust_l2::utils::{ eth_client::{ errors::{EthClientError, GetTransactionReceiptError}, eth_sender::Overrides, @@ -8,7 +7,8 @@ use ethrex_l2::utils::{ }, merkle_tree::merkle_proof, }; -use ethrex_rpc::types::{block::BlockBodyWrapper, receipt::RpcReceipt}; +use ethereum_rust_rpc::types::{block::BlockBodyWrapper, receipt::RpcReceipt}; +use ethereum_types::{Address, H160, H256, U256}; use itertools::Itertools; use keccak_hash::keccak; use secp256k1::SecretKey; diff --git a/crates/l2/tests/tests.rs b/crates/l2/tests/tests.rs index a4b53931b..081f73b39 100644 --- a/crates/l2/tests/tests.rs +++ b/crates/l2/tests/tests.rs @@ -1,6 +1,6 @@ use bytes::Bytes; +use ethereum_rust_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use ethereum_types::{Address, H160, U256}; -use ethrex_l2::utils::eth_client::{eth_sender::Overrides, EthClient}; use keccak_hash::H256; use secp256k1::SecretKey; use std::{str::FromStr, time::Duration}; @@ -59,7 +59,7 @@ async fn testito() { println!("Depositing funds from L1 to L2"); let deposit_value = U256::from(1000000000000000000000u128); - let deposit_tx = ethrex_l2_sdk::deposit( + let deposit_tx = ethereum_rust_l2_sdk::deposit( deposit_value, l1_rich_wallet_address(), l1_rich_wallet_private_key(), @@ -71,7 +71,7 @@ async fn testito() { println!("Waiting for deposit transaction receipt"); let _deposit_tx_receipt = - ethrex_l2_sdk::wait_for_transaction_receipt(deposit_tx, ð_client, 5).await; + ethereum_rust_l2_sdk::wait_for_transaction_receipt(deposit_tx, ð_client, 5).await; // 3. Check balances on L1 and L2 @@ -131,7 +131,7 @@ async fn testito() { .unwrap(); assert!(l2_random_account_initial_balance.is_zero()); let transfer_value = U256::from(10000000000u128); - let transfer_tx = ethrex_l2_sdk::transfer( + let transfer_tx = ethereum_rust_l2_sdk::transfer( transfer_value, l1_rich_wallet_address(), random_account_address, @@ -141,7 +141,7 @@ async fn testito() { .await .unwrap(); let _transfer_tx_receipt = - ethrex_l2_sdk::wait_for_transaction_receipt(transfer_tx, &proposer_client, 30).await; + ethereum_rust_l2_sdk::wait_for_transaction_receipt(transfer_tx, &proposer_client, 30).await; // 5. Check balances on L2 @@ -176,7 +176,7 @@ async fn testito() { println!("Withdrawing funds from L2 to L1"); let withdraw_value = U256::from(100000000000000000000u128); - let withdraw_tx = ethrex_l2_sdk::withdraw( + let withdraw_tx = ethereum_rust_l2_sdk::withdraw( withdraw_value, l1_rich_wallet_address(), l1_rich_wallet_private_key(), @@ -185,7 +185,7 @@ async fn testito() { .await .unwrap(); let withdraw_tx_receipt = - ethrex_l2_sdk::wait_for_transaction_receipt(withdraw_tx, &proposer_client, 30).await; + ethereum_rust_l2_sdk::wait_for_transaction_receipt(withdraw_tx, &proposer_client, 30).await; // 7. Check balances on L1 and L2 @@ -240,7 +240,7 @@ async fn testito() { std::thread::sleep(Duration::from_secs(2)); } - let claim_tx = ethrex_l2_sdk::claim_withdraw( + let claim_tx = ethereum_rust_l2_sdk::claim_withdraw( withdraw_tx, withdraw_value, l1_rich_wallet_address(), @@ -252,7 +252,7 @@ async fn testito() { .unwrap(); let _claim_tx_receipt = - ethrex_l2_sdk::wait_for_transaction_receipt(claim_tx, ð_client, 15).await; + ethereum_rust_l2_sdk::wait_for_transaction_receipt(claim_tx, ð_client, 15).await; // 9. Check balances on L1 and L2 diff --git a/crates/l2/utils/config/l1_watcher.rs b/crates/l2/utils/config/l1_watcher.rs index 0b679aedd..00a34a3f5 100644 --- a/crates/l2/utils/config/l1_watcher.rs +++ b/crates/l2/utils/config/l1_watcher.rs @@ -13,6 +13,7 @@ pub struct L1WatcherConfig { pub max_block_step: U256, #[serde(deserialize_with = "secret_key_deserializer")] pub l2_proposer_private_key: SecretKey, + pub l2_proposer_address: Address, } impl L1WatcherConfig { diff --git a/crates/l2/utils/eth_client/errors.rs b/crates/l2/utils/eth_client/errors.rs index 99287d03f..baa885332 100644 --- a/crates/l2/utils/eth_client/errors.rs +++ b/crates/l2/utils/eth_client/errors.rs @@ -1,4 +1,4 @@ -use ethrex_rpc::utils::RpcRequest; +use ethereum_rust_rpc::utils::RpcRequest; #[derive(Debug, thiserror::Error)] pub enum EthClientError { diff --git a/crates/l2/utils/eth_client/eth_sender.rs b/crates/l2/utils/eth_client/eth_sender.rs index e8c38e5d7..16969aeb6 100644 --- a/crates/l2/utils/eth_client/eth_sender.rs +++ b/crates/l2/utils/eth_client/eth_sender.rs @@ -3,10 +3,10 @@ use crate::utils::eth_client::{ EthClient, RpcResponse, }; use bytes::Bytes; +use ethereum_rust_core::types::{GenericTransaction, TxKind}; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_rpc::utils::{RpcRequest, RpcRequestId}; use ethereum_types::{Address, U256}; -use ethrex_core::types::{GenericTransaction, TxKind}; -use ethrex_rlp::encode::RLPEncode; -use ethrex_rpc::utils::{RpcRequest, RpcRequestId}; use keccak_hash::{keccak, H256}; use secp256k1::SecretKey; use serde_json::json; diff --git a/crates/l2/utils/eth_client/mod.rs b/crates/l2/utils/eth_client/mod.rs index 6d504f3e0..467a61f21 100644 --- a/crates/l2/utils/eth_client/mod.rs +++ b/crates/l2/utils/eth_client/mod.rs @@ -6,13 +6,12 @@ use errors::{ GetTransactionReceiptError, SendRawTransactionError, }; use eth_sender::Overrides; -use ethereum_types::{Address, H256, U256}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ BlobsBundle, EIP1559Transaction, EIP4844Transaction, GenericTransaction, PrivilegedL2Transaction, PrivilegedTxType, Signable, TxKind, TxType, }; -use ethrex_rlp::encode::RLPEncode; -use ethrex_rpc::{ +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_rpc::{ types::{ block::RpcBlock, receipt::{RpcLog, RpcReceipt}, @@ -20,14 +19,17 @@ use ethrex_rpc::{ }, utils::{RpcErrorResponse, RpcRequest, RpcRequestId, RpcSuccessResponse}, }; +use ethereum_types::{Address, H256, U256}; use keccak_hash::keccak; use reqwest::Client; use secp256k1::SecretKey; use serde::{Deserialize, Serialize}; use serde_json::json; +use sha2::{Digest, Sha256}; pub mod errors; pub mod eth_sender; +pub mod transaction; #[derive(Deserialize, Debug)] #[serde(untagged)] @@ -445,7 +447,17 @@ impl EthClient { overrides: Overrides, blobs_bundle: BlobsBundle, ) -> Result { - let blob_versioned_hashes = blobs_bundle.generate_versioned_hashes(); + let blob_versioned_hashes = blobs_bundle + .commitments + .iter() + .map(|commitment| { + let mut hasher = Sha256::new(); + hasher.update(commitment); + let mut blob_versioned_hash = hasher.finalize(); + blob_versioned_hash[0] = 0x01; // EIP-4844 versioning + H256::from_slice(blob_versioned_hash.as_slice()) + }) + .collect::>(); let mut tx = EIP4844Transaction { to, @@ -618,15 +630,15 @@ impl EthClient { #[derive(Serialize, Deserialize, Debug)] #[serde(rename_all = "camelCase")] pub struct GetTransactionByHashTransaction { - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub chain_id: u64, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub nonce: u64, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub max_priority_fee_per_gas: u64, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub max_fee_per_gas: u64, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub gas_limit: u64, #[serde(default)] pub to: Address, @@ -640,9 +652,9 @@ pub struct GetTransactionByHashTransaction { pub r#type: TxType, #[serde(default)] pub signature_y_parity: bool, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub signature_r: u64, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub signature_s: u64, #[serde(default)] pub block_number: U256, @@ -652,6 +664,6 @@ pub struct GetTransactionByHashTransaction { pub from: Address, #[serde(default)] pub hash: H256, - #[serde(default, with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(default, with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub transaction_index: u64, } diff --git a/crates/l2/utils/eth_client/transaction.rs b/crates/l2/utils/eth_client/transaction.rs new file mode 100644 index 000000000..212538857 --- /dev/null +++ b/crates/l2/utils/eth_client/transaction.rs @@ -0,0 +1,24 @@ +use bytes::Bytes; +use c_kzg::{Blob, BYTES_PER_BLOB}; + +pub fn blob_from_bytes(bytes: Bytes) -> Result { + // We set the first byte of every 32-bytes chunk to 0x00 + // so it's always under the field module. + if bytes.len() > BYTES_PER_BLOB * 31 / 32 { + return Err(c_kzg::Error::InvalidBytesLength(format!( + "Bytes too long for a Blob ({})", + bytes.len() + ))); + } + + let mut buf = [0u8; BYTES_PER_BLOB]; + buf[..(bytes.len() * 32).div_ceil(31)].copy_from_slice( + &bytes + .chunks(31) + .map(|x| [&[0x00], x].concat()) + .collect::>() + .concat(), + ); + + Blob::from_bytes(&buf) +} diff --git a/crates/l2/utils/test_data_io.rs b/crates/l2/utils/test_data_io.rs index 782b0b7e0..dcc7833ca 100644 --- a/crates/l2/utils/test_data_io.rs +++ b/crates/l2/utils/test_data_io.rs @@ -1,6 +1,6 @@ -use ethrex_core::types::{Block, Genesis}; -use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; -use ethrex_storage::Store; +use ethereum_rust_core::types::{Block, Genesis}; +use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethereum_rust_storage::Store; use tracing::info; use std::{ @@ -9,13 +9,13 @@ use std::{ path::PathBuf, }; -// From cmd/ethrex +// From cmd/ethereum_rust pub fn read_chain_file(chain_rlp_path: &str) -> Vec { let chain_file = File::open(chain_rlp_path).expect("Failed to open chain rlp file"); _chain_file(chain_file).expect("Failed to decode chain rlp file") } -// From cmd/ethrex +// From cmd/ethereum_rust pub fn read_genesis_file(genesis_file_path: &str) -> Genesis { let genesis_file = std::fs::File::open(genesis_file_path).expect("Failed to open genesis file"); _genesis_file(genesis_file).expect("Failed to decode genesis file") @@ -51,7 +51,7 @@ pub fn generate_rlp( Ok(()) } -// From cmd/ethrex/decode.rs +// From cmd/ethereum_rust/decode.rs fn _chain_file(file: File) -> Result, Box> { let mut chain_rlp_reader = BufReader::new(file); let mut buf = vec![]; @@ -65,7 +65,7 @@ fn _chain_file(file: File) -> Result, Box> { Ok(blocks) } -// From cmd/ethrex/decode.rs +// From cmd/ethereum_rust/decode.rs fn _genesis_file(file: File) -> Result { let genesis_reader = BufReader::new(file); serde_json::from_reader(genesis_reader) diff --git a/crates/networking/p2p/Cargo.toml b/crates/networking/p2p/Cargo.toml index f3db303f9..55661aa8b 100644 --- a/crates/networking/p2p/Cargo.toml +++ b/crates/networking/p2p/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethrex-net" +name = "ethereum_rust-net" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethrex-core.workspace = true -ethrex-rlp.workspace = true -ethrex-storage.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-rlp.workspace = true +ethereum_rust-storage.workspace = true tracing.workspace = true tokio.workspace = true @@ -21,8 +21,6 @@ snap.workspace = true k256 = { version = "0.13.3", features = ["ecdh"] } sha3 = "0.10.8" -serde_json = "1.0.117" - # RLPx concat-kdf = "0.1.0" hmac = "0.12.1" diff --git a/crates/networking/p2p/README.md b/crates/networking/p2p/README.md index 9a2187416..39030074b 100644 --- a/crates/networking/p2p/README.md +++ b/crates/networking/p2p/README.md @@ -19,7 +19,7 @@ At startup, the discovery server launches three concurrent tokio tasks: Before starting these tasks, we run a [startup](#startup) process to connect to an array of initial nodes. -Before diving into what each task does, first, we need to understand how we are storing our nodes. Nodes are stored in an in-memory matrix which we call a [Kademlia table](https://github.com/lambdaclass/ethrex/blob/main/crates/net/kademlia.rs#L20-L23), though it isn't really a Kademlia table as we don't thoroughly follow the spec but we take it as a reference, you can read more [here](https://en.wikipedia.org/wiki/Kademlia). This table holds: +Before diving into what each task does, first, we need to understand how we are storing our nodes. Nodes are stored in an in-memory matrix which we call a [Kademlia table](https://github.com/lambdaclass/ethereum_rust/blob/main/crates/net/kademlia.rs#L20-L23), though it isn't really a Kademlia table as we don't thoroughly follow the spec but we take it as a reference, you can read more [here](https://en.wikipedia.org/wiki/Kademlia). This table holds: - Our `node_id`: `node_id`s are derived from the public key. They are the 64 bytes starting from index 1 of the encoded pub key. - A vector of 256 `bucket`s which holds: @@ -46,7 +46,7 @@ Before starting the server, we do a startup where we connect to an array of seed - Inserting them into our table - Pinging them to notify our presence, so they acknowledge us. -This startup is far from being completed. The current state allows us to do basic tests and connections. Later, we want to do a real startup by first trying to connect to those nodes we were previously connected. For that, we'd need to store nodes on the database. If those nodes aren't enough to fill our table, then we also ping some bootnodes, which could be hardcoded or received through the cli. Current issues are opened regarding [startup](https://github.com/lambdaclass/ethrex/issues/398) and [nodes db](https://github.com/lambdaclass/ethrex/issues/454). +This startup is far from being completed. The current state allows us to do basic tests and connections. Later, we want to do a real startup by first trying to connect to those nodes we were previously connected. For that, we'd need to store nodes on the database. If those nodes aren't enough to fill our table, then we also ping some bootnodes, which could be hardcoded or received through the cli. Current issues are opened regarding [startup](https://github.com/lambdaclass/ethereum_rust/issues/398) and [nodes db](https://github.com/lambdaclass/ethereum_rust/issues/454). ### Listen loop @@ -55,8 +55,8 @@ The listen loop handles messages sent to our socket. The spec defines 6 types of - **Ping**: Responds with a `pong` message. If the peer is not in our table we add it, if the corresponding bucket is already filled then we add it as a replacement for that bucket. If it was inserted we send a `ping from our end to get an endpoint proof. - **Pong**: Verifies that the `pong` corresponds to a previously sent `ping`, if so we mark the peer as proven. - **FindNodes**: Responds with a `neighbors` message that contains as many as the 16 closest nodes from the given target. A target is a pubkey provided by the peer in the message. The response can't be sent in one packet as it might exceed the discv4 max packet size. So we split it into different packets. -- **Neighbors**: First we verify that we have sent the corresponding `find_node` message. If so, we receive the peers, store them, and ping them. Also, every [`find_node` request](https://github.com/lambdaclass/ethrex/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/discv4.rs#L305-L314) may have a [tokio `Sender`](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html) attached, if that is the case, we forward the nodes from the message through the channel. This becomes useful when waiting for a `find_node` response, [something we do in the lookups](https://github.com/lambdaclass/ethrex/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/net.rs#L517-L570). -- **ENRRequest**: currently not implemented see [here](https://github.com/lambdaclass/ethrex/issues/432). +- **Neighbors**: First we verify that we have sent the corresponding `find_node` message. If so, we receive the peers, store them, and ping them. Also, every [`find_node` request](https://github.com/lambdaclass/ethereum_rust/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/discv4.rs#L305-L314) may have a [tokio `Sender`](https://docs.rs/tokio/latest/tokio/sync/mpsc/struct.Sender.html) attached, if that is the case, we forward the nodes from the message through the channel. This becomes useful when waiting for a `find_node` response, [something we do in the lookups](https://github.com/lambdaclass/ethereum_rust/blob/229ca0b316a79403412a917d04e3b95f579c56c7/crates/net/net.rs#L517-L570). +- **ENRRequest**: currently not implemented see [here](https://github.com/lambdaclass/ethereum_rust/issues/432). - **ENRResponse**: same as above. ### Re-validations @@ -103,7 +103,7 @@ Finally, here is an example of how you could build a network and see how they co We'll have three nodes: `a`, `b`, and `c`, we'll start `a`, then `b` setting `a` as a bootnode, and finally we'll start `c` with `b` as bootnode we should see that `c` connects to both `a` and `b` and so all the network should be connected. **node a**: -`cargo run --bin ethrex --network test_data/kurtosis.json` +`cargo run --bin ethereum_rust --network test_data/kurtosis.json` We get the `enode` by querying the node_info: `curl http://localhost:8545 \ @@ -115,7 +115,7 @@ We get the `enode` by querying the node_info: We start a new server passing the `node_a` `enode` as bootnodes ```bash -cargo run --bin ethrex --network ./test_data/kurtosis.json --bootnodes=`NODE_A_ENODE` \ +cargo run --bin ethereum_rust --network ./test_data/kurtosis.json --bootnodes=`NODE_A_ENODE` \ --authrpc.port=8552 --http.port=8546 --p2p.port=30305 --discovery.port=3036 ``` @@ -123,7 +123,7 @@ cargo run --bin ethrex --network ./test_data/kurtosis.json --bootnodes=`NODE_A_E Finally, with `node_c` we connect to `node_b`. When the lookup runs, `node_c` should end up connecting to `node_a`: ```bash - cargo run --bin ethrex --network ./test_data/kurtosis.json --bootnodes=`NODE_B_ENODE`" \ + cargo run --bin ethereum_rust --network ./test_data/kurtosis.json --bootnodes=`NODE_B_ENODE`" \ --authrpc.port=8553 --http.port=8547 --p2p.port=30308 --discovery.port=30310 ``` diff --git a/crates/networking/p2p/bootnode.rs b/crates/networking/p2p/bootnode.rs index c4caed177..0a0bbf2f8 100644 --- a/crates/networking/p2p/bootnode.rs +++ b/crates/networking/p2p/bootnode.rs @@ -1,4 +1,4 @@ -use ethrex_core::H512; +use ethereum_rust_core::H512; use std::{net::SocketAddr, num::ParseIntError, str::FromStr}; #[derive(Debug, Clone, PartialEq, Eq, Copy)] @@ -12,7 +12,7 @@ impl FromStr for BootNode { /// Takes a str with the format "enode://nodeID@IPaddress:port" and /// parses it to a BootNode // TODO: fix it to support different UDP and TCP ports, according to - // https://github.com/lambdaclass/lambda_ethrex/issues/905 + // https://github.com/lambdaclass/lambda_ethereum_rust/issues/905 fn from_str(input: &str) -> Result { // TODO: error handling let node_id = H512::from_str(&input[8..136]).expect("Failed to parse node id"); diff --git a/crates/networking/p2p/discv4.rs b/crates/networking/p2p/discv4.rs index 692c64066..9a76bd510 100644 --- a/crates/networking/p2p/discv4.rs +++ b/crates/networking/p2p/discv4.rs @@ -2,8 +2,8 @@ use std::time::{Duration, SystemTime, UNIX_EPOCH}; use crate::types::{Endpoint, Node, NodeRecord}; use bytes::BufMut; -use ethrex_core::{H256, H512, H520}; -use ethrex_rlp::{ +use ethereum_rust_core::{H256, H512, H520}; +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -509,7 +509,7 @@ impl RLPEncode for ENRResponseMessage { mod tests { use super::*; use bytes::Bytes; - use ethrex_core::{H256, H264}; + use ethereum_rust_core::{H256, H264}; use std::fmt::Write; use std::net::IpAddr; use std::num::ParseIntError; diff --git a/crates/networking/p2p/kademlia.rs b/crates/networking/p2p/kademlia.rs index 94b665001..26578a540 100644 --- a/crates/networking/p2p/kademlia.rs +++ b/crates/networking/p2p/kademlia.rs @@ -2,7 +2,7 @@ use crate::{ discv4::{time_now_unix, FindNodeRequest}, types::Node, }; -use ethrex_core::{H256, H512, U256}; +use ethereum_rust_core::{H256, H512, U256}; use sha3::{Digest, Keccak256}; use tokio::sync::mpsc::UnboundedSender; diff --git a/crates/networking/p2p/net.rs b/crates/networking/p2p/net.rs index b7cb064f7..9b027fa92 100644 --- a/crates/networking/p2p/net.rs +++ b/crates/networking/p2p/net.rs @@ -10,15 +10,15 @@ use discv4::{ get_expiration, is_expired, time_now_unix, time_since_in_hs, FindNodeMessage, Message, NeighborsMessage, Packet, PingMessage, PongMessage, }; -use ethrex_core::{H256, H512}; -use ethrex_storage::Store; +use ethereum_rust_core::{H256, H512}; +use ethereum_rust_storage::Store; use k256::{ ecdsa::SigningKey, elliptic_curve::{sec1::ToEncodedPoint, PublicKey}, }; use kademlia::{bucket_number, KademliaTable, MAX_NODES_PER_BUCKET}; use rand::rngs::OsRng; -use rlpx::{connection::RLPxConnection, message::Message as RLPxMessage}; +use rlpx::{connection::RLPxConnection, error::RLPxError, message::Message as RLPxMessage}; use tokio::{ net::{TcpSocket, TcpStream, UdpSocket}, sync::{broadcast, Mutex}, @@ -345,7 +345,7 @@ async fn discovery_startup( ip: bootnode.socket_address.ip(), udp_port: bootnode.socket_address.port(), // TODO: udp port can differ from tcp port. - // see https://github.com/lambdaclass/lambda_ethrex/issues/905 + // see https://github.com/lambdaclass/lambda_ethereum_rust/issues/905 tcp_port: bootnode.socket_address.port(), node_id: bootnode.node_id, }); @@ -782,8 +782,8 @@ async fn handle_peer_as_receiver( table: Arc>, connection_broadcast: broadcast::Sender<(tokio::task::Id, Arc)>, ) { - let mut conn = RLPxConnection::receiver(signer, stream, storage, connection_broadcast); - conn.start_peer(table).await; + let conn = RLPxConnection::receiver(signer, stream, storage, connection_broadcast); + handle_peer(conn, table).await; } async fn handle_peer_as_initiator( @@ -801,13 +801,40 @@ async fn handle_peer_as_initiator( .await .unwrap(); match RLPxConnection::initiator(signer, msg, stream, storage, connection_broadcast).await { - Ok(mut conn) => conn.start_peer(table).await, + Ok(conn) => handle_peer(conn, table).await, Err(e) => { error!("Error: {e}, Could not start connection with {node:?}"); } } } +async fn handle_peer(mut conn: RLPxConnection, table: Arc>) { + // Perform handshake + if let Err(e) = conn.handshake().await { + peer_conn_failed("Handshake failed", e, conn, table).await; + } else { + // Handshake OK: handle connection + if let Err(e) = conn.handle_peer_conn().await { + peer_conn_failed("Error during RLPx connection", e, conn, table).await; + } + } +} + +async fn peer_conn_failed( + error_text: &str, + error: RLPxError, + conn: RLPxConnection, + table: Arc>, +) { + if let Ok(node_id) = conn.get_remote_node_id() { + // Discard peer from kademlia table + info!("{error_text}: ({error}), discarding peer {node_id}"); + table.lock().await.replace_peer(node_id); + } else { + info!("{error_text}: ({error}), unknown peer") + } +} + pub fn node_id_from_signing_key(signer: &SigningKey) -> H512 { let public_key = PublicKey::from(signer.verifying_key()); let encoded = public_key.to_encoded_point(false); @@ -817,7 +844,7 @@ pub fn node_id_from_signing_key(signer: &SigningKey) -> H512 { #[cfg(test)] mod tests { use super::*; - use ethrex_storage::EngineType; + use ethereum_rust_storage::EngineType; use kademlia::bucket_number; use rand::rngs::OsRng; use std::{ diff --git a/crates/networking/p2p/rlpx/connection.rs b/crates/networking/p2p/rlpx/connection.rs index bb4815b62..b319f09c8 100644 --- a/crates/networking/p2p/rlpx/connection.rs +++ b/crates/networking/p2p/rlpx/connection.rs @@ -9,7 +9,7 @@ use crate::{ }, handshake::encode_ack_message, message::Message, - p2p::{self, DisconnectMessage, PingMessage, PongMessage}, + p2p::{self, PingMessage, PongMessage}, utils::id2pubkey, }, snap::{ @@ -28,9 +28,9 @@ use super::{ utils::{ecdh_xchng, pubkey2id}, }; use aes::cipher::KeyIvInit; -use ethrex_core::{H256, H512}; -use ethrex_rlp::decode::RLPDecode; -use ethrex_storage::Store; +use ethereum_rust_core::{H256, H512}; +use ethereum_rust_rlp::decode::RLPDecode; +use ethereum_rust_storage::Store; use k256::{ ecdsa::{RecoveryId, Signature, SigningKey, VerifyingKey}, PublicKey, SecretKey, @@ -38,10 +38,7 @@ use k256::{ use sha3::{Digest, Keccak256}; use tokio::{ io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}, - sync::{ - broadcast::{self, error::RecvError}, - Mutex, - }, + sync::broadcast::{self, error::RecvError}, task, time::{sleep, Instant}, }; @@ -88,7 +85,7 @@ impl RLPxConnection { storage, capabilities: vec![], next_periodic_task_check: Instant::now() + PERIODIC_TASKS_CHECK_INTERVAL, - connection_broadcast_send: connection_broadcast, + connection_broadcast_send: connection_broadcast.clone(), } } @@ -142,50 +139,7 @@ impl RLPxConnection { )) } - /// Starts a handshake and runs the peer connection. - /// It runs in it's own task and blocks until the connection is dropped - pub async fn start_peer(&mut self, table: Arc>) { - // Perform handshake - if let Err(e) = self.handshake().await { - self.peer_conn_failed("Handshake failed", e, table).await; - } else { - // Handshake OK: handle connection - if let Err(e) = self.handle_peer_conn().await { - self.peer_conn_failed("Error during RLPx connection", e, table) - .await; - } - } - } - - async fn peer_conn_failed( - &mut self, - error_text: &str, - error: RLPxError, - table: Arc>, - ) { - self.send(Message::Disconnect(DisconnectMessage { - reason: self.match_disconnect_reason(&error), - })) - .await - .unwrap_or_else(|e| info!("Could not send Disconnect message: ({e})")); - if let Ok(node_id) = self.get_remote_node_id() { - // Discard peer from kademlia table - info!("{error_text}: ({error}), discarding peer {node_id}"); - table.lock().await.replace_peer(node_id); - } else { - info!("{error_text}: ({error}), unknown peer") - } - } - - fn match_disconnect_reason(&self, error: &RLPxError) -> Option { - match error { - RLPxError::RLPDecodeError(_) => Some(2_u8), - // TODO build a proper matching between error types and disconnection reasons - _ => None, - } - } - - async fn handshake(&mut self) -> Result<(), RLPxError> { + pub async fn handshake(&mut self) -> Result<(), RLPxError> { match &self.state { RLPxConnectionState::Initiator(_) => { self.send_auth().await?; @@ -207,7 +161,7 @@ impl RLPxConnection { Ok(()) } - async fn exchange_hello_messages(&mut self) -> Result<(), RLPxError> { + pub async fn exchange_hello_messages(&mut self) -> Result<(), RLPxError> { let hello_msg = Message::Hello(p2p::HelloMessage::new( SUPPORTED_CAPABILITIES.to_vec(), PublicKey::from(self.signer.verifying_key()), @@ -237,7 +191,7 @@ impl RLPxConnection { } } - async fn handle_peer_conn(&mut self) -> Result<(), RLPxError> { + pub async fn handle_peer_conn(&mut self) -> Result<(), RLPxError> { if let RLPxConnectionState::Established(_) = &self.state { self.init_peer_conn().await?; info!("Started peer main loop"); @@ -290,7 +244,7 @@ impl RLPxConnection { } } - fn get_remote_node_id(&self) -> Result { + pub fn get_remote_node_id(&self) -> Result { if let RLPxConnectionState::Established(state) = &self.state { Ok(state.remote_node_id) } else { @@ -323,10 +277,13 @@ impl RLPxConnection { Message::Pong(_) => { // We ignore received Pong messages } - Message::Status(msg_data) if !peer_supports_eth => { + // Implmenent Status vaidations + // https://github.com/lambdaclass/lambda_ethereum_rust/issues/420 + Message::Status(_) if !peer_supports_eth => { info!("Received Status"); - backend::validate_status(msg_data, &self.storage)? + // TODO: Check peer's status message. } + // TODO: implement handlers for each message type Message::GetAccountRange(req) => { let response = process_account_range_request(req, self.storage.clone())?; self.send(Message::AccountRange(response)).await? @@ -397,16 +354,16 @@ impl RLPxConnection { // Sending eth Status if peer supports it if self.capabilities.contains(&CAP_ETH) { let status = backend::get_status(&self.storage)?; - info!("Sending status"); self.send(Message::Status(status)).await?; // The next immediate message in the ETH protocol is the // status, reference here: // https://github.com/ethereum/devp2p/blob/master/caps/eth.md#status-0x00 + // let Ok(Message::Status(_)) = self.receive().await else { + // self.capabilities.iter_mut().position(|cap| cap == &CAP_ETH).map(|indx| self.capabilities.remove(indx)); + // } match self.receive().await? { - Message::Status(msg_data) => { + Message::Status(_) => { // TODO: Check message status is correct. - info!("Received Status"); - backend::validate_status(msg_data, &self.storage)? } _msg => { return Err(RLPxError::HandshakeError( @@ -415,6 +372,7 @@ impl RLPxConnection { } } } + // TODO: add new capabilities startup when required (eg. snap) Ok(()) } diff --git a/crates/networking/p2p/rlpx/error.rs b/crates/networking/p2p/rlpx/error.rs index bc8ca31b4..a3b6efa46 100644 --- a/crates/networking/p2p/rlpx/error.rs +++ b/crates/networking/p2p/rlpx/error.rs @@ -1,5 +1,5 @@ -use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; -use ethrex_storage::error::StoreError; +use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethereum_rust_storage::error::StoreError; use thiserror::Error; use tokio::sync::broadcast::error::RecvError; diff --git a/crates/networking/p2p/rlpx/eth/backend.rs b/crates/networking/p2p/rlpx/eth/backend.rs index 9ba059dcd..cc51bd8f2 100644 --- a/crates/networking/p2p/rlpx/eth/backend.rs +++ b/crates/networking/p2p/rlpx/eth/backend.rs @@ -1,5 +1,5 @@ -use ethrex_core::{types::ForkId, U256}; -use ethrex_storage::Store; +use ethereum_rust_core::{types::ForkId, U256}; +use ethereum_rust_storage::Store; use crate::rlpx::error::RLPxError; @@ -35,92 +35,3 @@ pub fn get_status(storage: &Store) -> Result { fork_id, }) } - -pub fn validate_status(msg_data: StatusMessage, storage: &Store) -> Result<(), RLPxError> { - let chain_config = storage.get_chain_config()?; - - // These blocks must always be available - let genesis_header = storage - .get_block_header(0)? - .ok_or(RLPxError::NotFound("Genesis Block".to_string()))?; - let block_number = storage - .get_latest_block_number()? - .ok_or(RLPxError::NotFound("Latest Block Number".to_string()))?; - let block_header = storage - .get_block_header(block_number)? - .ok_or(RLPxError::NotFound(format!("Block {block_number}")))?; - - let genesis = genesis_header.compute_block_hash(); - let fork_id = ForkId::new(chain_config, genesis, block_header.timestamp, block_number); - - //Check networkID - if msg_data.network_id != chain_config.chain_id { - return Err(RLPxError::HandshakeError( - "Network Id does not match".to_string(), - )); - } - //Check Protocol Version - if msg_data.eth_version != ETH_VERSION { - return Err(RLPxError::HandshakeError( - "Eth protocol version does not match".to_string(), - )); - } - //Check Genesis - if msg_data.genesis != genesis { - return Err(RLPxError::HandshakeError( - "Genesis does not match".to_string(), - )); - } - // Check ForkID - if msg_data.fork_id != fork_id { - return Err(RLPxError::HandshakeError( - "Fork Id does not match".to_string(), - )); - } - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::validate_status; - use crate::rlpx::eth::status::StatusMessage; - use ethrex_core::{ - types::{ForkId, Genesis}, - H256, U256, - }; - use ethrex_storage::{EngineType, Store}; - use std::{fs::File, io::BufReader}; - - #[test] - // TODO add tests for failing validations - fn test_validate_status() { - // Setup - // TODO we should have this setup exported to some test_utils module and use from there - let storage = - Store::new("temp.db", EngineType::InMemory).expect("Failed to create test DB"); - let file = File::open("../../../test_data/genesis-execution-api.json") - .expect("Failed to open genesis file"); - let reader = BufReader::new(file); - let genesis: Genesis = - serde_json::from_reader(reader).expect("Failed to deserialize genesis file"); - storage - .add_initial_state(genesis.clone()) - .expect("Failed to add genesis block to DB"); - let config = genesis.config; - let total_difficulty = U256::from(config.terminal_total_difficulty.unwrap_or_default()); - let genesis_hash = genesis.get_block().hash(); - let fork_id = ForkId::new(config, genesis_hash, 2707305664, 123); - - let message = StatusMessage { - eth_version: 68u32, - network_id: 3503995874084926, - total_difficulty, - block_hash: H256::random(), - genesis: genesis_hash, - fork_id, - }; - let result = validate_status(message, &storage); - assert!(result.is_ok()); - } -} diff --git a/crates/networking/p2p/rlpx/eth/blocks.rs b/crates/networking/p2p/rlpx/eth/blocks.rs index 4d50374c0..1ec932c0c 100644 --- a/crates/networking/p2p/rlpx/eth/blocks.rs +++ b/crates/networking/p2p/rlpx/eth/blocks.rs @@ -3,14 +3,14 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethrex_core::types::{BlockBody, BlockHash, BlockHeader, BlockNumber}; -use ethrex_rlp::{ +use ethereum_rust_core::types::{BlockBody, BlockHash, BlockHeader, BlockNumber}; +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; -use ethrex_storage::Store; +use ethereum_rust_storage::Store; use tracing::error; pub const HASH_FIRST_BYTE_DECODER: u8 = 160; @@ -303,7 +303,7 @@ impl RLPxMessage for BlockBodies { #[cfg(test)] mod tests { - use ethrex_core::types::BlockHash; + use ethereum_rust_core::types::BlockHash; use crate::rlpx::{ eth::blocks::{BlockBodies, GetBlockBodies, GetBlockHeaders}, diff --git a/crates/networking/p2p/rlpx/eth/receipts.rs b/crates/networking/p2p/rlpx/eth/receipts.rs index 0ec74af35..0eae0af0a 100644 --- a/crates/networking/p2p/rlpx/eth/receipts.rs +++ b/crates/networking/p2p/rlpx/eth/receipts.rs @@ -3,8 +3,8 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethrex_core::types::{BlockHash, Receipt}; -use ethrex_rlp::{ +use ethereum_rust_core::types::{BlockHash, Receipt}; +use ethereum_rust_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; @@ -86,7 +86,7 @@ impl RLPxMessage for Receipts { #[cfg(test)] mod tests { - use ethrex_core::types::{BlockHash, Receipt}; + use ethereum_rust_core::types::{BlockHash, Receipt}; use crate::rlpx::{ eth::receipts::{GetReceipts, Receipts}, diff --git a/crates/networking/p2p/rlpx/eth/status.rs b/crates/networking/p2p/rlpx/eth/status.rs index f39c36dcf..b0e21bbf8 100644 --- a/crates/networking/p2p/rlpx/eth/status.rs +++ b/crates/networking/p2p/rlpx/eth/status.rs @@ -3,11 +3,11 @@ use crate::rlpx::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::BufMut; -use ethrex_core::{ +use ethereum_rust_core::{ types::{BlockHash, ForkId}, U256, }; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; diff --git a/crates/networking/p2p/rlpx/eth/transactions.rs b/crates/networking/p2p/rlpx/eth/transactions.rs index 8ad8c627c..d40e85dec 100644 --- a/crates/networking/p2p/rlpx/eth/transactions.rs +++ b/crates/networking/p2p/rlpx/eth/transactions.rs @@ -1,6 +1,6 @@ use bytes::BufMut; -use ethrex_core::{types::Transaction, H256}; -use ethrex_rlp::{ +use ethereum_rust_core::{types::Transaction, H256}; +use ethereum_rust_rlp::{ error::{RLPDecodeError, RLPEncodeError}, structs::{Decoder, Encoder}, }; @@ -218,7 +218,7 @@ impl RLPxMessage for PooledTransactions { #[cfg(test)] mod tests { - use ethrex_core::{types::Transaction, H256}; + use ethereum_rust_core::{types::Transaction, H256}; use crate::rlpx::{ eth::transactions::{GetPooledTransactions, PooledTransactions}, diff --git a/crates/networking/p2p/rlpx/frame.rs b/crates/networking/p2p/rlpx/frame.rs index f766f8e4d..0480c5e8d 100644 --- a/crates/networking/p2p/rlpx/frame.rs +++ b/crates/networking/p2p/rlpx/frame.rs @@ -2,8 +2,8 @@ use aes::{ cipher::{BlockEncrypt as _, KeyInit as _, StreamCipher as _}, Aes256Enc, }; -use ethrex_core::H128; -use ethrex_rlp::encode::RLPEncode as _; +use ethereum_rust_core::H128; +use ethereum_rust_rlp::encode::RLPEncode as _; use sha3::Digest as _; use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt}; diff --git a/crates/networking/p2p/rlpx/handshake.rs b/crates/networking/p2p/rlpx/handshake.rs index 91ea09d41..87caa0038 100644 --- a/crates/networking/p2p/rlpx/handshake.rs +++ b/crates/networking/p2p/rlpx/handshake.rs @@ -1,7 +1,7 @@ use crate::rlpx::utils::{ecdh_xchng, id2pubkey, kdf, pubkey2id, sha256, sha256_hmac}; use aes::cipher::{KeyIvInit, StreamCipher}; -use ethrex_core::{Signature, H128, H256, H512}; -use ethrex_rlp::{ +use ethereum_rust_core::{Signature, H128, H256, H512}; +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -320,7 +320,7 @@ impl RLPDecode for AckMessage { mod tests { use std::str::FromStr; - use ethrex_core::H256; + use ethereum_rust_core::H256; use hex_literal::hex; use k256::SecretKey; diff --git a/crates/networking/p2p/rlpx/message.rs b/crates/networking/p2p/rlpx/message.rs index d44f1b4ca..6415d74e4 100644 --- a/crates/networking/p2p/rlpx/message.rs +++ b/crates/networking/p2p/rlpx/message.rs @@ -1,5 +1,5 @@ use bytes::BufMut; -use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; use std::fmt::Display; use super::eth::blocks::{BlockBodies, BlockHeaders, GetBlockBodies, GetBlockHeaders}; @@ -11,7 +11,7 @@ use super::snap::{ StorageRanges, TrieNodes, }; -use ethrex_rlp::encode::RLPEncode; +use ethereum_rust_rlp::encode::RLPEncode; pub trait RLPxMessage: Sized { fn encode(&self, buf: &mut dyn BufMut) -> Result<(), RLPEncodeError>; diff --git a/crates/networking/p2p/rlpx/p2p.rs b/crates/networking/p2p/rlpx/p2p.rs index fdc752a83..70573c021 100644 --- a/crates/networking/p2p/rlpx/p2p.rs +++ b/crates/networking/p2p/rlpx/p2p.rs @@ -1,6 +1,6 @@ use bytes::BufMut; -use ethrex_core::H512; -use ethrex_rlp::{ +use ethereum_rust_core::H512; +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, diff --git a/crates/networking/p2p/rlpx/snap.rs b/crates/networking/p2p/rlpx/snap.rs index 2c7643790..abec6c9f1 100644 --- a/crates/networking/p2p/rlpx/snap.rs +++ b/crates/networking/p2p/rlpx/snap.rs @@ -3,11 +3,11 @@ use super::{ utils::{snappy_compress, snappy_decompress}, }; use bytes::{BufMut, Bytes}; -use ethrex_core::{ +use ethereum_rust_core::{ types::{AccountState, EMPTY_KECCACK_HASH, EMPTY_TRIE_HASH}, H256, U256, }; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::{RLPDecodeError, RLPEncodeError}, diff --git a/crates/networking/p2p/rlpx/utils.rs b/crates/networking/p2p/rlpx/utils.rs index 6efe31d59..348932401 100644 --- a/crates/networking/p2p/rlpx/utils.rs +++ b/crates/networking/p2p/rlpx/utils.rs @@ -1,5 +1,5 @@ -use ethrex_core::H512; -use ethrex_rlp::error::{RLPDecodeError, RLPEncodeError}; +use ethereum_rust_core::H512; +use ethereum_rust_rlp::error::{RLPDecodeError, RLPEncodeError}; use k256::{ elliptic_curve::sec1::{FromEncodedPoint, ToEncodedPoint}, EncodedPoint, PublicKey, SecretKey, diff --git a/crates/networking/p2p/snap.rs b/crates/networking/p2p/snap.rs index 498101954..fefd7374b 100644 --- a/crates/networking/p2p/snap.rs +++ b/crates/networking/p2p/snap.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::{error::StoreError, Store}; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_storage::{error::StoreError, Store}; use crate::rlpx::{ error::RLPxError, @@ -157,9 +157,9 @@ pub fn process_trie_nodes_request( mod tests { use std::str::FromStr; - use ethrex_core::{types::AccountState, BigEndianHash, H256}; - use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; - use ethrex_storage::EngineType; + use ethereum_rust_core::{types::AccountState, BigEndianHash, H256}; + use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; + use ethereum_rust_storage::EngineType; use crate::rlpx::snap::AccountStateSlim; diff --git a/crates/networking/p2p/types.rs b/crates/networking/p2p/types.rs index 583f943a9..c44397a63 100644 --- a/crates/networking/p2p/types.rs +++ b/crates/networking/p2p/types.rs @@ -1,6 +1,6 @@ use bytes::{BufMut, Bytes}; -use ethrex_core::H512; -use ethrex_rlp::{ +use ethereum_rust_core::H512; +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/networking/rpc/Cargo.toml b/crates/networking/rpc/Cargo.toml index a694b8243..f461c4033 100644 --- a/crates/networking/rpc/Cargo.toml +++ b/crates/networking/rpc/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "ethrex-rpc" +name = "ethereum_rust-rpc" version = "0.1.0" edition = "2021" @@ -13,12 +13,12 @@ tokio.workspace = true bytes.workspace = true tracing.workspace = true tracing-subscriber.workspace = true -ethrex-core.workspace = true -ethrex-storage.workspace = true -ethrex-vm.workspace = true -ethrex-blockchain.workspace = true -ethrex-net.workspace = true -ethrex-rlp.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-storage.workspace = true +ethereum_rust-vm.workspace = true +ethereum_rust-blockchain.workspace = true +ethereum_rust-net.workspace = true +ethereum_rust-rlp.workspace = true hex.workspace = true axum-extra = { version = "0.9.3", features = ["typed-header"] } jsonwebtoken.workspace = true diff --git a/crates/networking/rpc/admin/mod.rs b/crates/networking/rpc/admin/mod.rs index 53311de94..1891f0e1a 100644 --- a/crates/networking/rpc/admin/mod.rs +++ b/crates/networking/rpc/admin/mod.rs @@ -1,6 +1,6 @@ -use ethrex_core::types::ChainConfig; -use ethrex_net::types::Node; -use ethrex_storage::Store; +use ethereum_rust_core::types::ChainConfig; +use ethereum_rust_net::types::Node; +use ethereum_rust_storage::Store; use serde::Serialize; use serde_json::Value; use std::collections::HashMap; @@ -41,7 +41,7 @@ pub fn node_info(storage: Store, local_node: Node) -> Result { let node_info = NodeInfo { enode: enode_url, id: hex::encode(local_node.node_id), - name: "ethrex/0.1.0/rust1.80".to_string(), + name: "ethereum_rust/0.1.0/rust1.80".to_string(), ip: local_node.ip.to_string(), ports: Ports { discovery: local_node.udp_port, diff --git a/crates/networking/rpc/engine/exchange_transition_config.rs b/crates/networking/rpc/engine/exchange_transition_config.rs index 751b1ad49..7d0faa2f1 100644 --- a/crates/networking/rpc/engine/exchange_transition_config.rs +++ b/crates/networking/rpc/engine/exchange_transition_config.rs @@ -1,4 +1,4 @@ -use ethrex_core::{serde_utils, H256}; +use ethereum_rust_core::{serde_utils, H256}; use serde::{Deserialize, Serialize}; use serde_json::Value; use tracing::{info, warn}; diff --git a/crates/networking/rpc/engine/fork_choice.rs b/crates/networking/rpc/engine/fork_choice.rs index a6bd6c1e1..78b7f7e89 100644 --- a/crates/networking/rpc/engine/fork_choice.rs +++ b/crates/networking/rpc/engine/fork_choice.rs @@ -1,4 +1,4 @@ -use ethrex_blockchain::{ +use ethereum_rust_blockchain::{ error::{ChainError, InvalidForkChoice}, fork_choice::apply_fork_choice, latest_canonical_block_hash, diff --git a/crates/networking/rpc/engine/payload.rs b/crates/networking/rpc/engine/payload.rs index f0d351e9d..b85188b7d 100644 --- a/crates/networking/rpc/engine/payload.rs +++ b/crates/networking/rpc/engine/payload.rs @@ -1,8 +1,8 @@ -use ethrex_blockchain::add_block; -use ethrex_blockchain::error::ChainError; -use ethrex_blockchain::payload::build_payload; -use ethrex_core::types::Fork; -use ethrex_core::{H256, U256}; +use ethereum_rust_blockchain::add_block; +use ethereum_rust_blockchain::error::ChainError; +use ethereum_rust_blockchain::payload::build_payload; +use ethereum_rust_core::types::Fork; +use ethereum_rust_core::{H256, U256}; use serde_json::Value; use tracing::{error, info, warn}; diff --git a/crates/networking/rpc/eth/account.rs b/crates/networking/rpc/eth/account.rs index 94f880da3..76e296570 100644 --- a/crates/networking/rpc/eth/account.rs +++ b/crates/networking/rpc/eth/account.rs @@ -1,4 +1,4 @@ -use ethrex_blockchain::mempool; +use ethereum_rust_blockchain::mempool; use serde_json::Value; use tracing::info; @@ -6,7 +6,7 @@ use crate::types::account_proof::{AccountProof, StorageProof}; use crate::types::block_identifier::{BlockIdentifierOrHash, BlockTag}; use crate::RpcApiContext; use crate::{utils::RpcErr, RpcHandler}; -use ethrex_core::{Address, BigEndianHash, H256, U256}; +use ethereum_rust_core::{Address, BigEndianHash, H256, U256}; pub struct GetBalanceRequest { pub address: Address, diff --git a/crates/networking/rpc/eth/block.rs b/crates/networking/rpc/eth/block.rs index fe014cc79..74295d090 100644 --- a/crates/networking/rpc/eth/block.rs +++ b/crates/networking/rpc/eth/block.rs @@ -1,5 +1,5 @@ -use ethrex_blockchain::find_parent_header; -use ethrex_rlp::encode::RLPEncode; +use ethereum_rust_blockchain::find_parent_header; +use ethereum_rust_rlp::encode::RLPEncode; use serde_json::Value; use tracing::info; @@ -12,14 +12,14 @@ use crate::{ utils::RpcErr, RpcApiContext, RpcHandler, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{ calculate_base_fee_per_blob_gas, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, Receipt, }, U256, }; -use ethrex_storage::Store; +use ethereum_rust_storage::Store; pub struct GetBlockByNumberRequest { pub block: BlockIdentifier, diff --git a/crates/networking/rpc/eth/fee_market.rs b/crates/networking/rpc/eth/fee_market.rs index ea01ce612..e1b8cf663 100644 --- a/crates/networking/rpc/eth/fee_market.rs +++ b/crates/networking/rpc/eth/fee_market.rs @@ -1,12 +1,12 @@ -use ethrex_blockchain::constants::MAX_BLOB_GAS_PER_BLOCK; -use ethrex_core::types::{Block, Transaction}; +use ethereum_rust_blockchain::constants::MAX_BLOB_GAS_PER_BLOCK; +use ethereum_rust_core::types::{Block, Transaction}; use serde::Serialize; use serde_json::Value; use tracing::info; use crate::{types::block_identifier::BlockIdentifier, utils::RpcErr, RpcApiContext, RpcHandler}; -use ethrex_core::types::calculate_base_fee_per_blob_gas; -use ethrex_storage::Store; +use ethereum_rust_core::types::calculate_base_fee_per_blob_gas; +use ethereum_rust_storage::Store; #[derive(Clone, Debug)] pub struct FeeHistoryRequest { diff --git a/crates/networking/rpc/eth/filter.rs b/crates/networking/rpc/eth/filter.rs index 0a7027dfd..c0e6018fd 100644 --- a/crates/networking/rpc/eth/filter.rs +++ b/crates/networking/rpc/eth/filter.rs @@ -2,8 +2,8 @@ // - Manually testing the behaviour deploying contracts on the Sepolia test network. // - Go-Ethereum, specifically: https://github.com/ethereum/go-ethereum/blob/368e16f39d6c7e5cce72a92ec289adbfbaed4854/eth/filters/filter.go // - Ethereum's reference: https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_newfilter -use ethrex_core::types::BlockNumber; -use ethrex_storage::Store; +use ethereum_rust_core::types::BlockNumber; +use ethereum_rust_storage::Store; use std::{ collections::HashMap, sync::{Arc, Mutex}, @@ -67,7 +67,7 @@ impl NewFilterRequest { pub fn handle( &self, - storage: ethrex_storage::Store, + storage: ethereum_rust_storage::Store, filters: ActiveFilters, ) -> Result { let from = self @@ -141,7 +141,7 @@ impl DeleteFilterRequest { pub fn handle( &self, - _storage: ethrex_storage::Store, + _storage: ethereum_rust_storage::Store, filters: ActiveFilters, ) -> Result { let mut active_filters_guard = filters.lock().unwrap_or_else(|mut poisoned_guard| { @@ -158,7 +158,7 @@ impl DeleteFilterRequest { pub fn stateful_call( req: &RpcRequest, - storage: ethrex_storage::Store, + storage: ethereum_rust_storage::Store, filters: ActiveFilters, ) -> Result { let request = Self::parse(&req.params)?; @@ -185,7 +185,7 @@ impl FilterChangesRequest { } pub fn handle( &self, - storage: ethrex_storage::Store, + storage: ethereum_rust_storage::Store, filters: ActiveFilters, ) -> Result { let Some(latest_block_num) = storage.get_latest_block_number()? else { @@ -242,7 +242,7 @@ impl FilterChangesRequest { } pub fn stateful_call( req: &RpcRequest, - storage: ethrex_storage::Store, + storage: ethereum_rust_storage::Store, filters: ActiveFilters, ) -> Result { let request = Self::parse(&req.params)?; @@ -272,8 +272,8 @@ mod tests { types::block_identifier::BlockIdentifier, utils::{test_utils::example_p2p_node, RpcRequest}, }; - use ethrex_core::types::Genesis; - use ethrex_storage::{EngineType, Store}; + use ethereum_rust_core::types::Genesis; + use ethereum_rust_storage::{EngineType, Store}; use serde_json::{json, Value}; use test_utils::TEST_GENESIS; diff --git a/crates/networking/rpc/eth/gas_price.rs b/crates/networking/rpc/eth/gas_price.rs index cba06811f..c35c5f241 100644 --- a/crates/networking/rpc/eth/gas_price.rs +++ b/crates/networking/rpc/eth/gas_price.rs @@ -1,4 +1,4 @@ -use ethrex_blockchain::constants::MIN_GAS_LIMIT; +use ethereum_rust_blockchain::constants::MIN_GAS_LIMIT; use tracing::error; use crate::utils::RpcErr; @@ -107,15 +107,15 @@ mod tests { RpcApiContext, RpcHandler, }; use bytes::Bytes; - use ethrex_core::{ + use ethereum_rust_core::{ types::{ Block, BlockBody, BlockHeader, EIP1559Transaction, Genesis, LegacyTransaction, Transaction, TxKind, }, Address, Bloom, H256, U256, }; - use ethrex_net::types::Node; - use ethrex_storage::{EngineType, Store}; + use ethereum_rust_net::types::Node; + use ethereum_rust_storage::{EngineType, Store}; use hex_literal::hex; use serde_json::json; use std::{net::Ipv4Addr, str::FromStr}; diff --git a/crates/networking/rpc/eth/logs.rs b/crates/networking/rpc/eth/logs.rs index 72eb0aed6..b45034e70 100644 --- a/crates/networking/rpc/eth/logs.rs +++ b/crates/networking/rpc/eth/logs.rs @@ -6,8 +6,8 @@ use crate::{ types::{block_identifier::BlockIdentifier, receipt::RpcLog}, RpcApiContext, RpcErr, RpcHandler, }; -use ethrex_core::{H160, H256}; -use ethrex_storage::Store; +use ethereum_rust_core::{H160, H256}; +use ethereum_rust_storage::Store; use serde::Deserialize; use serde_json::Value; use std::collections::HashSet; diff --git a/crates/networking/rpc/eth/transaction.rs b/crates/networking/rpc/eth/transaction.rs index efa7ec8e7..2727e0e8b 100644 --- a/crates/networking/rpc/eth/transaction.rs +++ b/crates/networking/rpc/eth/transaction.rs @@ -7,16 +7,16 @@ use crate::{ utils::RpcErr, RpcApiContext, RpcHandler, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{AccessListEntry, BlockHash, BlockHeader, BlockNumber, GenericTransaction, TxKind}, H256, U256, }; -use ethrex_blockchain::mempool; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::Store; +use ethereum_rust_blockchain::mempool; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_storage::Store; -use ethrex_vm::{evm_state, ExecutionResult, SpecId}; +use ethereum_rust_vm::{evm_state, ExecutionResult, SpecId}; use serde::Serialize; use serde_json::Value; @@ -68,7 +68,7 @@ pub struct AccessListResult { access_list: Vec, #[serde(skip_serializing_if = "Option::is_none")] error: Option, - #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] gas_used: u64, } @@ -233,7 +233,7 @@ impl RpcHandler for GetTransactionByHashRequest { _ => return Ok(Value::Null), }; - let transaction: ethrex_core::types::Transaction = + let transaction: ethereum_rust_core::types::Transaction = match storage.get_transaction_by_location(block_hash, index)? { Some(transaction) => transaction, _ => return Ok(Value::Null), @@ -319,7 +319,7 @@ impl RpcHandler for CreateAccessListRequest { _ => return Ok(Value::Null), }; // Run transaction and obtain access list - let (gas_used, access_list, error) = match ethrex_vm::create_access_list( + let (gas_used, access_list, error) = match ethereum_rust_vm::create_access_list( &self.transaction, &header, &mut evm_state(context.storage, header.compute_block_hash()), @@ -449,7 +449,8 @@ impl RpcHandler for EstimateGasRequest { } }; - let spec_id = ethrex_vm::spec_id(&storage.get_chain_config()?, block_header.timestamp); + let spec_id = + ethereum_rust_vm::spec_id(&storage.get_chain_config()?, block_header.timestamp); // If the transaction is a plain value transfer, short circuit estimation. if let TxKind::Call(address) = transaction.to { @@ -547,7 +548,7 @@ fn simulate_tx( storage: Store, spec_id: SpecId, ) -> Result { - match ethrex_vm::simulate_tx_from_generic( + match ethereum_rust_vm::simulate_tx_from_generic( transaction, block_header, &mut evm_state(storage, block_header.compute_block_hash()), diff --git a/crates/networking/rpc/rpc.rs b/crates/networking/rpc/rpc.rs index d8382aa38..645386c29 100644 --- a/crates/networking/rpc/rpc.rs +++ b/crates/networking/rpc/rpc.rs @@ -56,8 +56,8 @@ pub mod utils; mod web3; use axum::extract::State; -use ethrex_net::types::Node; -use ethrex_storage::Store; +use ethereum_rust_net::types::Node; +use ethereum_rust_storage::Store; #[derive(Debug, Clone)] pub struct RpcApiContext { @@ -301,8 +301,8 @@ where mod tests { use super::*; use crate::utils::test_utils::example_p2p_node; - use ethrex_core::types::{ChainConfig, Genesis}; - use ethrex_storage::EngineType; + use ethereum_rust_core::types::{ChainConfig, Genesis}; + use ethereum_rust_storage::EngineType; use std::fs::File; use std::io::BufReader; @@ -329,7 +329,7 @@ mod tests { let result = map_http_requests(&request, context); let rpc_response = rpc_response(request.id, result); let expected_response = to_rpc_response_success_value( - r#"{"jsonrpc":"2.0","id":1,"result":{"enode":"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@127.0.0.1:30303","id":"d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666","ip":"127.0.0.1","name":"ethrex/0.1.0/rust1.80","ports":{"discovery":30303,"listener":30303},"protocols":{"eth":{"chainId":3151908,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":false,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":null,"berlinBlock":0,"londonBlock":0,"arrowGlacierBlock":null,"grayGlacierBlock":null,"mergeNetsplitBlock":0,"shanghaiTime":0,"cancunTime":0,"pragueTime":1718232101,"verkleTime":null,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true}}}}"#, + r#"{"jsonrpc":"2.0","id":1,"result":{"enode":"enode://d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666@127.0.0.1:30303","id":"d860a01f9722d78051619d1e2351aba3f43f943f6f00718d1b9baa4101932a1f5011f16bb2b1bb35db20d6fe28fa0bf09636d26a87d31de9ec6203eeedb1f666","ip":"127.0.0.1","name":"ethereum_rust/0.1.0/rust1.80","ports":{"discovery":30303,"listener":30303},"protocols":{"eth":{"chainId":3151908,"homesteadBlock":0,"daoForkBlock":null,"daoForkSupport":false,"eip150Block":0,"eip155Block":0,"eip158Block":0,"byzantiumBlock":0,"constantinopleBlock":0,"petersburgBlock":0,"istanbulBlock":0,"muirGlacierBlock":null,"berlinBlock":0,"londonBlock":0,"arrowGlacierBlock":null,"grayGlacierBlock":null,"mergeNetsplitBlock":0,"shanghaiTime":0,"cancunTime":0,"pragueTime":1718232101,"verkleTime":null,"terminalTotalDifficulty":0,"terminalTotalDifficultyPassed":true}}}}"#, ); assert_eq!(rpc_response.to_string(), expected_response.to_string()) } diff --git a/crates/networking/rpc/types/account_proof.rs b/crates/networking/rpc/types/account_proof.rs index ed0ca8cdf..332026b30 100644 --- a/crates/networking/rpc/types/account_proof.rs +++ b/crates/networking/rpc/types/account_proof.rs @@ -1,4 +1,4 @@ -use ethrex_core::{serde_utils, Address, H256, U256}; +use ethereum_rust_core::{serde_utils, Address, H256, U256}; use serde::{ser::SerializeSeq, Serialize, Serializer}; #[derive(Debug, Serialize)] diff --git a/crates/networking/rpc/types/block.rs b/crates/networking/rpc/types/block.rs index 25ec4a78e..54a307610 100644 --- a/crates/networking/rpc/types/block.rs +++ b/crates/networking/rpc/types/block.rs @@ -1,10 +1,10 @@ use super::transaction::RpcTransaction; -use ethrex_core::{ +use ethereum_rust_core::{ serde_utils, types::{Block, BlockBody, BlockHash, BlockHeader, BlockNumber, Withdrawal}, H256, U256, }; -use ethrex_rlp::encode::RLPEncode; +use ethereum_rust_rlp::encode::RLPEncode; use serde::{Deserialize, Serialize}; @@ -101,7 +101,7 @@ impl FullBlockBody { mod test { use bytes::Bytes; - use ethrex_core::{ + use ethereum_rust_core::{ types::{EIP1559Transaction, Transaction, TxKind}, Address, Bloom, H256, U256, }; diff --git a/crates/networking/rpc/types/block_identifier.rs b/crates/networking/rpc/types/block_identifier.rs index b74963820..5e59db067 100644 --- a/crates/networking/rpc/types/block_identifier.rs +++ b/crates/networking/rpc/types/block_identifier.rs @@ -1,7 +1,7 @@ use std::{fmt::Display, str::FromStr}; -use ethrex_core::types::{BlockHash, BlockHeader, BlockNumber}; -use ethrex_storage::{error::StoreError, Store}; +use ethereum_rust_core::types::{BlockHash, BlockHeader, BlockNumber}; +use ethereum_rust_storage::{error::StoreError, Store}; use serde::Deserialize; use serde_json::Value; diff --git a/crates/networking/rpc/types/fork_choice.rs b/crates/networking/rpc/types/fork_choice.rs index 4cfcf2bd3..86189038a 100644 --- a/crates/networking/rpc/types/fork_choice.rs +++ b/crates/networking/rpc/types/fork_choice.rs @@ -1,5 +1,5 @@ use super::payload::PayloadStatus; -use ethrex_core::{serde_utils, types::Withdrawal, Address, H256}; +use ethereum_rust_core::{serde_utils, types::Withdrawal, Address, H256}; use serde::{Deserialize, Serialize}; #[derive(Debug, Deserialize, Serialize)] diff --git a/crates/networking/rpc/types/payload.rs b/crates/networking/rpc/types/payload.rs index b35825039..b1abb57cb 100644 --- a/crates/networking/rpc/types/payload.rs +++ b/crates/networking/rpc/types/payload.rs @@ -1,8 +1,8 @@ use bytes::Bytes; -use ethrex_rlp::error::RLPDecodeError; +use ethereum_rust_rlp::error::RLPDecodeError; use serde::{Deserialize, Serialize}; -use ethrex_core::{ +use ethereum_rust_core::{ serde_utils, types::{ compute_transactions_root, compute_withdrawals_root, BlobsBundle, Block, BlockBody, diff --git a/crates/networking/rpc/types/receipt.rs b/crates/networking/rpc/types/receipt.rs index 7c79bc145..829394b0b 100644 --- a/crates/networking/rpc/types/receipt.rs +++ b/crates/networking/rpc/types/receipt.rs @@ -1,10 +1,10 @@ -use ethrex_blockchain::constants::GAS_PER_BLOB; -use ethrex_core::{ +use ethereum_rust_blockchain::constants::GAS_PER_BLOB; +use ethereum_rust_core::{ serde_utils, types::{BlockHash, BlockHeader, BlockNumber, Log, Receipt, Transaction, TxKind, TxType}, Address, Bloom, Bytes, H256, }; -use ethrex_vm::RevmAddress; +use ethereum_rust_vm::RevmAddress; use serde::{Deserialize, Serialize}; @@ -138,18 +138,18 @@ impl RpcReceiptBlockInfo { #[serde(rename_all = "camelCase")] pub struct RpcReceiptTxInfo { pub transaction_hash: H256, - #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub transaction_index: u64, pub from: Address, pub to: Option
, pub contract_address: Option
, - #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub gas_used: u64, - #[serde(with = "ethrex_core::serde_utils::u64::hex_str")] + #[serde(with = "ethereum_rust_core::serde_utils::u64::hex_str")] pub effective_gas_price: u64, #[serde( skip_serializing_if = "Option::is_none", - with = "ethrex_core::serde_utils::u64::hex_str_opt", + with = "ethereum_rust_core::serde_utils::u64::hex_str_opt", default = "Option::default" )] pub blob_gas_price: Option, @@ -207,7 +207,7 @@ impl RpcReceiptTxInfo { #[cfg(test)] mod tests { use super::*; - use ethrex_core::{ + use ethereum_rust_core::{ types::{Log, TxType}, Bloom, Bytes, }; diff --git a/crates/networking/rpc/types/transaction.rs b/crates/networking/rpc/types/transaction.rs index df4b5a29c..fce3b5ff1 100644 --- a/crates/networking/rpc/types/transaction.rs +++ b/crates/networking/rpc/types/transaction.rs @@ -1,4 +1,4 @@ -use ethrex_core::{ +use ethereum_rust_core::{ serde_utils, types::{ BlobsBundle, BlockHash, BlockNumber, EIP1559Transaction, EIP2930Transaction, @@ -6,7 +6,7 @@ use ethrex_core::{ }, Address, H256, }; -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/networking/rpc/utils.rs b/crates/networking/rpc/utils.rs index 4ea572ed1..3fd23c2da 100644 --- a/crates/networking/rpc/utils.rs +++ b/crates/networking/rpc/utils.rs @@ -1,10 +1,10 @@ -use ethrex_storage::error::StoreError; -use ethrex_vm::EvmError; +use ethereum_rust_storage::error::StoreError; +use ethereum_rust_vm::EvmError; use serde::{Deserialize, Serialize}; use serde_json::Value; use crate::authentication::AuthenticationError; -use ethrex_blockchain::error::MempoolError; +use ethereum_rust_blockchain::error::MempoolError; #[derive(Debug, Deserialize)] pub enum RpcErr { @@ -246,9 +246,9 @@ pub fn parse_json_hex(hex: &serde_json::Value) -> Result { pub mod test_utils { use std::{net::SocketAddr, str::FromStr}; - use ethrex_core::H512; - use ethrex_net::types::Node; - use ethrex_storage::{EngineType, Store}; + use ethereum_rust_core::H512; + use ethereum_rust_net::types::Node; + use ethereum_rust_storage::{EngineType, Store}; use crate::start_api; diff --git a/crates/networking/rpc/web3/mod.rs b/crates/networking/rpc/web3/mod.rs index 1bbad9690..e5e628ec1 100644 --- a/crates/networking/rpc/web3/mod.rs +++ b/crates/networking/rpc/web3/mod.rs @@ -1,8 +1,8 @@ -use ethrex_storage::Store; +use ethereum_rust_storage::Store; use serde_json::Value; use crate::utils::{RpcErr, RpcRequest}; pub fn client_version(_req: &RpcRequest, _store: Store) -> Result { - Ok(Value::String("ethrex@0.1.0".to_owned())) + Ok(Value::String("ethereum_rust@0.1.0".to_owned())) } diff --git a/crates/storage/store/Cargo.toml b/crates/storage/store/Cargo.toml index 94cb2b178..92c8422e3 100644 --- a/crates/storage/store/Cargo.toml +++ b/crates/storage/store/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethrex-storage" +name = "ethereum_rust-storage" version = "0.1.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -ethrex-rlp.workspace = true -ethrex-core = { path = "../../common", default-features = false } -ethrex-trie = { path = "../../storage/trie", default-features = false } +ethereum_rust-rlp.workspace = true +ethereum_rust-core = { path = "../../common", default-features = false } +ethereum_rust-trie = { path = "../../storage/trie", default-features = false } ethereum-types = "0.14.1" anyhow = "1.0.86" @@ -25,8 +25,8 @@ libmdbx = { workspace = true, optional = true } default = ["libmdbx"] libmdbx = [ "dep:libmdbx", - "ethrex-trie/libmdbx", - "ethrex-core/libmdbx", + "ethereum_rust-trie/libmdbx", + "ethereum_rust-core/libmdbx", ] [dev-dependencies] diff --git a/crates/storage/store/engines/api.rs b/crates/storage/store/engines/api.rs index cca2e7333..ce1c898b9 100644 --- a/crates/storage/store/engines/api.rs +++ b/crates/storage/store/engines/api.rs @@ -1,12 +1,12 @@ use bytes::Bytes; -use ethereum_types::{H256, U256}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, }; +use ethereum_types::{H256, U256}; use std::{fmt::Debug, panic::RefUnwindSafe}; use crate::error::StoreError; -use ethrex_trie::Trie; +use ethereum_rust_trie::Trie; pub trait StoreEngine: Debug + Send + Sync + RefUnwindSafe { /// Add block header diff --git a/crates/storage/store/engines/in_memory.rs b/crates/storage/store/engines/in_memory.rs index 4d4ed4dbb..afaff9cfa 100644 --- a/crates/storage/store/engines/in_memory.rs +++ b/crates/storage/store/engines/in_memory.rs @@ -1,10 +1,10 @@ use crate::error::StoreError; use bytes::Bytes; -use ethereum_types::{H256, U256}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, }; -use ethrex_trie::{InMemoryTrieDB, Trie}; +use ethereum_rust_trie::{InMemoryTrieDB, Trie}; +use ethereum_types::{H256, U256}; use std::{ collections::HashMap, fmt::Debug, diff --git a/crates/storage/store/engines/libmdbx.rs b/crates/storage/store/engines/libmdbx.rs index 52b28e803..5b4bfa817 100644 --- a/crates/storage/store/engines/libmdbx.rs +++ b/crates/storage/store/engines/libmdbx.rs @@ -6,13 +6,13 @@ use crate::rlp::{ }; use anyhow::Result; use bytes::Bytes; -use ethereum_types::{H256, U256}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Index, Receipt, Transaction, }; -use ethrex_rlp::decode::RLPDecode; -use ethrex_rlp::encode::RLPEncode; -use ethrex_trie::{LibmdbxDupsortTrieDB, LibmdbxTrieDB, Trie}; +use ethereum_rust_rlp::decode::RLPDecode; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_trie::{LibmdbxDupsortTrieDB, LibmdbxTrieDB, Trie}; +use ethereum_types::{H256, U256}; use libmdbx::orm::{Decodable, Encodable, Table}; use libmdbx::{ dupsort, diff --git a/crates/storage/store/error.rs b/crates/storage/store/error.rs index 61687f543..ce8b8f335 100644 --- a/crates/storage/store/error.rs +++ b/crates/storage/store/error.rs @@ -1,5 +1,5 @@ -use ethrex_rlp::error::RLPDecodeError; -use ethrex_trie::TrieError; +use ethereum_rust_rlp::error::RLPDecodeError; +use ethereum_rust_trie::TrieError; use thiserror::Error; // TODO improve errors diff --git a/crates/storage/store/rlp.rs b/crates/storage/store/rlp.rs index 50991db8e..7d2aca7d3 100644 --- a/crates/storage/store/rlp.rs +++ b/crates/storage/store/rlp.rs @@ -1,12 +1,12 @@ use std::marker::PhantomData; use bytes::Bytes; -use ethereum_types::U256; -use ethrex_core::{ +use ethereum_rust_core::{ types::{Block, BlockBody, BlockHash, BlockHeader, Receipt}, H256, }; -use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethereum_types::U256; #[cfg(feature = "libmdbx")] use libmdbx::orm::{Decodable, Encodable}; diff --git a/crates/storage/store/storage.rs b/crates/storage/store/storage.rs index 0f43e67a9..6e440ae17 100644 --- a/crates/storage/store/storage.rs +++ b/crates/storage/store/storage.rs @@ -4,15 +4,15 @@ use self::engines::libmdbx::Store as LibmdbxStore; use self::error::StoreError; use bytes::Bytes; use engines::api::StoreEngine; -use ethereum_types::{Address, H256, U256}; -use ethrex_core::types::{ +use ethereum_rust_core::types::{ code_hash, AccountInfo, AccountState, BlobsBundle, Block, BlockBody, BlockHash, BlockHeader, BlockNumber, ChainConfig, Genesis, GenesisAccount, Index, MempoolTransaction, Receipt, Transaction, TxType, EMPTY_TRIE_HASH, }; -use ethrex_rlp::decode::RLPDecode; -use ethrex_rlp::encode::RLPEncode; -use ethrex_trie::Trie; +use ethereum_rust_rlp::decode::RLPDecode; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_trie::Trie; +use ethereum_types::{Address, H256, U256}; use sha3::{Digest as _, Keccak256}; use std::collections::HashMap; use std::fmt::Debug; @@ -915,12 +915,12 @@ mod tests { use std::{fs, panic, str::FromStr}; use bytes::Bytes; - use ethereum_types::{H256, U256}; - use ethrex_core::{ + use ethereum_rust_core::{ types::{Transaction, TxType, BYTES_PER_BLOB}, Bloom, }; - use ethrex_rlp::decode::RLPDecode; + use ethereum_rust_rlp::decode::RLPDecode; + use ethereum_types::{H256, U256}; use super::*; diff --git a/crates/storage/trie/Cargo.toml b/crates/storage/trie/Cargo.toml index a9069e488..25ddbdccf 100644 --- a/crates/storage/trie/Cargo.toml +++ b/crates/storage/trie/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "ethrex-trie" +name = "ethereum_rust-trie" version.workspace = true edition.workspace = true [dependencies] -ethrex-rlp.workspace = true +ethereum_rust-rlp.workspace = true ethereum-types = "0.14.1" anyhow = "1.0.86" @@ -16,6 +16,8 @@ hex.workspace = true serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" libmdbx = { workspace = true, optional = true } + +# trie deps smallvec = { version = "1.10.0", features = ["const_generics", "union"] } digest = "0.10.6" lazy_static.workspace = true diff --git a/crates/storage/trie/error.rs b/crates/storage/trie/error.rs index fe3bbfd37..f7cdf60e9 100644 --- a/crates/storage/trie/error.rs +++ b/crates/storage/trie/error.rs @@ -1,4 +1,4 @@ -use ethrex_rlp::error::RLPDecodeError; +use ethereum_rust_rlp::error::RLPDecodeError; use thiserror::Error; #[derive(Debug, Error)] @@ -7,6 +7,4 @@ pub enum TrieError { LibmdbxError(anyhow::Error), #[error(transparent)] RLPDecode(#[from] RLPDecodeError), - #[error("Verification Error: {0}")] - Verify(String), } diff --git a/crates/storage/trie/nibbles.rs b/crates/storage/trie/nibbles.rs index 6b7a91d3c..8c7f4b33b 100644 --- a/crates/storage/trie/nibbles.rs +++ b/crates/storage/trie/nibbles.rs @@ -1,6 +1,4 @@ -use std::cmp; - -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, @@ -58,14 +56,6 @@ impl Nibbles { } } - pub fn compare_prefix(&self, prefix: &Nibbles) -> cmp::Ordering { - if self.len() > prefix.len() { - self.data[..prefix.len()].cmp(&prefix.data) - } else { - self.data.cmp(&prefix.data) - } - } - /// Compares self to another and returns the shared nibble count (amount of nibbles that are equal, from the start) pub fn count_prefix(&self, other: &Nibbles) -> usize { self.as_ref() diff --git a/crates/storage/trie/node.rs b/crates/storage/trie/node.rs index 92c634a46..d24ccb49e 100644 --- a/crates/storage/trie/node.rs +++ b/crates/storage/trie/node.rs @@ -5,8 +5,8 @@ mod leaf; use std::array; pub use branch::BranchNode; +use ethereum_rust_rlp::{decode::decode_bytes, error::RLPDecodeError, structs::Decoder}; use ethereum_types::H256; -use ethrex_rlp::{decode::decode_bytes, error::RLPDecodeError, structs::Decoder}; pub use extension::ExtensionNode; pub use leaf::LeafNode; diff --git a/crates/storage/trie/node/branch.rs b/crates/storage/trie/node/branch.rs index 83b95b541..0b42455af 100644 --- a/crates/storage/trie/node/branch.rs +++ b/crates/storage/trie/node/branch.rs @@ -1,4 +1,4 @@ -use ethrex_rlp::structs::Encoder; +use ethereum_rust_rlp::structs::Encoder; use crate::{error::TrieError, nibbles::Nibbles, node_hash::NodeHash, state::TrieState, ValueRLP}; diff --git a/crates/storage/trie/node/extension.rs b/crates/storage/trie/node/extension.rs index a104716cf..ae1bd8c5c 100644 --- a/crates/storage/trie/node/extension.rs +++ b/crates/storage/trie/node/extension.rs @@ -1,4 +1,4 @@ -use ethrex_rlp::structs::Encoder; +use ethereum_rust_rlp::structs::Encoder; use crate::error::TrieError; use crate::nibbles::Nibbles; diff --git a/crates/storage/trie/node/leaf.rs b/crates/storage/trie/node/leaf.rs index f27cedfb5..969bd4bea 100644 --- a/crates/storage/trie/node/leaf.rs +++ b/crates/storage/trie/node/leaf.rs @@ -1,4 +1,4 @@ -use ethrex_rlp::structs::Encoder; +use ethereum_rust_rlp::structs::Encoder; use crate::{ error::TrieError, nibbles::Nibbles, node::BranchNode, node_hash::NodeHash, state::TrieState, diff --git a/crates/storage/trie/node_hash.rs b/crates/storage/trie/node_hash.rs index e4a6397ed..655e98647 100644 --- a/crates/storage/trie/node_hash.rs +++ b/crates/storage/trie/node_hash.rs @@ -1,5 +1,5 @@ +use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; use ethereum_types::H256; -use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; #[cfg(feature = "libmdbx")] use libmdbx::orm::{Decodable, Encodable}; use sha3::{Digest, Keccak256}; @@ -123,7 +123,9 @@ impl RLPEncode for NodeHash { } impl RLPDecode for NodeHash { - fn decode_unfinished(rlp: &[u8]) -> Result<(Self, &[u8]), ethrex_rlp::error::RLPDecodeError> { + fn decode_unfinished( + rlp: &[u8], + ) -> Result<(Self, &[u8]), ethereum_rust_rlp::error::RLPDecodeError> { let (hash, rest): (Vec, &[u8]); (hash, rest) = RLPDecode::decode_unfinished(rlp)?; let hash = NodeHash::from(hash); diff --git a/crates/storage/trie/rlp.rs b/crates/storage/trie/rlp.rs index 5e8a139c0..13eff2282 100644 --- a/crates/storage/trie/rlp.rs +++ b/crates/storage/trie/rlp.rs @@ -1,6 +1,6 @@ // Contains RLP encoding and decoding implementations for Trie Nodes // This encoding is only used to store the nodes in the DB, it is not the encoding used for hash computation -use ethrex_rlp::{ +use ethereum_rust_rlp::{ decode::RLPDecode, encode::RLPEncode, error::RLPDecodeError, diff --git a/crates/storage/trie/state.rs b/crates/storage/trie/state.rs index be42925e9..213e3716d 100644 --- a/crates/storage/trie/state.rs +++ b/crates/storage/trie/state.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::error::TrieError; -use ethrex_rlp::{decode::RLPDecode, encode::RLPEncode}; +use ethereum_rust_rlp::{decode::RLPDecode, encode::RLPEncode}; use super::db::TrieDB; diff --git a/crates/storage/trie/trie.rs b/crates/storage/trie/trie.rs index 389bb5e7a..f4e4d707e 100644 --- a/crates/storage/trie/trie.rs +++ b/crates/storage/trie/trie.rs @@ -1,17 +1,17 @@ mod db; mod error; -mod nibbles; mod node; mod node_hash; mod rlp; mod state; +mod trie_iter; + +mod nibbles; #[cfg(test)] mod test_utils; -mod trie_iter; -mod verify_range; +use ethereum_rust_rlp::constants::RLP_NULL; use ethereum_types::H256; -use ethrex_rlp::constants::RLP_NULL; use nibbles::Nibbles; use node::Node; use node_hash::NodeHash; @@ -21,7 +21,6 @@ use sha3::{Digest, Keccak256}; pub use self::db::{libmdbx::LibmdbxTrieDB, libmdbx_dupsort::LibmdbxDupsortTrieDB}; pub use self::db::{in_memory::InMemoryTrieDB, TrieDB}; -pub use self::verify_range::verify_range; pub use self::error::TrieError; use self::{node::LeafNode, state::TrieState, trie_iter::TrieIterator}; @@ -169,8 +168,19 @@ impl Trie { // 2. the trie contains the (key, value) pair to verify // We will only be using the trie's cache so we don't need a working DB + // struct NullTrieDB; + + // impl TrieDB for NullTrieDB { + // fn get(&self, _key: Vec) -> Result>, TrieError> { + // Ok(None) + // } - // let mut trie = Trie::stateless(); + // fn put(&self, _key: Vec, _value: Vec) -> Result<(), TrieError> { + // Ok(()) + // } + // } + + // let mut trie = Trie::new(Box::new(NullTrieDB)); // Insert root into trie // let mut proof = proof.into_iter(); @@ -203,20 +213,6 @@ impl Trie { pub fn compute_hash_from_unsorted_iter( iter: impl Iterator, ) -> H256 { - let mut trie = Trie::stateless(); - for (path, value) in iter { - // Unwraping here won't panic as our in_memory trie DB won't fail - trie.insert(path, value).unwrap(); - } - trie.root - .as_ref() - .map(|root| root.clone().finalize()) - .unwrap_or(*EMPTY_TRIE_HASH) - } - - /// Creates a new stateless trie. This trie won't be able to store any nodes so all data will be lost after calculating the hash - /// Only use it for proof verification or computing a hash from an iterator - pub(crate) fn stateless() -> Trie { // We will only be using the trie's cache so we don't need a working DB struct NullTrieDB; @@ -230,7 +226,15 @@ impl Trie { } } - Trie::new(Box::new(NullTrieDB)) + let mut trie = Trie::new(Box::new(NullTrieDB)); + for (path, value) in iter { + // Unwraping here won't panic as our in_memory trie DB won't fail + trie.insert(path, value).unwrap(); + } + trie.root + .as_ref() + .map(|root| root.clone().finalize()) + .unwrap_or(*EMPTY_TRIE_HASH) } /// Obtain the encoded node given its path. diff --git a/crates/storage/trie/verify_range.rs b/crates/storage/trie/verify_range.rs deleted file mode 100644 index 836535433..000000000 --- a/crates/storage/trie/verify_range.rs +++ /dev/null @@ -1,841 +0,0 @@ -use std::{cmp::Ordering, collections::HashMap}; - -use ethereum_types::H256; -use sha3::{Digest, Keccak256}; - -use crate::{ - nibbles::Nibbles, node::Node, node_hash::NodeHash, state::TrieState, Trie, TrieError, ValueRLP, -}; - -/// Verifies that the key value range belongs to the trie with the given root given the edge proofs for the range -/// Also returns true if there is more state to be fetched (aka if there are more keys to the right of the given range) -pub fn verify_range( - root: H256, - first_key: &H256, - keys: &[H256], - values: &[ValueRLP], - proof: &[Vec], -) -> Result { - // Store proof nodes by hash - let proof_nodes = ProofNodeStorage::from_proof(proof); - // Validate range - if keys.len() != values.len() { - return Err(TrieError::Verify(format!( - "inconsistent proof data, got {} keys and {} values", - keys.len(), - values.len() - ))); - } - // Check that the key range is monotonically increasing - for keys in keys.windows(2) { - if keys[0] >= keys[1] { - return Err(TrieError::Verify(String::from( - "key range is not monotonically increasing", - ))); - } - } - // Check for empty values - if values.iter().any(|value| value.is_empty()) { - return Err(TrieError::Verify(String::from( - "value range contains empty value", - ))); - } - - // Verify ranges depending on the given proof - let mut trie = Trie::stateless(); - - // Special Case: No proofs given, the range is expected to be the full set of leaves - if proof.is_empty() { - // Check that the trie constructed from the given keys and values has the expected root - for (key, value) in keys.iter().zip(values.iter()) { - trie.insert(key.0.to_vec(), value.clone())?; - } - let hash = trie.hash()?; - if hash != root { - return Err(TrieError::Verify(format!( - "invalid proof, expected root hash {}, got {}", - root, hash - ))); - } - return Ok(false); - } - - // Special Case: One edge proof, no range given, there are no more values in the trie - if keys.is_empty() { - // We need to check that the proof confirms the non-existance of the first key - // and that there are no more elements to the right of the first key - let value = fill_state(&mut trie.state, root, first_key, &proof_nodes)?; - let has_right_element = has_right_element(root, first_key.as_bytes(), &trie.state)?; - if has_right_element || !value.is_empty() { - return Err(TrieError::Verify( - "no keys returned but more are available on the trie".to_string(), - )); - } else { - return Ok(false); - } - } - - let last_key = keys.last().unwrap(); - - // Special Case: There is only one element and the two edge keys are the same - if keys.len() == 1 && first_key == last_key { - // We need to check that the proof confirms the existance of the first key - let value = fill_state(&mut trie.state, root, first_key, &proof_nodes)?; - if first_key != &keys[0] { - return Err(TrieError::Verify( - "correct proof but invalid key".to_string(), - )); - } - if value != values[0] { - return Err(TrieError::Verify( - "correct proof but invalid data".to_string(), - )); - } - return has_right_element(root, first_key.as_bytes(), &trie.state); - } - - // Regular Case: Two edge proofs - if first_key >= last_key { - return Err(TrieError::Verify("invalid edge keys".to_string())); - } - // Fill up the state with the nodes from the proof - fill_state(&mut trie.state, root, first_key, &proof_nodes)?; - fill_state(&mut trie.state, root, last_key, &proof_nodes)?; - // Remove all references to the internal nodes that belong to the range so they can be reconstructed - let empty = remove_internal_references(root, first_key, last_key, &mut trie.state)?; - if !empty { - trie.root = Some(NodeHash::from(root)); - } - // Reconstruct the internal nodes by inserting the elements on the range - for (key, value) in keys.iter().zip(values.iter()) { - trie.insert(key.0.to_vec(), value.clone())?; - } - // Check for elements to the right of the range before we wipe the sate - let has_right_element = has_right_element(root, last_key.as_bytes(), &trie.state)?; - // Check that the hash is the one we expected (aka the trie was properly reconstructed from the edge proofs and the range) - let hash = trie.hash()?; - if hash != root { - return Err(TrieError::Verify(format!( - "invalid proof, expected root hash {}, got {}", - root, hash - ))); - } - Ok(has_right_element) -} - -/// Fills up the TrieState with nodes from the proof traversing the path given by first_key -/// Returns an error if there are gaps in the proof node path -/// Also returns the value if it is part of the proof -fn fill_state( - trie_state: &mut TrieState, - root_hash: H256, - first_key: &H256, - proof_nodes: &ProofNodeStorage, -) -> Result, TrieError> { - let mut path = Nibbles::from_bytes(&first_key.0); - fill_node( - &mut path, - &NodeHash::from(root_hash), - trie_state, - proof_nodes, - ) -} - -/// Fills up the TrieState with nodes from the proof traversing the path given by first_key -/// Returns an error if there are gaps in the proof node path -/// Also returns the value if it is part of the proof -fn fill_node( - path: &mut Nibbles, - node_hash: &NodeHash, - trie_state: &mut TrieState, - proof_nodes: &ProofNodeStorage, -) -> Result, TrieError> { - let node = proof_nodes.get_node(node_hash)?; - let child_hash = get_child(path, &node); - if let Some(ref child_hash) = child_hash { - trie_state.insert_node(node, node_hash.clone()); - fill_node(path, child_hash, trie_state, proof_nodes) - } else { - let value = match &node { - Node::Branch(n) => n.value.clone(), - Node::Extension(_) => vec![], - Node::Leaf(n) => (*path == n.partial) - .then_some(n.value.clone()) - .unwrap_or_default(), - }; - trie_state.insert_node(node, node_hash.clone()); - Ok(value) - } -} - -/// Returns the node hash of the node's child (if any) following the given path -fn get_child<'a>(path: &'a mut Nibbles, node: &'a Node) -> Option { - match node { - Node::Branch(n) => { - if let Some(choice) = path.next_choice() { - if n.choices[choice].is_valid() { - return Some(n.choices[choice].clone()); - } - } - None - } - Node::Extension(n) => path.skip_prefix(&n.prefix).then_some(n.child.clone()), - Node::Leaf(_) => None, - } -} - -/// Returns true if the trie contains elements to the right of the given key -/// (Aka if the given key is not the edge key of the trie) -fn has_right_element( - root_hash: H256, - key: &[u8], - trie_state: &TrieState, -) -> Result { - let path = Nibbles::from_bytes(key); - has_right_element_inner(root_hash.into(), path, trie_state) -} - -/// Returns true if the node's subtrie contains elements to the right of the given key -/// (Aka if the given key is not the edge key of the subtrie) -fn has_right_element_inner( - node_hash: NodeHash, - mut path: Nibbles, - trie_state: &TrieState, -) -> Result { - let Ok(Some(node)) = trie_state.get_node(node_hash.clone()) else { - return Ok(false); - }; - match node { - Node::Branch(ref n) => { - // Check if there are children to the right side - if let Some(choice) = path.next_choice() { - if n.choices[choice + 1..].iter().any(|child| child.is_valid()) { - return Ok(true); - } else if n.choices[choice].is_valid() { - return has_right_element_inner(n.choices[choice].clone(), path, trie_state); - } - } - } - Node::Extension(n) => { - if path.skip_prefix(&n.prefix) { - return has_right_element_inner(n.child, path, trie_state); - } else { - return Ok(n.prefix.as_ref() > path.as_ref()); - } - } - // We reached the end of the path - Node::Leaf(_) => {} - } - Ok(false) -} - -/// Removes references to internal nodes between the left and right key -/// These nodes should be entirely reconstructed when inserting the elements between left and right key (the proven range) -/// Returns true if the trie is left empty (rootless) as a result of this process -/// Asumes that left_key & right_key are not equal and of same length -fn remove_internal_references( - root_hash: H256, - left_key: &H256, - right_key: &H256, - trie_state: &mut TrieState, -) -> Result { - // First find the node at which the left and right path differ - let left_path = Nibbles::from_bytes(&left_key.0); - let right_path = Nibbles::from_bytes(&right_key.0); - - remove_internal_references_inner(NodeHash::from(root_hash), left_path, right_path, trie_state) -} - -/// Traverses the left and right path starting from the given node until the paths diverge -/// Once the paths diverge, removes the nodes between the left and right path -/// Returns true if the given node was completely removed as a result of this process -/// In which case the caller should remove the reference to this node from its parent node -/// Asumes that left_key & right_key are not equal and of same length -fn remove_internal_references_inner( - node_hash: NodeHash, - mut left_path: Nibbles, - mut right_path: Nibbles, - trie_state: &mut TrieState, -) -> Result { - if !node_hash.is_valid() { - return Ok(true); - } - // We already looked up the nodes when filling the state so this shouldn't fail - let node = trie_state.get_node(node_hash.clone())?.unwrap(); - match node { - Node::Branch(mut n) => { - // If none of the paths have a next choice nibble then it means that this is the end of the path - // which would mean that both paths are equal, which we already checked before - // If only one path doesn't have a next choice then it would mean that the paths have different lengths, - // which we also checked before calling this function - // Therefore we can safely unwrap here - let left_choice = left_path.next_choice().unwrap(); - let right_choice = right_path.next_choice().unwrap(); - - if left_choice == right_choice && n.choices[left_choice].is_valid() { - // Keep going - // Check if the child extension node should be removed as a result of this process - let should_remove = remove_internal_references_inner( - n.choices[left_choice].clone(), - left_path, - right_path, - trie_state, - )?; - if should_remove { - // Remove child node - n.choices[left_choice] = NodeHash::default(); - // Update node in the state - trie_state.insert_node(n.into(), node_hash); - } - } else { - // We found our fork node, now we can remove the internal references - // Remove all child nodes between the left and right child nodes - for choice in &mut n.choices[left_choice + 1..right_choice] { - *choice = NodeHash::default() - } - // Remove nodes on the left and right choice's subtries - let should_remove_left = - remove_node(n.choices[left_choice].clone(), left_path, false, trie_state); - let should_remove_right = remove_node( - n.choices[right_choice].clone(), - right_path, - true, - trie_state, - ); - // Remove left and right child nodes if their subtries where wiped in the process - if should_remove_left { - n.choices[left_choice] = NodeHash::default(); - } - if should_remove_right { - n.choices[right_choice] = NodeHash::default(); - } - // Update node in the state - trie_state.insert_node(n.into(), node_hash); - } - } - Node::Extension(n) => { - // Compare left and right paths against prefix - - let left_fork = left_path.compare_prefix(&n.prefix); - let right_fork = right_path.compare_prefix(&n.prefix); - - match (left_fork, right_fork) { - // If both paths contain the same prefix as the extension node, keep going - (Ordering::Equal, Ordering::Equal) => { - return remove_internal_references_inner( - n.child, - left_path.offset(n.prefix.len()), - right_path.offset(n.prefix.len()), - trie_state, - ); - } - // If both paths are greater or lesser than the node's prefix then the range is empty - (Ordering::Greater, Ordering::Greater) | (Ordering::Less, Ordering::Less) => { - return Err(TrieError::Verify("empty range".to_string())) - } - // None of the paths fit the prefix, remove the entire subtrie - (left, right) if left.is_ne() && right.is_ne() => { - // Return true so that the parent node removes this node - return Ok(true); - } - // One path fits the prefix, the other one doesn't - (left, right) => { - // Remove the nodes from the child's subtrie - let path = if left.is_eq() { left_path } else { right_path }; - // Propagate the response so that this node will be removed too if the child's subtrie is wiped - return Ok(remove_node( - node_hash, - path.offset(n.prefix.len()), - right.is_eq(), - trie_state, - )); - } - } - } - // This case should be unreachable as we checked that left_path != right_path - // before calling this function - Node::Leaf(_) => {} - } - Ok(false) -} - -// Removes all nodes in the node's subtrie to the left or right of the path (given by the `remove_left` flag) -// If the whole subtrie is removed in the process this function will return true, in which case -// the caller must remove the reference to this node from it's parent node -fn remove_node( - node_hash: NodeHash, - mut path: Nibbles, - remove_left: bool, - trie_state: &mut TrieState, -) -> bool { - // Node doesn't exist already, no need to remove it - if !node_hash.is_valid() { - return false; - } - // We already looked up the nodes when filling the state so this shouldn't fail - let node = trie_state.get_node(node_hash.clone()).unwrap().unwrap(); - match node { - Node::Branch(mut n) => { - // Remove child nodes - let Some(choice) = path.next_choice() else { - // Path ends in the branch node - return true; - }; - if remove_left { - for child in &mut n.choices[..choice] { - *child = NodeHash::default() - } - } else { - for child in &mut n.choices[choice + 1..] { - *child = NodeHash::default() - } - } - // Remove nodes to the left/right of the choice's subtrie - let should_remove = - remove_node(n.choices[choice].clone(), path, remove_left, trie_state); - if should_remove { - n.choices[choice] = NodeHash::default(); - } - // Update node in the state - trie_state.insert_node(n.into(), node_hash); - } - Node::Extension(n) => { - // If no child subtrie would result from this process remove the node entirely - // (Such as removing the left side of a trie with no right side) - if !path.skip_prefix(&n.prefix) { - if (remove_left && path.compare_prefix(&n.prefix).is_gt()) - || !remove_left && path.compare_prefix(&n.prefix).is_lt() - { - return true; - } - } else { - // Remove left/right side of the child subtrie - return remove_node(n.child, path, remove_left, trie_state); - } - } - Node::Leaf(_) => return true, - } - false -} - -/// An intermediate storage for proof nodes, containing encoded nodes indexed by hash -struct ProofNodeStorage<'a> { - nodes: HashMap, &'a Vec>, -} - -impl<'a> ProofNodeStorage<'a> { - // Construct a ProofNodeStorage for a proof - fn from_proof(proof: &'a [Vec]) -> Self { - Self { - nodes: proof - .iter() - .map(|node| (Keccak256::new_with_prefix(node).finalize().to_vec(), node)) - .collect::>(), - } - } - // Fetch a node by its hash, return an error if the node is not present or badly encoded - fn get_node(&self, hash: &NodeHash) -> Result { - let encoded = match hash { - NodeHash::Hashed(hash) => { - let Some(encoded) = self.nodes.get(hash.as_bytes()) else { - return Err(TrieError::Verify(format!("proof node missing: {hash}"))); - }; - *encoded - } - - NodeHash::Inline(ref encoded) => encoded, - }; - Ok(Node::decode_raw(encoded)?) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::collection::{btree_set, vec}; - use proptest::prelude::any; - use proptest::{bool, proptest}; - use std::str::FromStr; - - #[test] - fn verify_range_regular_case_only_branch_nodes() { - // The trie will have keys and values ranging from 25-100 - // We will prove the range from 50-75 - // Note values are written as hashes in the form i -> [i;32] - let mut trie = Trie::new_temp(); - for k in 25..100_u8 { - trie.insert([k; 32].to_vec(), [k; 32].to_vec()).unwrap() - } - let mut proof = trie.get_proof(&[50; 32].to_vec()).unwrap(); - proof.extend(trie.get_proof(&[75; 32].to_vec()).unwrap()); - let root = trie.hash().unwrap(); - let keys = (50_u8..=75).map(|i| H256([i; 32])).collect::>(); - let values = (50_u8..=75).map(|i| [i; 32].to_vec()).collect::>(); - let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); - // Our trie contains more elements to the right - assert!(fetch_more) - } - - #[test] - fn verify_range_regular_case() { - // The account ranges were taken form a hive test state, but artificially modified - // so that the resulting trie has a wide variety of different nodes (and not only branches) - let account_addresses: [&str; 26] = [ - "0xaa56789abcde80cde11add7d3447cd4ca93a5f2205d9874261484ae180718bd6", - "0xaa56789abcdeda9ae19dd26a33bd10bbf825e28b3de84fc8fe1d15a21645067f", - "0xaa56789abc39a8284ef43790e3a511b2caa50803613c5096bc782e8de08fa4c5", - "0xaa5678931f4754834b0502de5b0342ceff21cde5bef386a83d2292f4445782c2", - "0xaa567896492bfe767f3d18be2aab96441c449cd945770ef7ef8555acc505b2e4", - "0xaa5f478d53bf78add6fa3708d9e061d59bfe14b21329b2a4cf1156d4f81b3d2d", - "0xaa67c643f67b47cac9efacf6fcf0e4f4e1b273a727ded155db60eb9907939eb6", - "0xaa04d8eaccf0b942c468074250cbcb625ec5c4688b6b5d17d2a9bdd8dd565d5a", - "0xaa63e52cda557221b0b66bd7285b043071df4c2ab146260f4e010970f3a0cccf", - "0xaad9aa4f67f8b24d70a0ffd757e82456d9184113106b7d9e8eb6c3e8a8df27ee", - "0xaa3df2c3b574026812b154a99b13b626220af85cd01bb1693b1d42591054bce6", - "0xaa79e46a5ed8a88504ac7d579b12eb346fbe4fd7e281bdd226b891f8abed4789", - "0xbbf68e241fff876598e8e01cd529bd76416b248caf11e0552047c5f1d516aab6", - "0xbbf68e241fff876598e8e01cd529c908cdf0d646049b5b83629a70b0117e2957", - "0xbbf68e241fff876598e8e0180b89744abb96f7af1171ed5f47026bdf01df1874", - "0xbbf68e241fff876598e8a4cd8e43f08be4715d903a0b1d96b3d9c4e811cbfb33", - "0xbbf68e241fff8765182a510994e2b54d14b731fac96b9c9ef434bc1924315371", - "0xbbf68e241fff87655379a3b66c2d8983ba0b2ca87abaf0ca44836b2a06a2b102", - "0xbbf68e241fffcbcec8301709a7449e2e7371910778df64c89f48507390f2d129", - "0xbbf68e241ffff228ed3aa7a29644b1915fde9ec22e0433808bf5467d914e7c7a", - "0xbbf68e24190b881949ec9991e48dec768ccd1980896aefd0d51fd56fd5689790", - "0xbbf68e2419de0a0cb0ff268c677aba17d39a3190fe15aec0ff7f54184955cba4", - "0xbbf68e24cc6cbd96c1400150417dd9b30d958c58f63c36230a90a02b076f78b5", - "0xbbf68e2490f33f1d1ba6d1521a00935630d2c81ab12fa03d4a0f4915033134f3", - "0xc017b10a7cc3732d729fe1f71ced25e5b7bc73dc62ca61309a8c7e5ac0af2f72", - "0xc098f06082dc467088ecedb143f9464ebb02f19dc10bd7491b03ba68d751ce45", - ]; - let mut account_addresses = account_addresses - .iter() - .map(|addr| H256::from_str(addr).unwrap()) - .collect::>(); - account_addresses.sort(); - let trie_values = account_addresses - .iter() - .map(|addr| addr.0.to_vec()) - .collect::>(); - let keys = account_addresses[7..=17].to_vec(); - let values = account_addresses[7..=17] - .iter() - .map(|v| v.0.to_vec()) - .collect::>(); - let mut trie = Trie::new_temp(); - for val in trie_values.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let mut proof = trie.get_proof(&trie_values[7]).unwrap(); - proof.extend(trie.get_proof(&trie_values[17]).unwrap()); - let root = trie.hash().unwrap(); - let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); - // Our trie contains more elements to the right - assert!(fetch_more) - } - - // Proptests for verify_range - proptest! { - - // Successful Cases - - #[test] - // Regular Case: Two Edge Proofs, both keys exist - fn proptest_verify_range_regular_case(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>()[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Generate proofs - let mut proof = trie.get_proof(&values[0]).unwrap(); - proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); - // Verify the range proof - let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); - if end == 199 { - // The last key is at the edge of the trie - assert!(!fetch_more) - } else { - // Our trie contains more elements to the right - assert!(fetch_more) - } - } - - #[test] - // Two Edge Proofs, first and last keys dont exist - fn proptest_verify_range_nonexistant_edge_keys(data in btree_set(vec(1..u8::MAX-1, 32), 200), start in 1_usize..=100_usize, end in 101..199_usize) { - let data = data.into_iter().collect::>(); - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Select the first and last keys - // As we will be using non-existant keys we will choose values that are `just` higer/lower than - // the first and last values in our key range - // Skip the test entirely in the unlucky case that the values just next to the edge keys are also part of the trie - let mut first_key = data[start].clone(); - first_key[31] -=1; - if first_key == data[start -1] { - // Skip test - return Ok(()); - } - let mut last_key = data[end].clone(); - last_key[31] +=1; - if last_key == data[end +1] { - // Skip test - return Ok(()); - } - // Generate proofs - let mut proof = trie.get_proof(&first_key).unwrap(); - proof.extend(trie.get_proof(&last_key).unwrap()); - // Verify the range proof - let fetch_more = verify_range(root, &H256::from_slice(&first_key), &keys, &values, &proof).unwrap(); - // Our trie contains more elements to the right - assert!(fetch_more) - } - - #[test] - // Two Edge Proofs, one key doesn't exist - fn proptest_verify_range_one_key_doesnt_exist(data in btree_set(vec(1..u8::MAX-1, 32), 200), start in 1_usize..=100_usize, end in 101..199_usize, first_key_exists in bool::ANY) { - let data = data.into_iter().collect::>(); - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Select the first and last keys - // As we will be using non-existant keys we will choose values that are `just` higer/lower than - // the first and last values in our key range - // Skip the test entirely in the unlucky case that the values just next to the edge keys are also part of the trie - let mut first_key = data[start].clone(); - let mut last_key = data[end].clone(); - if first_key_exists { - last_key[31] +=1; - if last_key == data[end +1] { - // Skip test - return Ok(()); - } - } else { - first_key[31] -=1; - if first_key == data[start -1] { - // Skip test - return Ok(()); - } - } - // Generate proofs - let mut proof = trie.get_proof(&first_key).unwrap(); - proof.extend(trie.get_proof(&last_key).unwrap()); - // Verify the range proof - let fetch_more = verify_range(root, &H256::from_slice(&first_key), &keys, &values, &proof).unwrap(); - // Our trie contains more elements to the right - assert!(fetch_more) - } - - #[test] - // Special Case: Range contains all the leafs in the trie, no proofs - fn proptest_verify_range_full_leafset(data in btree_set(vec(any::(), 32), 100..200)) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // The keyset contains the entire trie so we don't need edge proofs - let proof = vec![]; - // Verify the range proof - let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); - // Our range is the full leafset, there shouldn't be more values left in the trie - assert!(!fetch_more) - } - - #[test] - // Special Case: No values, one edge proof (of non-existance) - fn proptest_verify_range_no_values(mut data in btree_set(vec(any::(), 32), 100..200)) { - // Remove the last element so we can use it as key for the proof of non-existance - let last_element = data.pop_last().unwrap(); - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Range is empty - let values = vec![]; - let keys = vec![]; - let first_key = H256::from_slice(&last_element); - // Generate proof (last element) - let proof = trie.get_proof(&last_element).unwrap(); - // Verify the range proof - let fetch_more = verify_range(root, &first_key, &keys, &values, &proof).unwrap(); - // There are no more elements to the right of the range - assert!(!fetch_more) - } - - #[test] - // Special Case: One element range - fn proptest_verify_range_one_element(data in btree_set(vec(any::(), 32), 200), start in 0_usize..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = vec![data.iter().collect::>()[start].clone()]; - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Generate proofs - let proof = trie.get_proof(&values[0]).unwrap(); - // Verify the range proof - let fetch_more = verify_range(root, &keys[0], &keys, &values, &proof).unwrap(); - if start == 199 { - // The last key is at the edge of the trie - assert!(!fetch_more) - } else { - // Our trie contains more elements to the right - assert!(fetch_more) - } - } - - // Unsuccesful Cases - - #[test] - // Regular Case: Only one edge proof, both keys exist - fn proptest_verify_range_regular_case_only_one_edge_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>()[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Generate proofs (only prove first key) - let proof = trie.get_proof(&values[0]).unwrap(); - // Verify the range proof - assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); - } - - #[test] - // Regular Case: Two Edge Proofs, both keys exist, but there is a missing node in the proof - fn proptest_verify_range_regular_case_gap_in_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>()[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Generate proofs - let mut proof = trie.get_proof(&values[0]).unwrap(); - proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); - // Remove the last node of the second proof (to make sure we don't remove a node that is also part of the first proof) - proof.pop(); - // Verify the range proof - assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); - } - - #[test] - // Regular Case: Two Edge Proofs, both keys exist, but there is a missing node in the proof - fn proptest_verify_range_regular_case_gap_in_middle_of_proof(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>()[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Generate proofs - let mut proof = trie.get_proof(&values[0]).unwrap(); - let mut second_proof = trie.get_proof(&values[0]).unwrap(); - proof.extend(trie.get_proof(values.last().unwrap()).unwrap()); - // Remove the middle node of the second proof - let gap_idx = second_proof.len() / 2; - let removed = second_proof.remove(gap_idx); - // Remove the node from the first proof if it is also there - proof.retain(|n| n != &removed); - proof.extend(second_proof); - // Verify the range proof - assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); - } - - #[test] - // Regular Case: No proofs both keys exist - fn proptest_verify_range_regular_case_no_proofs(data in btree_set(vec(any::(), 32), 200), start in 1_usize..=100_usize, end in 101..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = data.into_iter().collect::>()[start..=end].to_vec(); - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Dont generate proof - let proof = vec![]; - // Verify the range proof - assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); - } - - #[test] - // Special Case: No values, one edge proof (of existance) - fn proptest_verify_range_no_values_proof_of_existance(data in btree_set(vec(any::(), 32), 100..200)) { - // Fetch the last element so we can use it as key for the proof - let last_element = data.last().unwrap(); - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Range is empty - let values = vec![]; - let keys = vec![]; - let first_key = H256::from_slice(last_element); - // Generate proof (last element) - let proof = trie.get_proof(last_element).unwrap(); - // Verify the range proof - assert!(verify_range(root, &first_key, &keys, &values, &proof).is_err()); - } - - #[test] - // Special Case: One element range (but the proof is of nonexistance) - fn proptest_verify_range_one_element_bad_proof(data in btree_set(vec(any::(), 32), 200), start in 0_usize..200_usize) { - // Build trie - let mut trie = Trie::new_temp(); - for val in data.iter() { - trie.insert(val.clone(), val.clone()).unwrap() - } - let root = trie.hash().unwrap(); - // Select range to prove - let values = vec![data.iter().collect::>()[start].clone()]; - let keys = values.iter().map(|a| H256::from_slice(a)).collect::>(); - // Remove the value to generate a proof of non-existance - trie.remove(values[0].clone()).unwrap(); - // Generate proofs - let proof = trie.get_proof(&values[0]).unwrap(); - // Verify the range proof - assert!(verify_range(root, &keys[0], &keys, &values, &proof).is_err()); - } - } -} diff --git a/crates/vm/Cargo.toml b/crates/vm/Cargo.toml index a9892ef84..616712a43 100644 --- a/crates/vm/Cargo.toml +++ b/crates/vm/Cargo.toml @@ -1,14 +1,14 @@ [package] -name = "ethrex-vm" +name = "ethereum_rust-vm" version = "0.1.0" edition = "2021" [dependencies] -ethrex-core = { path = "../common", default-features = false } -ethrex-storage = { path = "../storage/store", default-features = false } -ethrex-levm = { path = "./levm", optional = true } -ethrex-trie = { path = "../storage/trie", default-features = false } -ethrex-rlp = { path = "../common/rlp", default-features = false } +ethereum_rust-core = { path = "../common", default-features = false } +ethereum_rust-storage = { path = "../storage/store", default-features = false } +ethereum_rust-levm = { path = "./levm", optional = true } +ethereum_rust-trie = { path = "../storage/trie", default-features = false } +ethereum_rust-rlp = { path = "../common/rlp", default-features = false } revm = { version = "14.0.3", features = [ "serde", "std", @@ -42,8 +42,8 @@ default = ["libmdbx", "c-kzg", "blst"] l2 = [] c-kzg = ["revm/c-kzg"] blst = ["revm/blst"] -libmdbx = ["ethrex-storage/default", "ethrex-core/libmdbx"] -levm = ["ethrex-levm"] +libmdbx = ["ethereum_rust-storage/default", "ethereum_rust-core/libmdbx"] +levm = ["ethereum_rust-levm"] [profile.test] opt-level = 3 diff --git a/crates/vm/db.rs b/crates/vm/db.rs index d015ffa62..216cfb51a 100644 --- a/crates/vm/db.rs +++ b/crates/vm/db.rs @@ -1,5 +1,5 @@ -use ethrex_core::{types::BlockHash, Address as CoreAddress, H256 as CoreH256}; -use ethrex_storage::{error::StoreError, Store}; +use ethereum_rust_core::{types::BlockHash, Address as CoreAddress, H256 as CoreH256}; +use ethereum_rust_storage::{error::StoreError, Store}; use revm::primitives::{ AccountInfo as RevmAccountInfo, Address as RevmAddress, Bytecode as RevmBytecode, Bytes as RevmBytes, B256 as RevmB256, U256 as RevmU256, @@ -12,11 +12,11 @@ pub struct StoreWrapper { cfg_if::cfg_if! { if #[cfg(feature = "levm")] { - use ethrex_core::{U256 as CoreU256}; - use ethrex_levm::db::Database as LevmDatabase; + use ethereum_rust_core::{U256 as CoreU256}; + use ethereum_rust_levm::db::Database as LevmDatabase; impl LevmDatabase for StoreWrapper { - fn get_account_info(&self, address: CoreAddress) -> ethrex_levm::account::AccountInfo { + fn get_account_info(&self, address: CoreAddress) -> ethereum_rust_levm::account::AccountInfo { let acc_info = self .store .get_account_info_by_hash(self.block_hash, address) @@ -29,7 +29,7 @@ cfg_if::cfg_if! { .unwrap() .unwrap_or_default(); - ethrex_levm::account::AccountInfo { + ethereum_rust_levm::account::AccountInfo { balance: acc_info.balance, nonce: acc_info.nonce, bytecode: acc_code, diff --git a/crates/vm/errors.rs b/crates/vm/errors.rs index a2635b5f6..82ba7c646 100644 --- a/crates/vm/errors.rs +++ b/crates/vm/errors.rs @@ -1,7 +1,7 @@ +use ethereum_rust_core::types::BlockHash; +use ethereum_rust_storage::error::StoreError; +use ethereum_rust_trie::TrieError; use ethereum_types::H160; -use ethrex_core::types::BlockHash; -use ethrex_storage::error::StoreError; -use ethrex_trie::TrieError; use revm::primitives::{ result::EVMError as RevmError, Address as RevmAddress, B256 as RevmB256, U256 as RevmU256, }; diff --git a/crates/vm/execution_db.rs b/crates/vm/execution_db.rs index edf65021c..2b70b8ce4 100644 --- a/crates/vm/execution_db.rs +++ b/crates/vm/execution_db.rs @@ -1,13 +1,13 @@ use std::collections::HashMap; -use ethereum_types::{Address, H160, U256}; -use ethrex_core::{ +use ethereum_rust_core::{ types::{AccountState, Block, ChainConfig}, H256, }; -use ethrex_rlp::encode::RLPEncode; -use ethrex_storage::{hash_address, hash_key, Store}; -use ethrex_trie::Trie; +use ethereum_rust_rlp::encode::RLPEncode; +use ethereum_rust_storage::{hash_address, hash_key, Store}; +use ethereum_rust_trie::Trie; +use ethereum_types::{Address, H160, U256}; use revm::{ primitives::{ AccountInfo as RevmAccountInfo, Address as RevmAddress, Bytecode as RevmBytecode, @@ -72,13 +72,12 @@ impl ExecutionDB { for account_update in account_updates.iter() { let address = RevmAddress::from_slice(account_update.address.as_bytes()); - let account_state = match store.get_account_state_by_hash( - block.header.parent_hash, - H160::from_slice(address.as_slice()), - )? { - Some(state) => state, - None => continue, - }; + let account_state = store + .get_account_state_by_hash( + block.header.parent_hash, + H160::from_slice(address.as_slice()), + )? + .ok_or(ExecutionDBError::NewMissingAccountInfo(address))?; accounts.insert(address, account_state); let account_storage = account_update diff --git a/crates/vm/execution_result.rs b/crates/vm/execution_result.rs index 9f0d37086..1914b7939 100644 --- a/crates/vm/execution_result.rs +++ b/crates/vm/execution_result.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethrex_core::Address; -use ethrex_core::{types::Log, H256}; +use ethereum_rust_core::Address; +use ethereum_rust_core::{types::Log, H256}; use revm::primitives::result::Output as RevmOutput; use revm::primitives::result::SuccessReason as RevmSuccessReason; use revm::primitives::ExecutionResult as RevmExecutionResult; diff --git a/crates/vm/levm/Cargo.toml b/crates/vm/levm/Cargo.toml index 3635db593..827c28636 100644 --- a/crates/vm/levm/Cargo.toml +++ b/crates/vm/levm/Cargo.toml @@ -1,11 +1,11 @@ [package] -name = "ethrex-levm" +name = "ethereum_rust-levm" version.workspace = true edition.workspace = true [dependencies] -ethrex-core.workspace = true -ethrex-rlp.workspace = true +ethereum_rust-core.workspace = true +ethereum_rust-rlp.workspace = true bytes.workspace = true sha3 = "0.10.8" diff --git a/crates/vm/levm/Makefile b/crates/vm/levm/Makefile index b2947f224..316dd74a1 100644 --- a/crates/vm/levm/Makefile +++ b/crates/vm/levm/Makefile @@ -6,7 +6,7 @@ help: ## 📚 Show help for each of the Makefile recipes @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' test: ## 🧪 Runs all tests except Ethereum tests - cargo test -p ethrex-levm + cargo test -p ethereum_rust-levm lint: ## 🧹 Linter check cargo clippy --all-targets --all-features -- -D warnings diff --git a/crates/vm/levm/bench/revm_comparison/Cargo.toml b/crates/vm/levm/bench/revm_comparison/Cargo.toml index 59e2c0431..a97b97b81 100644 --- a/crates/vm/levm/bench/revm_comparison/Cargo.toml +++ b/crates/vm/levm/bench/revm_comparison/Cargo.toml @@ -8,7 +8,7 @@ name = "revm_comparison" path = "src/lib.rs" [dependencies] -ethrex-levm = { path = "../../" } +ethereum_rust-levm = { path = "../../" } hex = "0.4.3" revm = "9.0.0" bytes = "1.8.0" diff --git a/crates/vm/levm/bench/revm_comparison/src/lib.rs b/crates/vm/levm/bench/revm_comparison/src/lib.rs index 1a224d4bd..fa88a2993 100644 --- a/crates/vm/levm/bench/revm_comparison/src/lib.rs +++ b/crates/vm/levm/bench/revm_comparison/src/lib.rs @@ -1,5 +1,5 @@ use bytes::Bytes; -use ethrex_levm::{call_frame::CallFrame, errors::TxResult, utils::new_vm_with_bytecode}; +use ethereum_rust_levm::{call_frame::CallFrame, errors::TxResult, utils::new_vm_with_bytecode}; use revm::{ db::BenchmarkDB, primitives::{address, Bytecode, TransactTo}, diff --git a/crates/vm/levm/src/account.rs b/crates/vm/levm/src/account.rs index da8872a92..558d131bf 100644 --- a/crates/vm/levm/src/account.rs +++ b/crates/vm/levm/src/account.rs @@ -3,7 +3,7 @@ use crate::{ errors::{InternalError, VMError}, }; use bytes::Bytes; -use ethrex_core::{H256, U256}; +use ethereum_rust_core::{H256, U256}; use keccak_hash::keccak; use std::collections::HashMap; diff --git a/crates/vm/levm/src/call_frame.rs b/crates/vm/levm/src/call_frame.rs index 0602e26da..28535a27f 100644 --- a/crates/vm/levm/src/call_frame.rs +++ b/crates/vm/levm/src/call_frame.rs @@ -5,7 +5,7 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethrex_core::{types::Log, Address, U256}; +use ethereum_rust_core::{types::Log, Address, U256}; use std::collections::HashMap; /// [EIP-1153]: https://eips.ethereum.org/EIPS/eip-1153#reference-implementation diff --git a/crates/vm/levm/src/constants.rs b/crates/vm/levm/src/constants.rs index 6b07e6e87..8984a6673 100644 --- a/crates/vm/levm/src/constants.rs +++ b/crates/vm/levm/src/constants.rs @@ -1,4 +1,4 @@ -use ethrex_core::{H256, U256}; +use ethereum_rust_core::{H256, U256}; pub const SUCCESS_FOR_CALL: i32 = 1; pub const REVERT_FOR_CALL: i32 = 0; @@ -27,7 +27,7 @@ pub const MAX_CREATE_CODE_SIZE: usize = 2 * MAX_CODE_SIZE; pub const INVALID_CONTRACT_PREFIX: u8 = 0xef; pub mod create_opcode { - use ethrex_core::U256; + use ethereum_rust_core::U256; pub const INIT_CODE_WORD_COST: U256 = U256([2, 0, 0, 0]); pub const CODE_DEPOSIT_COST: U256 = U256([200, 0, 0, 0]); diff --git a/crates/vm/levm/src/db.rs b/crates/vm/levm/src/db.rs index d9abe7681..f1b124550 100644 --- a/crates/vm/levm/src/db.rs +++ b/crates/vm/levm/src/db.rs @@ -2,7 +2,7 @@ use crate::{ account::{Account, AccountInfo, StorageSlot}, errors::{InternalError, VMError}, }; -use ethrex_core::{Address, H256, U256}; +use ethereum_rust_core::{Address, H256, U256}; use std::collections::HashMap; pub trait Database { diff --git a/crates/vm/levm/src/environment.rs b/crates/vm/levm/src/environment.rs index 3e57703f2..0f5f91ab0 100644 --- a/crates/vm/levm/src/environment.rs +++ b/crates/vm/levm/src/environment.rs @@ -1,5 +1,5 @@ use crate::constants::TX_BASE_COST; -use ethrex_core::{Address, H256, U256}; +use ethereum_rust_core::{Address, H256, U256}; #[derive(Debug, Default, Clone)] pub struct Environment { diff --git a/crates/vm/levm/src/errors.rs b/crates/vm/levm/src/errors.rs index b6bad188c..e7c874218 100644 --- a/crates/vm/levm/src/errors.rs +++ b/crates/vm/levm/src/errors.rs @@ -1,6 +1,6 @@ use crate::account::Account; use bytes::Bytes; -use ethrex_core::{types::Log, Address}; +use ethereum_rust_core::{types::Log, Address}; use std::collections::HashMap; use thiserror; diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 75cdc321e..5b1e31a47 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -6,7 +6,7 @@ use crate::{ }; use bytes::Bytes; /// Contains the gas costs of the EVM instructions (in wei) -use ethrex_core::U256; +use ethereum_rust_core::U256; // Opcodes cost pub const ADD: U256 = U256([3, 0, 0, 0]); diff --git a/crates/vm/levm/src/memory.rs b/crates/vm/levm/src/memory.rs index 2eab4ada7..6c7b9d1a0 100644 --- a/crates/vm/levm/src/memory.rs +++ b/crates/vm/levm/src/memory.rs @@ -2,7 +2,7 @@ use crate::{ constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE}, errors::{InternalError, OutOfGasError, VMError}, }; -use ethrex_core::U256; +use ethereum_rust_core::U256; #[derive(Debug, Clone, Default, PartialEq)] pub struct Memory { diff --git a/crates/vm/levm/src/opcode_handlers/arithmetic.rs b/crates/vm/levm/src/opcode_handlers/arithmetic.rs index 975392dc1..3ab87d470 100644 --- a/crates/vm/levm/src/opcode_handlers/arithmetic.rs +++ b/crates/vm/levm/src/opcode_handlers/arithmetic.rs @@ -5,7 +5,7 @@ use crate::{ opcode_handlers::bitwise_comparison::checked_shift_left, vm::VM, }; -use ethrex_core::{U256, U512}; +use ethereum_rust_core::{U256, U512}; // Arithmetic Operations (11) // Opcodes: ADD, SUB, MUL, DIV, SDIV, MOD, SMOD, ADDMOD, MULMOD, EXP, SIGNEXTEND diff --git a/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs b/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs index c80812430..aef3fb81c 100644 --- a/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs +++ b/crates/vm/levm/src/opcode_handlers/bitwise_comparison.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethrex_core::U256; +use ethereum_rust_core::U256; // Comparison and Bitwise Logic Operations (14) // Opcodes: LT, GT, SLT, SGT, EQ, ISZERO, AND, OR, XOR, NOT, BYTE, SHL, SHR, SAR diff --git a/crates/vm/levm/src/opcode_handlers/block.rs b/crates/vm/levm/src/opcode_handlers/block.rs index d252b4d4a..1b092900f 100644 --- a/crates/vm/levm/src/opcode_handlers/block.rs +++ b/crates/vm/levm/src/opcode_handlers/block.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{address_to_word, VM}, }; -use ethrex_core::{ +use ethereum_rust_core::{ types::{BLOB_BASE_FEE_UPDATE_FRACTION, MIN_BASE_FEE_PER_BLOB_GAS}, H256, U256, }; diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index f0d7e02c8..a3f2c8e11 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{word_to_address, VM}, }; -use ethrex_core::U256; +use ethereum_rust_core::U256; use sha3::{Digest, Keccak256}; // Environmental Information (16) diff --git a/crates/vm/levm/src/opcode_handlers/keccak.rs b/crates/vm/levm/src/opcode_handlers/keccak.rs index 77a11c4df..7b002e8bd 100644 --- a/crates/vm/levm/src/opcode_handlers/keccak.rs +++ b/crates/vm/levm/src/opcode_handlers/keccak.rs @@ -4,7 +4,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethrex_core::U256; +use ethereum_rust_core::U256; use sha3::{Digest, Keccak256}; // KECCAK256 (1) diff --git a/crates/vm/levm/src/opcode_handlers/logging.rs b/crates/vm/levm/src/opcode_handlers/logging.rs index 8efa48268..88d29a255 100644 --- a/crates/vm/levm/src/opcode_handlers/logging.rs +++ b/crates/vm/levm/src/opcode_handlers/logging.rs @@ -6,7 +6,7 @@ use crate::{ vm::VM, }; use bytes::Bytes; -use ethrex_core::{types::Log, H256}; +use ethereum_rust_core::{types::Log, H256}; // Logging Operations (5) // Opcodes: LOG0 ... LOG4 diff --git a/crates/vm/levm/src/opcode_handlers/push.rs b/crates/vm/levm/src/opcode_handlers/push.rs index 2a83c4c65..39bed978b 100644 --- a/crates/vm/levm/src/opcode_handlers/push.rs +++ b/crates/vm/levm/src/opcode_handlers/push.rs @@ -5,7 +5,7 @@ use crate::{ opcodes::Opcode, vm::VM, }; -use ethrex_core::U256; +use ethereum_rust_core::U256; // Push Operations // Opcodes: PUSH0, PUSH1 ... PUSH32 diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index 55b38884d..bb70985d6 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -6,7 +6,7 @@ use crate::{ gas_cost, vm::VM, }; -use ethrex_core::{H256, U256}; +use ethereum_rust_core::{H256, U256}; // Stack, Memory, Storage and Flow Operations (15) // Opcodes: POP, MLOAD, MSTORE, MSTORE8, SLOAD, SSTORE, JUMP, JUMPI, PC, MSIZE, GAS, JUMPDEST, TLOAD, TSTORE, MCOPY @@ -159,7 +159,7 @@ impl VM { } // SSTORE operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1087 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1087 pub fn op_sstore( &mut self, current_call_frame: &mut CallFrame, diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 1012a2e95..ab42f8837 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -5,7 +5,7 @@ use crate::{ gas_cost, vm::{word_to_address, VM}, }; -use ethrex_core::{types::TxKind, U256}; +use ethereum_rust_core::{types::TxKind, U256}; // System Operations (10) // Opcodes: CREATE, CALL, CALLCODE, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT @@ -87,7 +87,7 @@ impl VM { } // CALLCODE operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 pub fn op_callcode( &mut self, current_call_frame: &mut CallFrame, @@ -192,7 +192,7 @@ impl VM { } // DELEGATECALL operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 pub fn op_delegatecall( &mut self, current_call_frame: &mut CallFrame, @@ -260,7 +260,7 @@ impl VM { } // STATICCALL operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 pub fn op_staticcall( &mut self, current_call_frame: &mut CallFrame, @@ -328,7 +328,7 @@ impl VM { } // CREATE operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 pub fn op_create( &mut self, current_call_frame: &mut CallFrame, @@ -357,7 +357,7 @@ impl VM { } // CREATE2 operation - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1086 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1086 pub fn op_create2( &mut self, current_call_frame: &mut CallFrame, diff --git a/crates/vm/levm/src/operations.rs b/crates/vm/levm/src/operations.rs index 03e99f91a..5f36d17f5 100644 --- a/crates/vm/levm/src/operations.rs +++ b/crates/vm/levm/src/operations.rs @@ -3,7 +3,7 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethrex_core::U256; +use ethereum_rust_core::U256; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Operation { diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index 56dc716fe..dd8a037d4 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -7,7 +7,7 @@ use crate::{ vm::VM, }; use bytes::Bytes; -use ethrex_core::{types::TxKind, Address, U256}; +use ethereum_rust_core::{types::TxKind, Address, U256}; use std::{collections::HashMap, sync::Arc}; pub fn ops_to_bytecode(operations: &[Operation]) -> Result { diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index f3041fbbf..1fc4a1953 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -12,9 +12,9 @@ use crate::{ opcodes::Opcode, }; use bytes::Bytes; -use ethrex_core::{types::TxKind, Address, H256, U256}; -use ethrex_rlp; -use ethrex_rlp::encode::RLPEncode; +use ethereum_rust_core::{types::TxKind, Address, H256, U256}; +use ethereum_rust_rlp; +use ethereum_rust_rlp::encode::RLPEncode; use keccak_hash::keccak; use sha3::{Digest, Keccak256}; use std::{ @@ -25,7 +25,7 @@ use std::{ pub type Storage = HashMap; #[derive(Debug, Clone, Default)] -// TODO: https://github.com/lambdaclass/ethrex/issues/604 +// TODO: https://github.com/lambdaclass/ethereum_rust/issues/604 pub struct Substate { // accessed addresses and storage keys are considered WARM // pub accessed_addresses: HashSet
, @@ -151,7 +151,7 @@ impl VM { }) } } - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1088 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1088 } pub fn execute(&mut self, current_call_frame: &mut CallFrame) -> TransactionReport { @@ -600,7 +600,7 @@ impl VM { )) } - // TODO: Improve and test REVERT behavior for XCALL opcodes. Issue: https://github.com/lambdaclass/lambda_ethrex/issues/1061 + // TODO: Improve and test REVERT behavior for XCALL opcodes. Issue: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1061 #[allow(clippy::too_many_arguments)] pub fn generic_call( &mut self, @@ -782,7 +782,7 @@ impl VM { /// Common behavior for CREATE and CREATE2 opcodes /// /// Could be used for CREATE type transactions - // TODO: Improve and test REVERT behavior for CREATE. Issue: https://github.com/lambdaclass/lambda_ethrex/issues/1061 + // TODO: Improve and test REVERT behavior for CREATE. Issue: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1061 pub fn create( &mut self, value_in_wei_to_send: U256, diff --git a/crates/vm/levm/tests/edge_case_tests.rs b/crates/vm/levm/tests/edge_case_tests.rs index 99b198d05..4cb8bb422 100644 --- a/crates/vm/levm/tests/edge_case_tests.rs +++ b/crates/vm/levm/tests/edge_case_tests.rs @@ -1,6 +1,6 @@ use bytes::Bytes; -use ethrex_core::U256; -use ethrex_levm::{ +use ethereum_rust_core::U256; +use ethereum_rust_levm::{ operations::Operation, utils::{new_vm_with_bytecode, new_vm_with_ops}, }; diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index 06fef1b97..cb2e64048 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -2,8 +2,8 @@ #![allow(clippy::unwrap_used)] use bytes::Bytes; -use ethrex_core::{types::TxKind, Address, H256, U256}; -use ethrex_levm::{ +use ethereum_rust_core::{types::TxKind, Address, H256, U256}; +use ethereum_rust_levm::{ account::Account, constants::*, db::{Cache, Db}, diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index 0c54dff60..0733b095d 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -9,14 +9,14 @@ use db::StoreWrapper; use execution_db::ExecutionDB; use std::cmp::min; -use ethrex_core::{ +use ethereum_rust_core::{ types::{ AccountInfo, Block, BlockHash, BlockHeader, ChainConfig, Fork, GenericTransaction, PrivilegedTxType, Receipt, Transaction, TxKind, Withdrawal, GWEI_TO_WEI, INITIAL_BASE_FEE, }, Address, BigEndianHash, H256, U256, }; -use ethrex_storage::{error::StoreError, AccountUpdate, Store}; +use ethereum_rust_storage::{error::StoreError, AccountUpdate, Store}; use lazy_static::lazy_static; use revm::{ db::{states::bundle_state::BundleRetention, AccountState, AccountStatus}, @@ -79,14 +79,14 @@ impl From for EvmState { cfg_if::cfg_if! { if #[cfg(feature = "levm")] { - use ethrex_levm::{ + use ethereum_rust_levm::{ db::{Cache, Database as LevmDatabase}, errors::{TransactionReport, TxResult, VMError}, vm::VM, Environment, }; use std::{collections::HashMap, sync::Arc}; - use ethrex_core::types::code_hash; + use ethereum_rust_core::types::code_hash; /// Executes all transactions in a block and returns their receipts. pub fn execute_block( @@ -116,7 +116,7 @@ cfg_if::cfg_if! { transaction.tx_type(), matches!(result.result, TxResult::Success), cumulative_gas_used, - // TODO: https://github.com/lambdaclass/lambda_ethrex/issues/1089 + // TODO: https://github.com/lambdaclass/lambda_ethereum_rust/issues/1089 vec![], ); receipts.push(receipt); diff --git a/test_data/genesis-l2-old.json b/test_data/genesis-l2-old.json deleted file mode 100644 index eb629d94d..000000000 --- a/test_data/genesis-l2-old.json +++ /dev/null @@ -1,548 +0,0 @@ -{ - "config": { - "chainId": 1729, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "mergeNetsplitBlock": 0, - "terminalTotalDifficulty": 0, - "terminalTotalDifficultyPassed": true, - "shanghaiTime": 0, - "cancunTime": 0, - "pragueTime": 1718232101 - }, - "alloc": { - "0x0007a881CD95B1484fca47615B64803dad620C8d": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0000bd19F707CA481886244bDd20Bd6B8a81bd3e": { - "balance": "0xc097ce7bc90715b34b9f1000000000", - "nonce": "0" - }, - "0x000cD1537A823Ae7609E3897DA8d95801B557a8a": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006d77295a0260ceAC113c5Aa15CFf0d28d9723": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000eA2e72065A2ceCA7f677Bc5E648279c2D843d": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000a52D537c4150ec274dcE3962a0d179B7E71B0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0009aEFF154De37C8e02E83f93D2FeC5EC96f8a3": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000f1EB7F258D4A7683E5D0FC3C01058841DDC6f": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000aC79590dCc656c00c4453f123AcBf10DBb086": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002Bf507275217c9E5EE250bC1B5ca177bb4f74": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000a3fC3BFD55b37025E6F4f57B0B6121F54e5bF": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000b4C43cce938dfD3420F975591Ee46D872C136": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004b0C6de796fD980554cc7ff7B062b3B5079E1": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00025eea83bA285532F5054b238c938076833d13": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000352E93fe11f9B715fdc61864315970B3DC082": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c0d6b7C4516a5B274C51EA331A9410fe69127": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000D06C23EeD09A7Fa81cADd7eD5C783E8a25635": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003Ea7fDFCdb89E9ddAb0128ec5C628F8D09D45": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005C34d7B8b06CE8019C3Bb232dE82B2748A560": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00079f33619F70F1DCE64EB6782E45D3498d807C": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003E72436Ff296B3d39339784499D021b72Aca5": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00075af7E665F3Ca4A4b05520CD6d5c13BbFEAf8": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000b59AeD48ADCd6c36Ae5f437AbB9CA730a2c43": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004e4dfCed9d798767A4d7BA2B03495cE80A2b7": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e73282F60E2CdE0D4FA9B323B6D54d860f330": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00010AB05661Bfde304A4d884DF99d3011A83C54": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000B9Ea41A9dF00b7ae597afc0D10AF42666081F": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00087C666bf7f52758DE186570979C4C79747157": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0008a52c83D34f0791D07FfeD04Fb6b14f94E2D4": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A7Bbde38Fc53925D0De9cc1beE3038d36c2d2": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000Aa0154ed6560257d222B5dbE6ce4b66c48979": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000b681738e1f8aF387c41b2b1f0A04E0C33e9DB": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000D66A7706f2DD5F557d5b68e01E07E8FFDfaf5": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00069DA530A71Dc92D02090d7f5f63e326e9beD0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000db74a3da16609F183ACE7AF65B43D896349CE": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003B1aB565508e095a543C89531e3fbc4a349DA": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001c94c108BcE19CDb36b00F867A1798A81DedA": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000995137728C7C2a9142F4628f95c98Cac433d7": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000Ec60762AD0425A04C40c118Db5B9710Aa639e": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000Ebf88AE1BA960B06b0a9bbE576baa3B72E92E": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e1a554572dd96fF3d1F2664832F3E4a66E7b7": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00032C03f3b02D816128Fb5D2752398E2919a03c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A073dAC5ec2058a0De0e175874D5E297E086E": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e06626Bb8618D9A1867362D46ddb1bF95ad75": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000212949b4866db43bAF7c4e0975426710ED081": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00094cc0653B52406170105F4eb96C5e2f31Ab74": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000E67E4b1A23A3826304099cb24f337c916CF4b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000885A4932ebeD6D760EA381e4EdAe51A53db05": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000883A40409Fa2193b698928459CB9E4DD5f8D8": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002590DD45738F909115B163F1322A8A24a8B4E": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005f132597da3152a6Da6beDB7C10bcC9B1B7f5": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00031470def99c1d4dfE1fd08DD7A8520Ce21DB7": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001Ebe3a3bA36f57F5989B3F0e5BEEBc710569C": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006Bd0469166f63D0A1c33F71898D2b2E009b9b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00000A8d3f37af8DeF18832962Ee008d8dCa4F7b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e490f26249951F8527779399aa8F281509aC0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0000638374f7dB166990BDc6aBeE884Ee01a8920": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00031dE95353DeE86dc9B1248e825500DE0B39aF": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000511B42328794337D8b6846E5cFFef30c2d77A": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000d0576AdEf7083d53F6676bfc7c30d03b6Db1B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001E8Ff6406a7cd9071F46B8255Db6C16178448": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000C47c771A8db282eC233b28AD8525dc74D13FE": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000798832bb08268dB237898b95A8DaE9D58b62c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c877a5D9b9De61e5318B3f4330c56ecdC0865": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003Ffc1f09d39FBFE87eD63E98249039C7b1d9A": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000d72403c18B2516d8ada074E1E7822bF1084DB": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00054e17Db8C8Db028B19cB0f631888AdEb35E4b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002d9b2a816717C4d70040D66A714795F9B27a4": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002AfCC1B0B608E86b5a1Dc45dE08184E629796": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000b1db69627F04688aA47951d847c8BFAB3fFaE": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c2de896E4a92e796d6A9c1E4B01feB3e6Ed61": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000EDC52118DadB4B81f013005b6db2665B682ac": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0009e10C0D2F1a7A2b00b61c476aa8b608c60aDc": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000f2AbaA7581fAA2ad5C82b604C77ef68c3eAD9": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000F74AA6EE08C15076b3576eE33Ed3a80c9A1AD": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001533C6C5b425815b2BaDdCdd42DFF3be04BCb": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002D79686DeF20a0aB43FEA4a41a1Ad56529621": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00077A336FCA40F933a7A301F4a39C26594F3EB5": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000B05E15C62CBC266A4DD1804b017d1f6dB078b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000130badE00212bE1AA2F4aCFe965934635C9cD": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0008Bd31Ee6A758e168844cBEA107Ca4d87251aF": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A390975F21371F1Cf3C783a4A7C1aF49074Fe": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000701F7d594Fb146e4d1c71342012e48A788055": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005c6BeD054FEad199D72C6f663fC6fBf996153": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0009d862F87F26c638AAd14F2cc48FCa54DBf49d": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00029637dA962294449549f804f8184046F5fbB0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000279CB54E00B858774afEA4601034Db41c1A05": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003dDe6f01e3B755e24891a5B0f2463BaD83e15": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000086Eeea461Ca48e4D319F9789F3Efd134E574": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004351AD413792131011CC7ed8299dd783C6487": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00097B4463159340Ac83B9bdf657C304cD70c11c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004ad0D0823e3d31C6ECA2A3495373fA76c43aC": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005E815c1A3F40011Bd70C76062bbcBc51c546B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006A070bAC6195b59d4bC7f73741DCBe4e16b5e": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006cEE23d8E9BC8d99E826cDa50481394aD9bDD": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000a523148845bEe3EE1e9F83df8257a1191C85B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000D268F322F10925cdB5d2AD527E582259Da655": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000E5DE0a0175866d21F4Ec6c41F0422A05f14D6": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000cDF8Dba2393a40857cbCB0FCD9b998a941078": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A341763112a5E3452c7AEE45c382a3fb7dc78": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000635BCbB109781Cea0Cd53e9f1370Dbac9937f": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000E0ea540095B3853c4cb09E5Cdd197330D3B55": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00044cbfb4Ef6054667994C37c0fe0B6BB639718": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00065fC4337dF331242bEE738031dAf35817Ee9e": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000815A8A659a51A8EF01F02441947Ea99182568": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004C8da21c68dED2F63efD9836De7D43e7cDa10": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006ed38815a9439c59bD917c12f77a9A7D39BCE": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004Aa0442d0d43222431b3017912EC6a099771C": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000b3F6da04b6261B4154C8FaEd119632C49DBd5": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000AEBc2568796FDB763CAB67B31e0feE58Fe17d": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000425E97fC6692891876012824a210451cC06C4": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000036e0f87f8Cd3e97f9cfDB2e4E5Ff193c217a": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000305CD7184aB37fdd3D826B92A640218D09527": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c95f1D83De53B76a0828F1bCdB1DfE12C0ab3": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000882c5FbD315801e4C367BCB04dBD299B9F571": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0000E101815A78EbB9FBBa34F4871aD32d5eb6CD": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A997c1ceCB1DA78C16249e032e77d1865646a": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00056bde49E3cAA9166C2a4C4951d0Cf067956A0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e65342176C7dac47bc75113F569695d6A113C": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0008D608884cd733642ab17aCa0c8504850B94fA": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000dFE27e1b71a49B641ad762aB95558584878D1": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00085D9D1a71acf1080cED44CB501B350900627f": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0007d272a1f7Dfe862b030adE2922D149f3bDe3B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0004b230511F921934F33E8B4425E43295232680": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0007514395022786B59ff91408692462C48d872c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005b34eB0d99dE72DB14d466f692009c4049D46": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001a2c749FE0Ab1C09f1131BA17530f9D764fBC": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c6c1D8F778D981968F9904772B0c455E1C17c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000e64e0a2Fd76B4883c800833c82c5F2420b813": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000577bDc84B4019F77D9D09BDD8ED6145E0e890": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000029bD811D292E7f1CF36c0FA08fd753C45074": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000cE6740261E297FaD4c975D6D8F89f95C29add": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0001d0bAE8B1B9fe61d0B788E562A987813cbD98": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000E3388598A0534275104Ad44745620AF31EC7E": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000791D3185781e14eBb342E5df3BC9910f62E6F": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000Df55E76cf6dfD9598DD2b54948dE937f50f2B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000055acf237931902ceBf4B905BF59813180555": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00009074D8fc5Eeb25f1548Df05AD955E21FB08D": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000C1aE5FeCf09595C0C76Db609FEB2a5Af0962E": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000F76B2Fe7cCC13474de28586A877664EBA16B4": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000F7cFBa0B176Afc2eBadA9d4764d2eA6BBC5a1": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00002132cE94eEfB06eB15898C1AABd94feb0AC2": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00069dC0cc6b9d7B48B5348b12F625E8aB704104": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000A0191cf913E03bd594bC8817FC3B2895C0a25": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0007316aEDc52EB35c9B5c2E44e9fD712d1DF887": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000EBd066B6FEBB9d7f3B767DF06C08e369Dc20F": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00096af89fd96f0d6E1721d9145944e813317d46": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000C5e39879228A1Fc8dF2470822CB8ce2Af8e07": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000ea86B4A3d7e4AF8CFab052c8b9a040149b507": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000796370C839773893a2cEFA5fc81f2332936fB": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000990B05481b1661bc6211298f6429451B09425": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0008a02d3E8507621f430345b98478058cDca79A": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000d35f8cd11bd989216b3669cBaac6fd8c07196": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000541653a96ABAdDba52fAA8D118e570d529543": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006264bf7E3395309F728222641Ff8D0e1ad2C0": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000688AA0fBfB3F1e6554A63dF13bE08cB671b3b": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00030da862690D170F096074e9E8b38db7D6f037": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0005e37296348571bd3604f7E56B67a7022801f6": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000ed6E0F4Fdc3615663BF4A601E35e7A8d66E1c": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000c53b37fA4977B59FD3Efdb473D8069844aDeA": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00057714949aD700733C5b8E6cF3e8c6B7D228a2": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000C8FC4132881c31f67638c3941dF8D94a92299": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000fA71E446e1EcFd74d835b5bD6fA848A770d26": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000784B47aC2843419Df4cAd697d4e7b65CE1F93": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0002869e27c6FaEe08cCA6b765a726E7a076Ee0F": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0003135C47c441506b58483Ec6173F767182670B": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0006E80d584cbF9EB8C41CF2b009C607744a70F6": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000C1C05dBFf111c79D5c9E91420DFBEA1c31716": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x0009Bf72AF31A4E6B8Ef6FbbFcb017823E4d2aF2": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x00021C20F3e68F930077Cca109Ca3C044E8B39bD": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000E90875aC71eD46A11dc1b509d2B35E2c9C31F": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - }, - "0x000f17eB09AA3f28132323E6075C672949526d5A": { - "balance": "0xc097ce7bc90715b34b9f1000000000" - } - }, - "coinbase": "0x0000000000000000000000000000000000000000", - "difficulty": "0x01", - "extraData": "", - "gasLimit": "0x17d7840", - "nonce": "0x1234", - "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "timestamp": "1718040081" -} diff --git a/test_data/network_params.yaml b/test_data/network_params.yaml index 6e251057b..f15c36bd2 100644 --- a/test_data/network_params.yaml +++ b/test_data/network_params.yaml @@ -1,5 +1,5 @@ participants: - - el_type: ethrex + - el_type: ethereumrust cl_type: lighthouse validator_count: 32 - el_type: geth @@ -20,4 +20,4 @@ assertoor_params: run_block_proposal_check: false run_blob_transaction_test: true tests: - - 'https://raw.githubusercontent.com/lambdaclass/lambda_ethrex/refs/heads/main/test_data/el-stability-check.yml' + - 'https://raw.githubusercontent.com/lambdaclass/lambda_ethereum_rust/refs/heads/main/test_data/el-stability-check.yml' From 221990cbe4e68b93ee3f43a0648de7db5accb3f6 Mon Sep 17 00:00:00 2001 From: Ivan Litteri <67517699+ilitteri@users.noreply.github.com> Date: Fri, 22 Nov 2024 10:55:04 -0300 Subject: [PATCH 13/49] Update cmd/ef_tests/levm/runner/levm_runner.rs --- cmd/ef_tests/levm/runner/levm_runner.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 46d003d3e..ce85102ae 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -172,7 +172,7 @@ pub fn ensure_post_state( None => { let levm_account_updates = get_state_transitions(execution_report); let pos_state_root = post_state_root(&levm_account_updates, test); - let expected_post_state_value = test.post.iter().next().cloned(); + let expected_post_state_value = test.post.vector_post_value(vector).cloned(); if let Some(expected_post_state_root_hash) = expected_post_state_value { let expected_post_state_root_hash = expected_post_state_root_hash.hash; if expected_post_state_root_hash != pos_state_root { From a43750867b6cd4af82da4ff8cac51ca1da2f1b68 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Fri, 22 Nov 2024 10:58:02 -0300 Subject: [PATCH 14/49] Fix --- cmd/ef_tests/levm/runner/levm_runner.rs | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index ce85102ae..208b6258b 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -172,23 +172,14 @@ pub fn ensure_post_state( None => { let levm_account_updates = get_state_transitions(execution_report); let pos_state_root = post_state_root(&levm_account_updates, test); - let expected_post_state_value = test.post.vector_post_value(vector).cloned(); - if let Some(expected_post_state_root_hash) = expected_post_state_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!( - "Post-state root mismatch: expected {expected_post_state_root_hash:#x}, got {pos_state_root:#x}", - ); - return Err(EFTestRunnerError::FailedToEnsurePostState( - execution_report.clone(), - error_reason, - )); - } - } else { - let error_reason = "No post-state root hash provided"; + let expected_post_state_root_hash = test.post.vector_post_value(vector).hash; + if expected_post_state_root_hash != pos_state_root { + let error_reason = format!( + "Post-state root mismatch: expected {expected_post_state_root_hash:#x}, got {pos_state_root:#x}", + ); return Err(EFTestRunnerError::FailedToEnsurePostState( execution_report.clone(), - error_reason.to_owned(), + error_reason, )); } } From 12333c1b342aa83f6cfe1b4a43cd3b0fe9fd4b41 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Fri, 22 Nov 2024 10:59:25 -0300 Subject: [PATCH 15/49] Fix parsing --- cmd/ef_tests/levm/parser.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/ef_tests/levm/parser.rs b/cmd/ef_tests/levm/parser.rs index 491e82890..aa945b0cd 100644 --- a/cmd/ef_tests/levm/parser.rs +++ b/cmd/ef_tests/levm/parser.rs @@ -77,9 +77,10 @@ pub fn parse_ef_test_dir( } // Skip tests that are not in the list of tests to run. - if !opts - .tests - .contains(&test_dir.file_name().to_str().unwrap().to_owned()) + if !opts.tests.is_empty() + && !opts + .tests + .contains(&test_dir.file_name().to_str().unwrap().to_owned()) { continue; } From 0e51cf8d0f2f8e16339df4a0f479b82d75336297 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Fri, 22 Nov 2024 11:13:27 -0300 Subject: [PATCH 16/49] Fix parsing --- cmd/ef_tests/levm/parser.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cmd/ef_tests/levm/parser.rs b/cmd/ef_tests/levm/parser.rs index aa945b0cd..df2481646 100644 --- a/cmd/ef_tests/levm/parser.rs +++ b/cmd/ef_tests/levm/parser.rs @@ -43,6 +43,8 @@ pub fn parse_ef_test_dir( opts: &EFTestRunnerOptions, directory_parsing_spinner: &mut Spinner, ) -> Result, EFTestParseError> { + directory_parsing_spinner.update_text(format!("Parsing directory {:?}", test_dir.file_name())); + let mut directory_tests = Vec::new(); for test in std::fs::read_dir(test_dir.path()) .map_err(|err| { @@ -82,12 +84,13 @@ pub fn parse_ef_test_dir( .tests .contains(&test_dir.file_name().to_str().unwrap().to_owned()) { - continue; + directory_parsing_spinner.update_text(format!( + "Skipping test {:?} as it is not in the list of tests to run", + test.path().file_name() + )); + return Ok(Vec::new()); } - directory_parsing_spinner - .update_text(format!("Parsing directory {:?}", test_dir.file_name())); - let test_file = std::fs::File::open(test.path()).map_err(|err| { EFTestParseError::FailedToReadFile(format!("{:?}: {err}", test.path())) })?; From 3171a94cc91dba93835af3f54c3f05356064a03d Mon Sep 17 00:00:00 2001 From: ilitteri Date: Fri, 22 Nov 2024 12:49:53 -0300 Subject: [PATCH 17/49] We do not need a mutable reference to the current call frame for computing gas costs --- crates/vm/levm/src/gas_cost.rs | 48 +++++++++++++++++----------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 75cdc321e..54b60ffa8 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -114,7 +114,7 @@ pub fn exp(exponent_bits: u64) -> Result { } pub fn calldatacopy( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, dest_offset: usize, ) -> Result { @@ -128,7 +128,7 @@ pub fn calldatacopy( } pub fn codecopy( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, dest_offset: usize, ) -> Result { @@ -142,15 +142,15 @@ pub fn codecopy( } pub fn extcodecopy( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, dest_offset: usize, - is_cached: bool, + address_is_cold: bool, ) -> Result { - let address_access_cost = if is_cached { - WARM_ADDRESS_ACCESS_COST - } else { + let address_access_cost = if address_is_cold { COLD_ADDRESS_ACCESS_COST + } else { + WARM_ADDRESS_ACCESS_COST }; // address_access_cost is not a static cost, but there's no static @@ -165,7 +165,7 @@ pub fn extcodecopy( } pub fn returndatacopy( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, dest_offset: usize, ) -> Result { @@ -181,7 +181,7 @@ pub fn returndatacopy( fn copy_behavior( dynamic_base: U256, static_cost: U256, - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, offset: usize, ) -> Result { @@ -208,7 +208,7 @@ fn copy_behavior( } pub fn keccak256( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, offset: usize, ) -> Result { @@ -222,7 +222,7 @@ pub fn keccak256( } pub fn log( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, offset: usize, number_of_topics: u8, @@ -248,20 +248,20 @@ pub fn log( .ok_or(OutOfGasError::GasCostOverflow) } -pub fn mload(current_call_frame: &mut CallFrame, offset: usize) -> Result { +pub fn mload(current_call_frame: &CallFrame, offset: usize) -> Result { mem_expansion_behavior(current_call_frame, offset, WORD_SIZE, MLOAD_STATIC) } -pub fn mstore(current_call_frame: &mut CallFrame, offset: usize) -> Result { +pub fn mstore(current_call_frame: &CallFrame, offset: usize) -> Result { mem_expansion_behavior(current_call_frame, offset, WORD_SIZE, MSTORE_STATIC) } -pub fn mstore8(current_call_frame: &mut CallFrame, offset: usize) -> Result { +pub fn mstore8(current_call_frame: &CallFrame, offset: usize) -> Result { mem_expansion_behavior(current_call_frame, offset, 1, MSTORE8_STATIC) } fn mem_expansion_behavior( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, offset: usize, offset_add: usize, static_cost: U256, @@ -318,7 +318,7 @@ pub fn sstore( } pub fn mcopy( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, size: usize, src_offset: usize, dest_offset: usize, @@ -351,7 +351,7 @@ pub fn mcopy( #[allow(clippy::too_many_arguments)] pub fn call( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, args_size: usize, args_offset: usize, ret_size: usize, @@ -400,7 +400,7 @@ pub fn call( } pub fn callcode( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, args_size: usize, args_offset: usize, ret_size: usize, @@ -429,7 +429,7 @@ pub fn callcode( } pub fn delegatecall( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, args_size: usize, args_offset: usize, ret_size: usize, @@ -447,7 +447,7 @@ pub fn delegatecall( } pub fn staticcall( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, args_size: usize, args_offset: usize, ret_size: usize, @@ -465,7 +465,7 @@ pub fn staticcall( } fn compute_gas_call( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, args_size: usize, args_offset: usize, ret_size: usize, @@ -494,7 +494,7 @@ fn compute_gas_call( } pub fn create( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, code_offset_in_memory: U256, code_size_in_memory: U256, ) -> Result { @@ -507,7 +507,7 @@ pub fn create( } pub fn create_2( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, code_offset_in_memory: U256, code_size_in_memory: U256, ) -> Result { @@ -520,7 +520,7 @@ pub fn create_2( } fn compute_gas_create( - current_call_frame: &mut CallFrame, + current_call_frame: &CallFrame, code_offset_in_memory: U256, code_size_in_memory: U256, is_create_2: bool, From 19856543917eda0259e810f98259e21b98a6977c Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 22:32:49 -0300 Subject: [PATCH 18/49] Refactor `op_balance` --- crates/vm/levm/src/gas_cost.rs | 31 +++++++++++++++++++ .../levm/src/opcode_handlers/environment.rs | 14 +++------ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 54b60ffa8..281cce42e 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -88,6 +88,10 @@ pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); pub const SELFDESTRUCT_STATIC: U256 = U256([5000, 0, 0, 0]); pub const SELFDESTRUCT_DYNAMIC: U256 = U256([25000, 0, 0, 0]); +pub const BALANCE_STATIC: U256 = U256::zero(); +pub const BALANCE_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); +pub const BALANCE_COLD_DYNAMIC: U256 = U256([2600, 0, 0, 0]); + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -618,3 +622,30 @@ pub fn tx_creation(code_length: u64, number_of_words: u64) -> Result Result { + let static_gas = static_cost; + let dynamic_cost: U256 = if address_is_cold { + cold_dynamic_cost + } else { + warm_dynamic_cost + }; + + static_gas + .checked_add(dynamic_cost) + .ok_or(OutOfGasError::GasCostOverflow) +} + +pub fn balance(address_is_cold: bool) -> Result { + address_access_cost( + address_is_cold, + BALANCE_STATIC, + BALANCE_COLD_DYNAMIC, + BALANCE_WARM_DYNAMIC, + ) +} diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index f0d7e02c8..62802a58d 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -31,18 +31,14 @@ impl VM { &mut self, current_call_frame: &mut CallFrame, ) -> Result { - let address = &word_to_address(current_call_frame.stack.pop()?); + let address = word_to_address(current_call_frame.stack.pop()?); - if self.cache.is_account_cached(address) { - self.increase_consumed_gas(current_call_frame, WARM_ADDRESS_ACCESS_COST)?; - } else { - self.increase_consumed_gas(current_call_frame, BALANCE_COLD_ADDRESS_ACCESS_COST)?; - self.cache_from_db(address); - }; + let (account_info, address_is_cold) = self.access_account(address); + + self.increase_consumed_gas(current_call_frame, gas_cost::balance(address_is_cold)?)?; - let balance = self.get_account(address).info.balance; + current_call_frame.stack.push(account_info.balance)?; - current_call_frame.stack.push(balance)?; Ok(OpcodeSuccess::Continue) } From b94ad3185ac7e303ca5182cc4dcdddb04264961a Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 22:37:21 -0300 Subject: [PATCH 19/49] Refactor `op_extcodesize` --- crates/vm/levm/src/gas_cost.rs | 23 ++++++++++++++++--- .../levm/src/opcode_handlers/environment.rs | 14 +++++------ 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 281cce42e..d7b1f8cad 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -88,9 +88,17 @@ pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); pub const SELFDESTRUCT_STATIC: U256 = U256([5000, 0, 0, 0]); pub const SELFDESTRUCT_DYNAMIC: U256 = U256([25000, 0, 0, 0]); -pub const BALANCE_STATIC: U256 = U256::zero(); -pub const BALANCE_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); -pub const BALANCE_COLD_DYNAMIC: U256 = U256([2600, 0, 0, 0]); +pub const DEFAULT_STATIC: U256 = U256::zero(); +pub const DEFAULT_COLD_DYNAMIC: U256 = U256([2600, 0, 0, 0]); +pub const DEFAULT_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); + +pub const BALANCE_STATIC: U256 = DEFAULT_STATIC; +pub const BALANCE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const BALANCE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; + +pub const EXTCODESIZE_STATIC: U256 = DEFAULT_STATIC; +pub const EXTCODESIZE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const EXTCODESIZE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); @@ -649,3 +657,12 @@ pub fn balance(address_is_cold: bool) -> Result { BALANCE_WARM_DYNAMIC, ) } + +pub fn extcodesize(address_is_cold: bool) -> Result { + address_access_cost( + address_is_cold, + EXTCODESIZE_STATIC, + EXTCODESIZE_COLD_DYNAMIC, + EXTCODESIZE_WARM_DYNAMIC, + ) +} diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index 62802a58d..545d23e4c 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -268,16 +268,14 @@ impl VM { ) -> Result { let address = word_to_address(current_call_frame.stack.pop()?); - if self.cache.is_account_cached(&address) { - self.increase_consumed_gas(current_call_frame, WARM_ADDRESS_ACCESS_COST)?; - } else { - self.increase_consumed_gas(current_call_frame, BALANCE_COLD_ADDRESS_ACCESS_COST)?; - self.cache_from_db(&address); - }; + let (account_info, address_is_cold) = self.access_account(address); - let bytecode = self.get_account(&address).info.bytecode; + self.increase_consumed_gas(current_call_frame, gas_cost::extcodesize(address_is_cold)?)?; + + current_call_frame + .stack + .push(account_info.bytecode.len().into())?; - current_call_frame.stack.push(bytecode.len().into())?; Ok(OpcodeSuccess::Continue) } From e56ee1c771acd370482ed5bd612766cab561eedb Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 22:40:17 -0300 Subject: [PATCH 20/49] Refactor `op_extcodehash` --- crates/vm/levm/src/gas_cost.rs | 9 +++++++++ .../vm/levm/src/opcode_handlers/environment.rs | 18 +++++------------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index d7b1f8cad..8691b5672 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -666,3 +666,12 @@ pub fn extcodesize(address_is_cold: bool) -> Result { EXTCODESIZE_WARM_DYNAMIC, ) } + +pub fn extcodehash(address_is_cold: bool) -> Result { + address_access_cost( + address_is_cold, + EXTCODEHASH_STATIC, + EXTCODEHASH_COLD_DYNAMIC, + EXTCODEHASH_WARM_DYNAMIC, + ) +} diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index 545d23e4c..dc703d3ca 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -413,21 +413,13 @@ impl VM { ) -> Result { let address = word_to_address(current_call_frame.stack.pop()?); - if self.cache.is_account_cached(&address) { - self.increase_consumed_gas(current_call_frame, WARM_ADDRESS_ACCESS_COST)?; - } else { - self.increase_consumed_gas(current_call_frame, BALANCE_COLD_ADDRESS_ACCESS_COST)?; - self.cache_from_db(&address); - }; + let (account_info, address_is_cold) = self.access_account(address); - let bytecode = self.get_account(&address).info.bytecode; + self.increase_consumed_gas(current_call_frame, gas_cost::extcodehash(address_is_cold)?)?; - let mut hasher = Keccak256::new(); - hasher.update(bytecode); - let result = hasher.finalize(); - current_call_frame - .stack - .push(U256::from_big_endian(&result))?; + current_call_frame.stack.push(U256::from_big_endian( + keccak(account_info.bytecode).as_fixed_bytes(), + ))?; Ok(OpcodeSuccess::Continue) } From 553e998f43b1886f4021b990957801a3ca30a3b5 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 23:08:00 -0300 Subject: [PATCH 21/49] Refactor `op_extcodecopy` --- crates/vm/levm/src/gas_cost.rs | 102 ++++++++++++------ .../levm/src/opcode_handlers/environment.rs | 36 ++++--- 2 files changed, 91 insertions(+), 47 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 8691b5672..d5799f6ca 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -1,8 +1,8 @@ use crate::{ call_frame::CallFrame, - constants::{COLD_STORAGE_ACCESS_COST, WORD_SIZE}, - errors::OutOfGasError, - StorageSlot, + constants::{COLD_STORAGE_ACCESS_COST, WORD_SIZE, WORD_SIZE_IN_BYTES}, + errors::{InternalError, OutOfGasError, VMError}, + memory, StorageSlot, }; use bytes::Bytes; /// Contains the gas costs of the EVM instructions (in wei) @@ -84,7 +84,6 @@ pub const CODESIZE: U256 = U256([2, 0, 0, 0]); pub const CODECOPY_STATIC: U256 = U256([3, 0, 0, 0]); pub const CODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); pub const GASPRICE: U256 = U256([2, 0, 0, 0]); -pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); pub const SELFDESTRUCT_STATIC: U256 = U256([5000, 0, 0, 0]); pub const SELFDESTRUCT_DYNAMIC: U256 = U256([25000, 0, 0, 0]); @@ -100,6 +99,15 @@ pub const EXTCODESIZE_STATIC: U256 = DEFAULT_STATIC; pub const EXTCODESIZE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; pub const EXTCODESIZE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; +pub const EXTCODEHASH_STATIC: U256 = DEFAULT_STATIC; +pub const EXTCODEHASH_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const EXTCODEHASH_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; + +pub const EXTCODECOPY_STATIC: U256 = U256::zero(); +pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); +pub const EXTCODECOPY_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const EXTCODECOPY_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -153,29 +161,6 @@ pub fn codecopy( ) } -pub fn extcodecopy( - current_call_frame: &CallFrame, - size: usize, - dest_offset: usize, - address_is_cold: bool, -) -> Result { - let address_access_cost = if address_is_cold { - COLD_ADDRESS_ACCESS_COST - } else { - WARM_ADDRESS_ACCESS_COST - }; - - // address_access_cost is not a static cost, but there's no static - // cost and there is the address_access_cost - copy_behavior( - EXTCODECOPY_DYNAMIC_BASE, - address_access_cost, - current_call_frame, - size, - dest_offset, - ) -} - pub fn returndatacopy( current_call_frame: &CallFrame, size: usize, @@ -636,7 +621,7 @@ fn address_access_cost( static_cost: U256, cold_dynamic_cost: U256, warm_dynamic_cost: U256, -) -> Result { +) -> Result { let static_gas = static_cost; let dynamic_cost: U256 = if address_is_cold { cold_dynamic_cost @@ -644,12 +629,43 @@ fn address_access_cost( warm_dynamic_cost }; - static_gas + Ok(static_gas .checked_add(dynamic_cost) - .ok_or(OutOfGasError::GasCostOverflow) + .ok_or(OutOfGasError::GasCostOverflow)?) +} + +fn memory_access_cost( + new_memory_size: U256, + current_memory_size: U256, + static_cost: U256, + dynamic_base_cost: U256, +) -> Result { + let minimum_word_size = new_memory_size + .checked_add( + WORD_SIZE_IN_BYTES + .checked_sub(U256::one()) + .ok_or(InternalError::ArithmeticOperationUnderflow)?, + ) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_div(WORD_SIZE_IN_BYTES) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; + + let static_gas = static_cost; + let dynamic_cost = dynamic_base_cost + .checked_mul(minimum_word_size) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_add(memory::expansion_cost( + new_memory_size, + current_memory_size, + )?) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_cost) + .ok_or(OutOfGasError::GasCostOverflow)?) } -pub fn balance(address_is_cold: bool) -> Result { +pub fn balance(address_is_cold: bool) -> Result { address_access_cost( address_is_cold, BALANCE_STATIC, @@ -658,7 +674,7 @@ pub fn balance(address_is_cold: bool) -> Result { ) } -pub fn extcodesize(address_is_cold: bool) -> Result { +pub fn extcodesize(address_is_cold: bool) -> Result { address_access_cost( address_is_cold, EXTCODESIZE_STATIC, @@ -667,7 +683,27 @@ pub fn extcodesize(address_is_cold: bool) -> Result { ) } -pub fn extcodehash(address_is_cold: bool) -> Result { +pub fn extcodecopy( + new_memory_size: U256, + current_memory_size: U256, + address_is_cold: bool, +) -> Result { + Ok(memory_access_cost( + new_memory_size, + current_memory_size, + EXTCODECOPY_STATIC, + EXTCODECOPY_DYNAMIC_BASE, + )? + .checked_add(address_access_cost( + address_is_cold, + EXTCODECOPY_STATIC, + EXTCODECOPY_COLD_DYNAMIC, + EXTCODECOPY_WARM_DYNAMIC, + )?) + .ok_or(OutOfGasError::GasCostOverflow)?) +} + +pub fn extcodehash(address_is_cold: bool) -> Result { address_access_cost( address_is_cold, EXTCODEHASH_STATIC, diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index dc703d3ca..ce1b21edd 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -301,25 +301,32 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let is_cached = self.cache.is_account_cached(&address); - - let gas_cost = gas_cost::extcodecopy(current_call_frame, size, dest_offset, is_cached) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; - - if !is_cached { - self.cache_from_db(&address); - }; - - let bytecode = self.get_account(&address).info.bytecode; + let (account_info, address_is_cold) = self.access_account(address); let new_memory_size = dest_offset.checked_add(size).ok_or(VMError::Internal( InternalError::ArithmeticOperationOverflow, ))?; let current_memory_size = current_call_frame.memory.data.len(); + + self.increase_consumed_gas( + current_call_frame, + gas_cost::extcodecopy( + new_memory_size.into(), + current_memory_size.into(), + address_is_cold, + )?, + )?; + if current_memory_size < new_memory_size { - current_call_frame.memory.data.resize(new_memory_size, 0); + current_call_frame + .memory + .data + .try_reserve(new_memory_size) + .map_err(|_err| VMError::MemorySizeOverflow)?; + current_call_frame + .memory + .data + .extend(std::iter::repeat(0).take(new_memory_size)); } for i in 0..size { @@ -331,7 +338,8 @@ impl VM { InternalError::ArithmeticOperationOverflow, ))?) { - *memory_byte = *bytecode + *memory_byte = *account_info + .bytecode .get(offset.checked_add(i).ok_or(VMError::Internal( InternalError::ArithmeticOperationOverflow, ))?) From 722c8e326ddaf33cec8d51f3096526b554472671 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 23:23:13 -0300 Subject: [PATCH 22/49] Refactor `op_call` --- crates/vm/levm/src/constants.rs | 3 + crates/vm/levm/src/gas_cost.rs | 100 +++++++++---------- crates/vm/levm/src/opcode_handlers/system.rs | 79 ++++++++------- 3 files changed, 94 insertions(+), 88 deletions(-) diff --git a/crates/vm/levm/src/constants.rs b/crates/vm/levm/src/constants.rs index 6b07e6e87..83bb88ec9 100644 --- a/crates/vm/levm/src/constants.rs +++ b/crates/vm/levm/src/constants.rs @@ -1,5 +1,8 @@ use ethrex_core::{H256, U256}; +pub const WORD_SIZE_IN_BYTES: U256 = U256([32, 0, 0, 0]); +pub const WORD_SIZE_IN_BYTES_USIZE: usize = 32; + pub const SUCCESS_FOR_CALL: i32 = 1; pub const REVERT_FOR_CALL: i32 = 0; pub const HALT_FOR_CALL: i32 = 2; diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index d5799f6ca..d32e9f9d3 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -108,6 +108,13 @@ pub const EXTCODECOPY_DYNAMIC_BASE: U256 = U256([3, 0, 0, 0]); pub const EXTCODECOPY_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; pub const EXTCODECOPY_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; +pub const CALL_STATIC: U256 = DEFAULT_STATIC; +pub const CALL_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const CALL_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; +pub const CALL_POSITIVE_VALUE: U256 = U256([9000, 0, 0, 0]); +pub const CALL_POSITIVE_VALUE_STIPEND: U256 = U256([2300, 0, 0, 0]); +pub const CALL_TO_EMPTY_ACCOUNT: U256 = U256([25000, 0, 0, 0]); + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -346,56 +353,6 @@ pub fn mcopy( .ok_or(OutOfGasError::GasCostOverflow) } -#[allow(clippy::too_many_arguments)] -pub fn call( - current_call_frame: &CallFrame, - args_size: usize, - args_offset: usize, - ret_size: usize, - ret_offset: usize, - value: U256, - is_cached: bool, - account_is_empty: bool, -) -> Result { - let memory_byte_size = args_size - .checked_add(args_offset) - .ok_or(OutOfGasError::GasCostOverflow)? - .max( - ret_size - .checked_add(ret_offset) - .ok_or(OutOfGasError::GasCostOverflow)?, - ); - let memory_expansion_cost = current_call_frame.memory.expansion_cost(memory_byte_size)?; - - let positive_value_cost = if !value.is_zero() { - NON_ZERO_VALUE_COST - .checked_add(BASIC_FALLBACK_FUNCTION_STIPEND) - .ok_or(OutOfGasError::GasCostOverflow)? - } else { - U256::zero() - }; - - let address_access_cost = if !is_cached { - COLD_ADDRESS_ACCESS_COST - } else { - WARM_ADDRESS_ACCESS_COST - }; - - let value_to_empty_account_cost = if !value.is_zero() && account_is_empty { - VALUE_TO_EMPTY_ACCOUNT_COST - } else { - U256::zero() - }; - - memory_expansion_cost - .checked_add(address_access_cost) - .ok_or(OutOfGasError::GasCostOverflow)? - .checked_add(positive_value_cost) - .ok_or(OutOfGasError::GasCostOverflow)? - .checked_add(value_to_empty_account_cost) - .ok_or(OutOfGasError::GasCostOverflow) -} - pub fn callcode( current_call_frame: &CallFrame, args_size: usize, @@ -711,3 +668,46 @@ pub fn extcodehash(address_is_cold: bool) -> Result { EXTCODEHASH_WARM_DYNAMIC, ) } + +pub fn call( + new_memory_size: U256, + current_memory_size: U256, + address_is_cold: bool, + address_is_empty: bool, + value_to_transfer: U256, +) -> Result { + let static_gas = CALL_STATIC; + + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; + let address_access_cost = address_access_cost( + address_is_cold, + CALL_STATIC, + CALL_COLD_DYNAMIC, + CALL_WARM_DYNAMIC, + )?; + let positive_value_cost = if !value_to_transfer.is_zero() { + CALL_POSITIVE_VALUE + .checked_add(CALL_POSITIVE_VALUE_STIPEND) + .ok_or(InternalError::ArithmeticOperationOverflow)? + } else { + U256::zero() + }; + let value_to_empty_account = if address_is_empty && !value_to_transfer.is_zero() { + CALL_TO_EMPTY_ACCOUNT + } else { + U256::zero() + }; + + // Note: code_execution_cost will be charged from the sub context post-state. + let dynamic_gas = memory_expansion_cost + .checked_add(address_access_cost) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_add(positive_value_cost) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_add(value_to_empty_account) + .ok_or(OutOfGasError::GasCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_gas) + .ok_or(OutOfGasError::GasCostOverflow)?) +} diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 1012a2e95..a0aaf2e36 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -1,11 +1,11 @@ use crate::{ call_frame::CallFrame, - constants::SUCCESS_FOR_RETURN, + constants::{SUCCESS_FOR_RETURN, WORD_SIZE_IN_BYTES_USIZE}, errors::{InternalError, OpcodeSuccess, ResultReason, VMError}, gas_cost, vm::{word_to_address, VM}, }; -use ethrex_core::{types::TxKind, U256}; +use ethrex_core::{types::TxKind, Address, U256}; // System Operations (10) // Opcodes: CREATE, CALL, CALLCODE, RETURN, DELEGATECALL, CREATE2, STATICCALL, REVERT, INVALID, SELFDESTRUCT @@ -16,10 +16,15 @@ impl VM { &mut self, current_call_frame: &mut CallFrame, ) -> Result { - let gas = current_call_frame.stack.pop()?; - let code_address = word_to_address(current_call_frame.stack.pop()?); - let value = current_call_frame.stack.pop()?; - let args_offset: usize = current_call_frame + let gas_for_call = current_call_frame.stack.pop()?; + let callee: Address = word_to_address(current_call_frame.stack.pop()?); + let value_to_transfer: U256 = current_call_frame.stack.pop()?; + + if current_call_frame.is_static && !value_to_transfer.is_zero() { + return Err(VMError::OpcodeNotAllowedInStaticContext); + } + + let args_start_offset: usize = current_call_frame .stack .pop()? .try_into() @@ -29,60 +34,58 @@ impl VM { .pop()? .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let ret_offset: usize = current_call_frame + let return_data_start_offset: usize = current_call_frame .stack .pop()? .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let ret_size: usize = current_call_frame + let return_data_size: usize = current_call_frame .stack .pop()? .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - if current_call_frame.is_static && !value.is_zero() { - return Err(VMError::OpcodeNotAllowedInStaticContext); - } + let new_memory_size_for_args = (args_start_offset + .checked_add(args_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size_for_return_data = (return_data_start_offset + .checked_add(return_data_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); + let current_memory_size = current_call_frame.memory.data.len(); - let is_cached = self.cache.is_account_cached(&code_address); + let (account_info, address_is_cold) = self.access_account(callee); - if !is_cached { - self.cache_from_db(&code_address); - } - - let account_is_empty = self.get_account(&code_address).clone().is_empty(); - - let gas_cost = gas_cost::call( + self.increase_consumed_gas( current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - value, - is_cached, - account_is_empty, - ) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + gas_cost::call( + new_memory_size.into(), + current_memory_size.into(), + address_is_cold, + account_info.is_empty(), + value_to_transfer, + )?, + )?; let msg_sender = current_call_frame.to; // The new sender will be the current contract. - let to = code_address; // In this case code_address and the sub-context account are the same. Unlike CALLCODE or DELEGATECODE. + let to = callee; // In this case code_address and the sub-context account are the same. Unlike CALLCODE or DELEGATECODE. let is_static = current_call_frame.is_static; self.generic_call( current_call_frame, - gas, - value, + gas_for_call, + value_to_transfer, msg_sender, to, - code_address, + callee, false, is_static, - args_offset, + args_start_offset, args_size, - ret_offset, - ret_size, + return_data_start_offset, + return_data_size, ) } @@ -120,7 +123,7 @@ impl VM { let is_cached = self.cache.is_account_cached(&code_address); if !is_cached { - self.cache_from_db(&code_address); + self.cache_from_db(code_address); }; let gas_cost = gas_cost::callcode( From 7d9b9570859d7c32eab4ac5b4727e2f4bdf58067 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 23:23:40 -0300 Subject: [PATCH 23/49] Leftovers --- crates/vm/levm/src/opcode_handlers/environment.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index ce1b21edd..c86d91661 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -1,12 +1,11 @@ use crate::{ call_frame::CallFrame, - constants::{BALANCE_COLD_ADDRESS_ACCESS_COST, WARM_ADDRESS_ACCESS_COST}, errors::{InternalError, OpcodeSuccess, OutOfGasError, VMError}, gas_cost, vm::{word_to_address, VM}, }; use ethrex_core::U256; -use sha3::{Digest, Keccak256}; +use keccak_hash::keccak; // Environmental Information (16) // Opcodes: ADDRESS, BALANCE, ORIGIN, CALLER, CALLVALUE, CALLDATALOAD, CALLDATASIZE, CALLDATACOPY, CODESIZE, CODECOPY, GASPRICE, EXTCODESIZE, EXTCODECOPY, RETURNDATASIZE, RETURNDATACOPY, EXTCODEHASH From f574d87647a9c4baed6f5a5992f5d29dc9901adb Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 23:34:06 -0300 Subject: [PATCH 24/49] Refactor `op_callcode` --- crates/vm/levm/src/gas_cost.rs | 70 ++++++++++++-------- crates/vm/levm/src/opcode_handlers/system.rs | 51 +++++++------- 2 files changed, 68 insertions(+), 53 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index d32e9f9d3..584643636 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -115,6 +115,12 @@ pub const CALL_POSITIVE_VALUE: U256 = U256([9000, 0, 0, 0]); pub const CALL_POSITIVE_VALUE_STIPEND: U256 = U256([2300, 0, 0, 0]); pub const CALL_TO_EMPTY_ACCOUNT: U256 = U256([25000, 0, 0, 0]); +pub const CALLCODE_STATIC: U256 = DEFAULT_STATIC; +pub const CALLCODE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const CALLCODE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; +pub const CALLCODE_POSITIVE_VALUE: U256 = U256([9000, 0, 0, 0]); +pub const CALLCODE_POSITIVE_VALUE_STIPEND: U256 = U256([2300, 0, 0, 0]); + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -353,35 +359,6 @@ pub fn mcopy( .ok_or(OutOfGasError::GasCostOverflow) } -pub fn callcode( - current_call_frame: &CallFrame, - args_size: usize, - args_offset: usize, - ret_size: usize, - ret_offset: usize, - value: U256, - is_cached: bool, -) -> Result { - let transfer_cost = if value == U256::zero() { - U256::zero() - } else { - NON_ZERO_VALUE_COST - // Should also add BASIC_FALLBACK_FUNCTION_STIPEND?? - // See https://www.evm.codes/?fork=cancun#f2 and call impl - }; - - compute_gas_call( - current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - is_cached, - )? - .checked_add(transfer_cost) - .ok_or(OutOfGasError::GasCostOverflow) -} - pub fn delegatecall( current_call_frame: &CallFrame, args_size: usize, @@ -711,3 +688,38 @@ pub fn call( .checked_add(dynamic_gas) .ok_or(OutOfGasError::GasCostOverflow)?) } + +pub fn callcode( + new_memory_size: U256, + current_memory_size: U256, + address_is_cold: bool, + value_to_transfer: U256, +) -> Result { + let static_gas = CALLCODE_STATIC; + + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; + let address_access_cost = address_access_cost( + address_is_cold, + CALLCODE_STATIC, + CALLCODE_COLD_DYNAMIC, + CALLCODE_WARM_DYNAMIC, + )?; + let positive_value_cost = if !value_to_transfer.is_zero() { + CALLCODE_POSITIVE_VALUE + .checked_add(CALLCODE_POSITIVE_VALUE_STIPEND) + .ok_or(InternalError::ArithmeticOperationOverflow)? + } else { + U256::zero() + }; + + // Note: code_execution_cost will be charged from the sub context post-state. + let dynamic_gas = memory_expansion_cost + .checked_add(address_access_cost) + .ok_or(OutOfGasError::GasCostOverflow)? + .checked_add(positive_value_cost) + .ok_or(OutOfGasError::GasCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_gas) + .ok_or(OutOfGasError::GasCostOverflow)?) +} diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index a0aaf2e36..2dca35658 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -97,8 +97,8 @@ impl VM { ) -> Result { let gas = current_call_frame.stack.pop()?; let code_address = word_to_address(current_call_frame.stack.pop()?); - let value = current_call_frame.stack.pop()?; - let args_offset: usize = current_call_frame + let value_to_transfer = current_call_frame.stack.pop()?; + let args_start_offset: usize = current_call_frame .stack .pop()? .try_into() @@ -108,36 +108,39 @@ impl VM { .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_offset: usize = current_call_frame + let return_data_start_offset: usize = current_call_frame .stack .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_size = current_call_frame + let return_data_size = current_call_frame .stack .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - // Gas consumed - let is_cached = self.cache.is_account_cached(&code_address); + let new_memory_size_for_args = (args_start_offset + .checked_add(args_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size_for_return_data = (return_data_start_offset + .checked_add(return_data_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); + let current_memory_size = current_call_frame.memory.data.len(); - if !is_cached { - self.cache_from_db(code_address); - }; + let (_account_info, address_is_cold) = self.access_account(code_address); - let gas_cost = gas_cost::callcode( + self.increase_consumed_gas( current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - value, - is_cached, - ) - .map_err(VMError::OutOfGas)?; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + gas_cost::callcode( + new_memory_size.into(), + current_memory_size.into(), + address_is_cold, + value_to_transfer, + )?, + )?; // Sender and recipient are the same in this case. But the code executed is from another account. let msg_sender = current_call_frame.to; @@ -147,16 +150,16 @@ impl VM { self.generic_call( current_call_frame, gas, - value, + value_to_transfer, msg_sender, to, code_address, false, is_static, - args_offset, + args_start_offset, args_size, - ret_offset, - ret_size, + return_data_start_offset, + return_data_size, ) } From 8a1785dad09f04d5bee6bcd555122bbf0b3f2bc8 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Mon, 25 Nov 2024 23:34:17 -0300 Subject: [PATCH 25/49] Add memory expansion cost functions --- crates/vm/levm/src/memory.rs | 40 +++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/crates/vm/levm/src/memory.rs b/crates/vm/levm/src/memory.rs index 2eab4ada7..995990ca8 100644 --- a/crates/vm/levm/src/memory.rs +++ b/crates/vm/levm/src/memory.rs @@ -1,5 +1,5 @@ use crate::{ - constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE}, + constants::{MEMORY_EXPANSION_QUOTIENT, WORD_SIZE, WORD_SIZE_IN_BYTES}, errors::{InternalError, OutOfGasError, VMError}, }; use ethrex_core::U256; @@ -194,3 +194,41 @@ impl Memory { .into()) } } + +/// The total cost for a given memory size. +pub fn cost(memory_size: U256) -> Result { + let memory_size_word = memory_size + .checked_add( + WORD_SIZE_IN_BYTES + .checked_sub(U256::one()) + .ok_or(InternalError::ArithmeticOperationUnderflow)?, + ) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_div(WORD_SIZE_IN_BYTES) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?; + + Ok(memory_size_word + .checked_pow(U256::from(2)) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_div(U256::from(512)) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)? + .checked_add( + U256::from(3) + .checked_mul(memory_size_word) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?, + ) + .ok_or(OutOfGasError::MemoryExpansionCostOverflow)?) +} + +/// When a memory expansion is triggered, only the additional bytes of memory +/// must be paid for. +pub fn expansion_cost(new_memory_size: U256, old_memory_size: U256) -> Result { + let cost = if new_memory_size <= old_memory_size { + U256::zero() + } else { + cost(new_memory_size)? + .checked_sub(cost(old_memory_size)?) + .ok_or(InternalError::ArithmeticOperationUnderflow)? + }; + Ok(cost) +} From cce5db9e98b598072472725d7e99d3a108a68371 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 10:46:10 -0300 Subject: [PATCH 26/49] Refactor `op_delegatecall` --- crates/vm/levm/src/gas_cost.rs | 25 +++++++++++ crates/vm/levm/src/opcode_handlers/system.rs | 46 +++++++++++--------- 2 files changed, 50 insertions(+), 21 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 584643636..430b13344 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -723,3 +723,28 @@ pub fn callcode( .checked_add(dynamic_gas) .ok_or(OutOfGasError::GasCostOverflow)?) } + +pub fn delegatecall( + new_memory_size: U256, + current_memory_size: U256, + address_is_cold: bool, +) -> Result { + let static_gas = DELEGATECALL_STATIC; + + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; + let address_access_cost = address_access_cost( + address_is_cold, + DELEGATECALL_STATIC, + DELEGATECALL_COLD_DYNAMIC, + DELEGATECALL_WARM_DYNAMIC, + )?; + + // Note: code_execution_cost will be charged from the sub context post-state. + let dynamic_gas = memory_expansion_cost + .checked_add(address_access_cost) + .ok_or(OutOfGasError::GasCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_gas) + .ok_or(OutOfGasError::GasCostOverflow)?) +} diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 2dca35658..9e7cc842d 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -205,7 +205,7 @@ impl VM { ) -> Result { let gas = current_call_frame.stack.pop()?; let code_address = word_to_address(current_call_frame.stack.pop()?); - let args_offset: usize = current_call_frame + let args_start_offset: usize = current_call_frame .stack .pop()? .try_into() @@ -215,12 +215,12 @@ impl VM { .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_offset: usize = current_call_frame + let return_data_start_offset: usize = current_call_frame .stack .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_size = current_call_frame + let return_data_size = current_call_frame .stack .pop()? .try_into() @@ -231,23 +231,27 @@ impl VM { let to = current_call_frame.to; let is_static = current_call_frame.is_static; - // Gas consumed - let is_cached = self.cache.is_account_cached(&code_address); - if !is_cached { - self.cache_from_db(&code_address); - }; + let (_account_info, address_is_cold) = self.access_account(code_address); - let gas_cost = gas_cost::delegatecall( - current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - is_cached, - ) - .map_err(VMError::OutOfGas)?; + let new_memory_size_for_args = (args_start_offset + .checked_add(args_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size_for_return_data = (return_data_start_offset + .checked_add(return_data_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); + let current_memory_size = current_call_frame.memory.data.len(); - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::delegatecall( + new_memory_size.into(), + current_memory_size.into(), + address_is_cold, + )?, + )?; self.generic_call( current_call_frame, @@ -258,10 +262,10 @@ impl VM { code_address, false, is_static, - args_offset, + args_start_offset, args_size, - ret_offset, - ret_size, + return_data_start_offset, + return_data_size, ) } From c0a71c3dae3437e6424e0bd222b91ecc7ebb8d02 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 10:46:43 -0300 Subject: [PATCH 27/49] Refactor `op_delegatecall` leftovers --- crates/vm/levm/src/gas_cost.rs | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 430b13344..7fe5538fc 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -121,6 +121,10 @@ pub const CALLCODE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; pub const CALLCODE_POSITIVE_VALUE: U256 = U256([9000, 0, 0, 0]); pub const CALLCODE_POSITIVE_VALUE_STIPEND: U256 = U256([2300, 0, 0, 0]); +pub const DELEGATECALL_STATIC: U256 = DEFAULT_STATIC; +pub const DELEGATECALL_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const DELEGATECALL_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -359,24 +363,6 @@ pub fn mcopy( .ok_or(OutOfGasError::GasCostOverflow) } -pub fn delegatecall( - current_call_frame: &CallFrame, - args_size: usize, - args_offset: usize, - ret_size: usize, - ret_offset: usize, - is_cached: bool, -) -> Result { - compute_gas_call( - current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - is_cached, - ) -} - pub fn staticcall( current_call_frame: &CallFrame, args_size: usize, From f9dded0fb0e7d1e22cc8ae03bd1c2a2fb3eb7622 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 10:56:09 -0300 Subject: [PATCH 28/49] Refactor `op_staticcall` --- crates/vm/levm/src/gas_cost.rs | 47 +++++++++++------- crates/vm/levm/src/opcode_handlers/system.rs | 51 +++++++++++--------- 2 files changed, 56 insertions(+), 42 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 7fe5538fc..afda0565b 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -125,6 +125,10 @@ pub const DELEGATECALL_STATIC: U256 = DEFAULT_STATIC; pub const DELEGATECALL_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; pub const DELEGATECALL_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; +pub const STATICCALL_STATIC: U256 = DEFAULT_STATIC; +pub const STATICCALL_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; +pub const STATICCALL_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; + // Costs in gas for call opcodes (in wei) pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); pub const COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); @@ -363,24 +367,6 @@ pub fn mcopy( .ok_or(OutOfGasError::GasCostOverflow) } -pub fn staticcall( - current_call_frame: &CallFrame, - args_size: usize, - args_offset: usize, - ret_size: usize, - ret_offset: usize, - is_cached: bool, -) -> Result { - compute_gas_call( - current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - is_cached, - ) -} - fn compute_gas_call( current_call_frame: &CallFrame, args_size: usize, @@ -734,3 +720,28 @@ pub fn delegatecall( .checked_add(dynamic_gas) .ok_or(OutOfGasError::GasCostOverflow)?) } + +pub fn staticcall( + new_memory_size: U256, + current_memory_size: U256, + address_is_cold: bool, +) -> Result { + let static_gas = STATICCALL_STATIC; + + let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; + let address_access_cost = address_access_cost( + address_is_cold, + STATICCALL_STATIC, + STATICCALL_COLD_DYNAMIC, + STATICCALL_WARM_DYNAMIC, + )?; + + // Note: code_execution_cost will be charged from the sub context post-state. + let dynamic_gas = memory_expansion_cost + .checked_add(address_access_cost) + .ok_or(OutOfGasError::GasCostOverflow)?; + + Ok(static_gas + .checked_add(dynamic_gas) + .ok_or(OutOfGasError::GasCostOverflow)?) +} diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 9e7cc842d..6a7e51e73 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -277,7 +277,7 @@ impl VM { ) -> Result { let gas = current_call_frame.stack.pop()?; let code_address = word_to_address(current_call_frame.stack.pop()?); - let args_offset: usize = current_call_frame + let args_start_offset: usize = current_call_frame .stack .pop()? .try_into() @@ -287,39 +287,42 @@ impl VM { .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_offset: usize = current_call_frame + let return_data_start_offset: usize = current_call_frame .stack .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let ret_size = current_call_frame + let return_data_size = current_call_frame .stack .pop()? .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let value = U256::zero(); - let msg_sender = current_call_frame.to; // The new sender will be the current contract. - let to = code_address; // In this case code_address and the sub-context account are the same. Unlike CALLCODE or DELEGATECODE. - - // Gas consumed - let is_cached = self.cache.is_account_cached(&code_address); + let (_account_info, address_is_cold) = self.access_account(code_address); - if !is_cached { - self.cache_from_db(&code_address); - }; + let new_memory_size_for_args = (args_start_offset + .checked_add(args_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size_for_return_data = (return_data_start_offset + .checked_add(return_data_size) + .ok_or(InternalError::ArithmeticOperationOverflow)?) + .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); + let current_memory_size = current_call_frame.memory.data.len(); - let gas_cost = gas_cost::staticcall( + self.increase_consumed_gas( current_call_frame, - args_size, - args_offset, - ret_size, - ret_offset, - is_cached, - ) - .map_err(VMError::OutOfGas)?; + gas_cost::staticcall( + new_memory_size.into(), + current_memory_size.into(), + address_is_cold, + )?, + )?; - self.increase_consumed_gas(current_call_frame, gas_cost)?; + let value = U256::zero(); + let msg_sender = current_call_frame.to; // The new sender will be the current contract. + let to = code_address; // In this case code_address and the sub-context account are the same. Unlike CALLCODE or DELEGATECODE. self.generic_call( current_call_frame, @@ -330,10 +333,10 @@ impl VM { code_address, false, true, - args_offset, + args_start_offset, args_size, - ret_offset, - ret_size, + return_data_start_offset, + return_data_size, ) } From 6af6ce3f97ccb17fa31598b8dd1335d72dec79af Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 15:04:52 -0300 Subject: [PATCH 29/49] Refactor transaction validation --- crates/vm/levm/src/vm.rs | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index f3041fbbf..322ca18c6 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -374,29 +374,20 @@ impl VM { } } - let origin = self.env.origin; + let (sender_account_info, _address_is_cold) = self.access_account(self.env.origin); - let mut sender_account = self.get_account(&origin); - - // See if it's raised in upper layers - sender_account.info.nonce = sender_account - .info - .nonce - .checked_add(1) - .ok_or(VMError::Internal(InternalError::NonceOverflowed))?; + self.increment_account_nonce(self.env.origin)?; // (4) - if sender_account.has_code()? { + if sender_account_info.has_code() { return Err(VMError::SenderAccountShouldNotHaveBytecode); } // (6) - if sender_account.info.balance < call_frame.msg_value { + if sender_account_info.balance < call_frame.msg_value { return Err(VMError::SenderBalanceShouldContainTransferValue); } - self.cache.add_account(&origin, &sender_account); - // (7) if self.env.gas_price < self.env.base_fee_per_gas { return Err(VMError::GasPriceIsLowerThanBaseFee); From 08c545b0443437d4268877912f04e1dd579b34d4 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 16:39:20 -0300 Subject: [PATCH 30/49] Refactor `op_sload` --- crates/vm/levm/src/constants.rs | 5 ---- crates/vm/levm/src/gas_cost.rs | 24 +++++++++++------ .../stack_memory_storage_flow.rs | 27 ++++++------------- 3 files changed, 24 insertions(+), 32 deletions(-) diff --git a/crates/vm/levm/src/constants.rs b/crates/vm/levm/src/constants.rs index 83bb88ec9..ba91f928f 100644 --- a/crates/vm/levm/src/constants.rs +++ b/crates/vm/levm/src/constants.rs @@ -45,11 +45,6 @@ pub const TARGET_BLOB_GAS_PER_BLOCK: U256 = U256([393216, 0, 0, 0]); // TARGET_B pub const MIN_BASE_FEE_PER_BLOB_GAS: U256 = U256([1, 0, 0, 0]); pub const BLOB_BASE_FEE_UPDATE_FRACTION: U256 = U256([3338477, 0, 0, 0]); -// Storage constants -pub const COLD_STORAGE_ACCESS_COST: U256 = U256([2100, 0, 0, 0]); -pub const WARM_ADDRESS_ACCESS_COST: U256 = U256([100, 0, 0, 0]); -pub const BALANCE_COLD_ADDRESS_ACCESS_COST: U256 = U256([2600, 0, 0, 0]); - // Block constants pub const LAST_AVAILABLE_BLOCK_LIMIT: U256 = U256([256, 0, 0, 0]); pub const MAX_BLOCK_GAS_LIMIT: U256 = U256([30_000_000, 0, 0, 0]); diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index afda0565b..634f06e85 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -1,6 +1,6 @@ use crate::{ call_frame::CallFrame, - constants::{COLD_STORAGE_ACCESS_COST, WORD_SIZE, WORD_SIZE_IN_BYTES}, + constants::{WORD_SIZE, WORD_SIZE_IN_BYTES}, errors::{InternalError, OutOfGasError, VMError}, memory, StorageSlot, }; @@ -91,6 +91,10 @@ pub const DEFAULT_STATIC: U256 = U256::zero(); pub const DEFAULT_COLD_DYNAMIC: U256 = U256([2600, 0, 0, 0]); pub const DEFAULT_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); +pub const SLOAD_STATIC: U256 = U256::zero(); +pub const SLOAD_COLD_DYNAMIC: U256 = U256([2100, 0, 0, 0]); +pub const SLOAD_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); + pub const BALANCE_STATIC: U256 = DEFAULT_STATIC; pub const BALANCE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; pub const BALANCE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; @@ -294,14 +298,18 @@ fn mem_expansion_behavior( .ok_or(OutOfGasError::GasCostOverflow) } -pub fn sload(is_cached: bool) -> U256 { - if is_cached { - // If slot is warm (cached) add 100 to base_dynamic_gas - WARM_ADDRESS_ACCESS_COST +pub fn sload(storage_slot_was_cold: bool) -> Result { + let static_gas = SLOAD_STATIC; + + let dynamic_cost = if storage_slot_was_cold { + SLOAD_COLD_DYNAMIC } else { - // If slot is cold (not cached) add 2100 to base_dynamic_gas - COLD_STORAGE_ACCESS_COST - } + SLOAD_WARM_DYNAMIC + }; + + Ok(static_gas + .checked_add(dynamic_cost) + .ok_or(OutOfGasError::GasCostOverflow)?) } pub fn sstore( diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index 55b38884d..bb5d4f6c7 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -1,7 +1,6 @@ use crate::{ - account::StorageSlot, call_frame::CallFrame, - constants::{COLD_STORAGE_ACCESS_COST, WARM_ADDRESS_ACCESS_COST, WORD_SIZE}, + constants::WORD_SIZE, errors::{InternalError, OpcodeSuccess, OutOfGasError, VMError}, gas_cost, vm::VM, @@ -132,29 +131,19 @@ impl VM { &mut self, current_call_frame: &mut CallFrame, ) -> Result { - let key = current_call_frame.stack.pop()?; - + let storage_slot_key = current_call_frame.stack.pop()?; let address = current_call_frame.to; let mut bytes = [0u8; 32]; - key.to_big_endian(&mut bytes); - let key = H256::from(bytes); + storage_slot_key.to_big_endian(&mut bytes); + let storage_slot_key = H256::from(bytes); - let is_cached = self.cache.is_slot_cached(&address, key); + let (storage_slot, storage_slot_was_cold) = + self.access_storage_slot(address, storage_slot_key); - let gas_cost = if is_cached { - // If slot is warm (cached) add 100 to gas_cost - WARM_ADDRESS_ACCESS_COST - } else { - // If slot is cold (not cached) add 2100 to gas_cost - COLD_STORAGE_ACCESS_COST - }; - - let current_value = self.get_storage_slot(&address, key).current_value; - - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas(current_call_frame, gas_cost::sload(storage_slot_was_cold)?)?; - current_call_frame.stack.push(current_value)?; + current_call_frame.stack.push(storage_slot.current_value)?; Ok(OpcodeSuccess::Continue) } From e80ed33fa3838bb538c984ecd068edc1888dd520 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 16:58:14 -0300 Subject: [PATCH 31/49] Refactor `op_sstore` --- crates/vm/levm/src/gas_cost.rs | 35 ++++++++--------- .../stack_memory_storage_flow.rs | 38 +++++++------------ 2 files changed, 32 insertions(+), 41 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 634f06e85..451b91e26 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -95,6 +95,8 @@ pub const SLOAD_STATIC: U256 = U256::zero(); pub const SLOAD_COLD_DYNAMIC: U256 = U256([2100, 0, 0, 0]); pub const SLOAD_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); +pub const SSTORE_STATIC: U256 = U256::zero(); + pub const BALANCE_STATIC: U256 = DEFAULT_STATIC; pub const BALANCE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; pub const BALANCE_WARM_DYNAMIC: U256 = DEFAULT_WARM_DYNAMIC; @@ -313,34 +315,33 @@ pub fn sload(storage_slot_was_cold: bool) -> Result { } pub fn sstore( - value: U256, - is_cached: bool, storage_slot: &StorageSlot, -) -> Result { - let mut base_dynamic_gas: U256 = U256::zero(); - - if !is_cached { - // If slot is cold 2100 is added to base_dynamic_gas - base_dynamic_gas = base_dynamic_gas - .checked_add(U256::from(2100)) - .ok_or(OutOfGasError::GasCostOverflow)?; - }; + new_value: U256, + storage_slot_was_cold: bool, +) -> Result { + let static_gas = SSTORE_STATIC; - let sstore_gas_cost = if value == storage_slot.current_value { + let mut base_dynamic_gas = if new_value == storage_slot.current_value { U256::from(100) } else if storage_slot.current_value == storage_slot.original_value { - if storage_slot.original_value == U256::zero() { + if storage_slot.original_value.is_zero() { U256::from(20000) } else { - U256::from(2900) + U256::from(5000) } } else { U256::from(100) }; - base_dynamic_gas - .checked_add(sstore_gas_cost) - .ok_or(OutOfGasError::GasCostOverflow) + if storage_slot_was_cold { + base_dynamic_gas = base_dynamic_gas + .checked_add(U256::from(2100)) + .ok_or(OutOfGasError::GasCostOverflow)?; + } + + Ok(static_gas + .checked_add(base_dynamic_gas) + .ok_or(OutOfGasError::GasCostOverflow)?) } pub fn mcopy( diff --git a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs index bb5d4f6c7..b7869fe12 100644 --- a/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs +++ b/crates/vm/levm/src/opcode_handlers/stack_memory_storage_flow.rs @@ -157,31 +157,28 @@ impl VM { return Err(VMError::OpcodeNotAllowedInStaticContext); } - let key = current_call_frame.stack.pop()?; - let value = current_call_frame.stack.pop()?; + let storage_slot_key = current_call_frame.stack.pop()?; + let new_storage_slot_value = current_call_frame.stack.pop()?; // Convert key from U256 to H256 let mut bytes = [0u8; 32]; - key.to_big_endian(&mut bytes); + storage_slot_key.to_big_endian(&mut bytes); let key = H256::from(bytes); - let address = current_call_frame.to; - - let is_cached = self.cache.is_slot_cached(&address, key); - - let storage_slot = self.get_storage_slot(&address, key); - - let gas_cost = - gas_cost::sstore(value, is_cached, &storage_slot).map_err(VMError::OutOfGas)?; + let (storage_slot, storage_slot_was_cold) = + self.access_storage_slot(current_call_frame.to, key); - self.increase_consumed_gas(current_call_frame, gas_cost)?; + self.increase_consumed_gas( + current_call_frame, + gas_cost::sstore(&storage_slot, new_storage_slot_value, storage_slot_was_cold)?, + )?; // Gas Refunds // TODO: Think about what to do in case of underflow of gas refunds (when we try to substract from it if the value is low) let mut gas_refunds = U256::zero(); - if value != storage_slot.current_value { + if new_storage_slot_value != storage_slot.current_value { if storage_slot.current_value == storage_slot.original_value { - if storage_slot.original_value.is_zero() && value.is_zero() { + if storage_slot.original_value.is_zero() && new_storage_slot_value.is_zero() { gas_refunds = gas_refunds .checked_add(U256::from(4800)) .ok_or(VMError::GasRefundsOverflow)?; @@ -191,12 +188,12 @@ impl VM { gas_refunds = gas_refunds .checked_sub(U256::from(4800)) .ok_or(VMError::GasRefundsUnderflow)?; - } else if value.is_zero() { + } else if new_storage_slot_value.is_zero() { gas_refunds = gas_refunds .checked_add(U256::from(4800)) .ok_or(VMError::GasRefundsOverflow)?; } - } else if value == storage_slot.original_value { + } else if new_storage_slot_value == storage_slot.original_value { if storage_slot.original_value.is_zero() { gas_refunds = gas_refunds .checked_add(U256::from(19900)) @@ -215,14 +212,7 @@ impl VM { .checked_add(gas_refunds) .ok_or(VMError::GasLimitPriceProductOverflow)?; - self.cache.write_account_storage( - &address, - key, - StorageSlot { - original_value: storage_slot.original_value, - current_value: value, - }, - )?; + self.update_account_storage(current_call_frame.to, key, new_storage_slot_value)?; Ok(OpcodeSuccess::Continue) } From fe459a426ae899980500b0d8cacd859c8eb73c84 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 16:58:32 -0300 Subject: [PATCH 32/49] Leftovers --- crates/vm/levm/src/gas_cost.rs | 69 ++++++++++------------------------ 1 file changed, 20 insertions(+), 49 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index 451b91e26..b681e2143 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -376,35 +376,6 @@ pub fn mcopy( .ok_or(OutOfGasError::GasCostOverflow) } -fn compute_gas_call( - current_call_frame: &CallFrame, - args_size: usize, - args_offset: usize, - ret_size: usize, - ret_offset: usize, - is_cached: bool, -) -> Result { - let memory_byte_size = args_offset - .checked_add(args_size) - .and_then(|src_sum| { - ret_offset - .checked_add(ret_size) - .map(|dest_sum| src_sum.max(dest_sum)) - }) - .ok_or(OutOfGasError::GasCostOverflow)?; - let memory_expansion_cost = current_call_frame.memory.expansion_cost(memory_byte_size)?; - - let access_cost = if is_cached { - WARM_ADDRESS_ACCESS_COST - } else { - COLD_ADDRESS_ACCESS_COST - }; - - memory_expansion_cost - .checked_add(access_cost) - .ok_or(OutOfGasError::GasCostOverflow) -} - pub fn create( current_call_frame: &CallFrame, code_offset_in_memory: U256, @@ -478,10 +449,10 @@ fn compute_gas_create( .ok_or(OutOfGasError::CreationCostIsTooHigh) } -pub fn selfdestruct(is_cached: bool, account_is_empty: bool) -> Result { +pub fn selfdestruct(address_was_cold: bool, account_is_empty: bool) -> Result { let mut gas_cost = SELFDESTRUCT_STATIC; - if !is_cached { + if address_was_cold { gas_cost = gas_cost .checked_add(COLD_ADDRESS_ACCESS_COST) .ok_or(OutOfGasError::GasCostOverflow)?; @@ -532,13 +503,13 @@ pub fn tx_creation(code_length: u64, number_of_words: u64) -> Result Result { let static_gas = static_cost; - let dynamic_cost: U256 = if address_is_cold { + let dynamic_cost: U256 = if address_was_cold { cold_dynamic_cost } else { warm_dynamic_cost @@ -580,18 +551,18 @@ fn memory_access_cost( .ok_or(OutOfGasError::GasCostOverflow)?) } -pub fn balance(address_is_cold: bool) -> Result { +pub fn balance(address_was_cold: bool) -> Result { address_access_cost( - address_is_cold, + address_was_cold, BALANCE_STATIC, BALANCE_COLD_DYNAMIC, BALANCE_WARM_DYNAMIC, ) } -pub fn extcodesize(address_is_cold: bool) -> Result { +pub fn extcodesize(address_was_cold: bool) -> Result { address_access_cost( - address_is_cold, + address_was_cold, EXTCODESIZE_STATIC, EXTCODESIZE_COLD_DYNAMIC, EXTCODESIZE_WARM_DYNAMIC, @@ -601,7 +572,7 @@ pub fn extcodesize(address_is_cold: bool) -> Result { pub fn extcodecopy( new_memory_size: U256, current_memory_size: U256, - address_is_cold: bool, + address_was_cold: bool, ) -> Result { Ok(memory_access_cost( new_memory_size, @@ -610,7 +581,7 @@ pub fn extcodecopy( EXTCODECOPY_DYNAMIC_BASE, )? .checked_add(address_access_cost( - address_is_cold, + address_was_cold, EXTCODECOPY_STATIC, EXTCODECOPY_COLD_DYNAMIC, EXTCODECOPY_WARM_DYNAMIC, @@ -618,9 +589,9 @@ pub fn extcodecopy( .ok_or(OutOfGasError::GasCostOverflow)?) } -pub fn extcodehash(address_is_cold: bool) -> Result { +pub fn extcodehash(address_was_cold: bool) -> Result { address_access_cost( - address_is_cold, + address_was_cold, EXTCODEHASH_STATIC, EXTCODEHASH_COLD_DYNAMIC, EXTCODEHASH_WARM_DYNAMIC, @@ -630,7 +601,7 @@ pub fn extcodehash(address_is_cold: bool) -> Result { pub fn call( new_memory_size: U256, current_memory_size: U256, - address_is_cold: bool, + address_was_cold: bool, address_is_empty: bool, value_to_transfer: U256, ) -> Result { @@ -638,7 +609,7 @@ pub fn call( let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let address_access_cost = address_access_cost( - address_is_cold, + address_was_cold, CALL_STATIC, CALL_COLD_DYNAMIC, CALL_WARM_DYNAMIC, @@ -673,14 +644,14 @@ pub fn call( pub fn callcode( new_memory_size: U256, current_memory_size: U256, - address_is_cold: bool, + address_was_cold: bool, value_to_transfer: U256, ) -> Result { let static_gas = CALLCODE_STATIC; let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let address_access_cost = address_access_cost( - address_is_cold, + address_was_cold, CALLCODE_STATIC, CALLCODE_COLD_DYNAMIC, CALLCODE_WARM_DYNAMIC, @@ -708,13 +679,13 @@ pub fn callcode( pub fn delegatecall( new_memory_size: U256, current_memory_size: U256, - address_is_cold: bool, + address_was_cold: bool, ) -> Result { let static_gas = DELEGATECALL_STATIC; let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let address_access_cost = address_access_cost( - address_is_cold, + address_was_cold, DELEGATECALL_STATIC, DELEGATECALL_COLD_DYNAMIC, DELEGATECALL_WARM_DYNAMIC, @@ -733,13 +704,13 @@ pub fn delegatecall( pub fn staticcall( new_memory_size: U256, current_memory_size: U256, - address_is_cold: bool, + address_was_cold: bool, ) -> Result { let static_gas = STATICCALL_STATIC; let memory_expansion_cost = memory::expansion_cost(new_memory_size, current_memory_size)?; let address_access_cost = address_access_cost( - address_is_cold, + address_was_cold, STATICCALL_STATIC, STATICCALL_COLD_DYNAMIC, STATICCALL_WARM_DYNAMIC, From 4cdabdc7c0d315df842213bf15e30238de9fc30e Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 17:24:36 -0300 Subject: [PATCH 33/49] Refactor DB module --- crates/vm/levm/src/account.rs | 15 +-- crates/vm/levm/src/db/cache.rs | 35 +++++ crates/vm/levm/src/{db.rs => db/mod.rs} | 71 +--------- crates/vm/levm/src/errors.rs | 8 +- crates/vm/levm/src/utils.rs | 14 +- crates/vm/levm/tests/tests.rs | 164 ++++++++++++------------ crates/vm/vm.rs | 4 +- 7 files changed, 136 insertions(+), 175 deletions(-) create mode 100644 crates/vm/levm/src/db/cache.rs rename crates/vm/levm/src/{db.rs => db/mod.rs} (51%) diff --git a/crates/vm/levm/src/account.rs b/crates/vm/levm/src/account.rs index da8872a92..2df80233a 100644 --- a/crates/vm/levm/src/account.rs +++ b/crates/vm/levm/src/account.rs @@ -1,7 +1,4 @@ -use crate::{ - constants::EMPTY_CODE_HASH, - errors::{InternalError, VMError}, -}; +use crate::constants::EMPTY_CODE_HASH; use bytes::Bytes; use ethrex_core::{H256, U256}; use keccak_hash::keccak; @@ -89,14 +86,4 @@ impl Account { self.info.nonce = nonce; self } - - // TODO: Replace nonce increments with this or cache's analog (currently does not have senders) - pub fn increment_nonce(&mut self) -> Result<(), VMError> { - self.info.nonce = self - .info - .nonce - .checked_add(1) - .ok_or(VMError::Internal(InternalError::NonceOverflowed))?; - Ok(()) - } } diff --git a/crates/vm/levm/src/db/cache.rs b/crates/vm/levm/src/db/cache.rs new file mode 100644 index 000000000..dc5847003 --- /dev/null +++ b/crates/vm/levm/src/db/cache.rs @@ -0,0 +1,35 @@ +use crate::Account; +use ethrex_core::Address; +use std::collections::HashMap; + +pub type CacheDB = HashMap; + +pub fn get_account<'cache>( + cached_accounts: &'cache CacheDB, + address: &Address, +) -> Option<&'cache Account> { + cached_accounts.get(address) +} + +pub fn get_account_mut<'cache>( + cached_accounts: &'cache mut CacheDB, + address: &Address, +) -> Option<&'cache mut Account> { + cached_accounts.get_mut(address) +} + +pub fn insert_account( + cached_accounts: &mut CacheDB, + address: Address, + account: Account, +) -> Option { + cached_accounts.insert(address, account) +} + +pub fn remove_account(cached_accounts: &mut CacheDB, address: &Address) -> Option { + cached_accounts.remove(address) +} + +pub fn is_account_cached(cached_accounts: &CacheDB, address: &Address) -> bool { + cached_accounts.contains_key(address) +} diff --git a/crates/vm/levm/src/db.rs b/crates/vm/levm/src/db/mod.rs similarity index 51% rename from crates/vm/levm/src/db.rs rename to crates/vm/levm/src/db/mod.rs index d9abe7681..f3bdfb65f 100644 --- a/crates/vm/levm/src/db.rs +++ b/crates/vm/levm/src/db/mod.rs @@ -1,10 +1,10 @@ -use crate::{ - account::{Account, AccountInfo, StorageSlot}, - errors::{InternalError, VMError}, -}; +use crate::account::{Account, AccountInfo, StorageSlot}; use ethrex_core::{Address, H256, U256}; use std::collections::HashMap; +pub mod cache; +pub use cache::CacheDB; + pub trait Database { fn get_account_info(&self, address: Address) -> AccountInfo; fn get_storage_slot(&self, address: Address, key: H256) -> U256; @@ -73,66 +73,3 @@ impl Database for Db { self.block_hashes.get(&block_number).cloned() } } - -#[derive(Debug, Default, Clone, Eq, PartialEq)] -pub struct Cache { - pub accounts: HashMap, -} - -impl Cache { - pub fn get_account(&self, address: Address) -> Option<&Account> { - self.accounts.get(&address) - } - - pub fn get_mut_account(&mut self, address: Address) -> Option<&mut Account> { - self.accounts.get_mut(&address) - } - - pub fn get_storage_slot(&self, address: Address, key: H256) -> Option { - self.get_account(address)?.storage.get(&key).cloned() - } - - pub fn add_account(&mut self, address: &Address, account: &Account) { - self.accounts.insert(*address, account.clone()); - } - - pub fn write_account_storage( - &mut self, - address: &Address, - key: H256, - slot: StorageSlot, - ) -> Result<(), VMError> { - self.accounts - .get_mut(address) - .ok_or(VMError::Internal( - InternalError::AccountShouldHaveBeenCached, - ))? - .storage - .insert(key, slot); - Ok(()) - } - - // TODO: Replace nonce increments with this (currently does not have senders) - pub fn increment_account_nonce(&mut self, address: &Address) -> Result<(), VMError> { - if let Some(account) = self.accounts.get_mut(address) { - account.info.nonce = account - .info - .nonce - .checked_add(1) - .ok_or(VMError::Internal(InternalError::NonceOverflowed))?; - } - Ok(()) - } - - pub fn is_account_cached(&self, address: &Address) -> bool { - self.accounts.contains_key(address) - } - - pub fn is_slot_cached(&self, address: &Address, key: H256) -> bool { - self.is_account_cached(address) - && self - .get_account(*address) - .map(|account| account.storage.contains_key(&key)) - .unwrap_or(false) - } -} diff --git a/crates/vm/levm/src/errors.rs b/crates/vm/levm/src/errors.rs index b6bad188c..ab7257805 100644 --- a/crates/vm/levm/src/errors.rs +++ b/crates/vm/levm/src/errors.rs @@ -65,6 +65,10 @@ pub enum VMError { GasRefundsOverflow, #[error("Memory size overflows")] MemorySizeOverflow, + #[error("Nonce overflowed")] + NonceOverflow, + #[error("Nonce underflowed")] + NonceUnderflow, // OutOfGas #[error("Out Of Gas")] OutOfGas(#[from] OutOfGasError), @@ -91,10 +95,6 @@ pub enum OutOfGasError { #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] pub enum InternalError { - #[error("Overflowed when incrementing nonce")] - NonceOverflowed, - #[error("Underflowed when incrementing nonce")] - NonceUnderflowed, #[error("Overflowed when incrementing program counter")] PCOverflowed, #[error("Underflowed when decrementing program counter")] diff --git a/crates/vm/levm/src/utils.rs b/crates/vm/levm/src/utils.rs index 56dc716fe..02fcb9177 100644 --- a/crates/vm/levm/src/utils.rs +++ b/crates/vm/levm/src/utils.rs @@ -1,6 +1,6 @@ use crate::{ account::{Account, AccountInfo}, - db::{Cache, Db}, + db::{cache, CacheDB, Db}, environment::Environment, errors::{InternalError, VMError}, operations::Operation, @@ -27,7 +27,7 @@ pub fn new_vm_with_bytecode(bytecode: Bytes) -> Result { Address::from_low_u64_be(100), U256::MAX, Db::new(), - Cache::default(), + CacheDB::default(), ) } @@ -38,7 +38,7 @@ pub fn new_vm_with_ops(operations: &[Operation]) -> Result { Address::from_low_u64_be(100), U256::MAX, Db::new(), - Cache::default(), + CacheDB::default(), ) } @@ -49,7 +49,7 @@ pub fn new_vm_with_ops_db(operations: &[Operation], db: Db) -> Result Result { let accounts = [ // This is the contract account that is going to be executed @@ -91,8 +91,8 @@ pub fn new_vm_with_ops_addr_bal_db( db.add_accounts(accounts.to_vec()); // add to cache accounts from list accounts - cache.add_account(&accounts[0].0, &accounts[0].1); - cache.add_account(&accounts[1].0, &accounts[1].1); + cache::insert_account(&mut cache, accounts[0].0, accounts[0].1.clone()); + cache::insert_account(&mut cache, accounts[1].0, accounts[1].1.clone()); let env = Environment::default_from_address(sender_address); diff --git a/crates/vm/levm/tests/tests.rs b/crates/vm/levm/tests/tests.rs index 06fef1b97..be9a1519d 100644 --- a/crates/vm/levm/tests/tests.rs +++ b/crates/vm/levm/tests/tests.rs @@ -6,7 +6,7 @@ use ethrex_core::{types::TxKind, Address, H256, U256}; use ethrex_levm::{ account::Account, constants::*, - db::{Cache, Db}, + db::{cache, CacheDB, Db}, errors::{TxResult, VMError}, gas_cost, operations::Operation, @@ -1717,8 +1717,8 @@ fn call_returns_if_bytecode_empty() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -1761,8 +1761,8 @@ fn call_changes_callframe_and_stores() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -1862,9 +1862,9 @@ fn nested_calls() { (callee3_address, callee3_account.clone()), ]); - let mut cache = Cache::default(); - cache.add_account(&callee2_address, &callee2_account); - cache.add_account(&callee3_address, &callee3_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee2_address, callee2_account); + cache::insert_account(&mut cache, callee3_address, callee3_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -1935,8 +1935,8 @@ fn staticcall_changes_callframe_is_static() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -2051,8 +2051,8 @@ fn pc_op_with_push_offset() { // let mut db = Db::new(); // db.add_accounts(vec![(callee_address, callee_account.clone())]); -// let mut cache = Cache::default(); -// cache.add_account(&callee_address, &callee_account); +// let mut cache = CacheDB::default(); +// cache::insert_account(&mut cache, callee_address, callee_account); // let mut vm = new_vm_with_ops_addr_bal_db( // ops_to_bytecode(&caller_ops).unwrap(), @@ -2115,8 +2115,8 @@ fn pc_op_with_push_offset() { // let mut db = Db::new(); // db.add_accounts(vec![(callee_address, callee_account.clone())]); -// let mut cache = Cache::default(); -// cache.add_account(&callee_address, &callee_account); +// let mut cache = CacheDB::default(); +// cache::insert_account(&mut cache, callee_address, callee_account); // let mut vm = new_vm_with_ops_addr_bal_db( // ops_to_bytecode(&caller_ops).unwrap(), @@ -2177,8 +2177,8 @@ fn pc_op_with_push_offset() { // let mut db = Db::new(); // db.add_accounts(vec![(callee_address, callee_account.clone())]); -// let mut cache = Cache::default(); -// cache.add_account(&callee_address, &callee_account); +// let mut cache = CacheDB::default(); +// cache::insert_account(&mut cache, callee_address, callee_account); // let mut vm = new_vm_with_ops_addr_bal_db( // ops_to_bytecode(&caller_ops).unwrap(), @@ -2238,8 +2238,8 @@ fn pc_op_with_push_offset() { // let mut db = Db::new(); // db.add_accounts(vec![(callee_address, callee_account.clone())]); -// let mut cache = Cache::default(); -// cache.add_account(&callee_address, &callee_account); +// let mut cache = CacheDB::default(); +// cache::insert_account(&mut cache, callee_address, callee_account); // let mut vm = new_vm_with_ops_addr_bal_db( // ops_to_bytecode(&caller_ops).unwrap(), @@ -2461,8 +2461,8 @@ fn calldataload_being_set_by_parent() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -2598,8 +2598,8 @@ fn returndatacopy_being_set_by_parent() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -2641,7 +2641,7 @@ fn blockhash_op() { Address::default(), U256::MAX, db, - Cache::default(), + CacheDB::default(), ) .unwrap(); @@ -2709,7 +2709,7 @@ fn blockhash_block_number_not_from_recent_256() { Address::default(), U256::MAX, db, - Cache::default(), + CacheDB::default(), ) .unwrap(); @@ -2988,9 +2988,9 @@ fn sstore_op() { key.to_big_endian(&mut bytes); let key = H256::from(bytes); - let stored_value = vm.cache.get_storage_slot(sender_address, key).unwrap(); + let (storage_slot, _storage_slot_was_cold) = vm.access_storage_slot(sender_address, key); - assert_eq!(value, stored_value.current_value); + assert_eq!(value, storage_slot.current_value); } #[test] @@ -3412,8 +3412,8 @@ fn logs_from_multiple_callers() { let mut db = Db::new(); db.add_accounts(vec![(callee_address, callee_account.clone())]); - let mut cache = Cache::default(); - cache.add_account(&callee_address, &callee_account); + let mut cache = CacheDB::default(); + cache::insert_account(&mut cache, callee_address, callee_account); let mut vm = new_vm_with_ops_addr_bal_db( ops_to_bytecode(&caller_ops).unwrap(), @@ -3785,7 +3785,7 @@ fn create_happy_path() { sender_addr, sender_balance, Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); vm.current_call_frame_mut().unwrap().msg_sender = sender_addr; @@ -3800,15 +3800,12 @@ fn create_happy_path() { assert_eq!(word_to_address(returned_address), expected_address); // check the created account is correct - let new_account = vm - .cache - .get_account(word_to_address(returned_address)) - .unwrap(); + let new_account = cache::get_account(&vm.cache, &word_to_address(returned_address)).unwrap(); assert_eq!(new_account.info.balance, U256::from(value_to_transfer)); assert_eq!(new_account.info.nonce, 0); // This was previously set to 1 but I understand that a new account should have nonce 0 // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce + 1); assert_eq!( sender_account.info.balance, @@ -3832,7 +3829,7 @@ fn cant_create_with_size_longer_than_max_code_size() { sender_addr, sender_balance, Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); vm.current_call_frame_mut().unwrap().msg_sender = sender_addr; @@ -3845,7 +3842,7 @@ fn cant_create_with_size_longer_than_max_code_size() { assert_eq!(create_return_value, U256::from(REVERT_FOR_CREATE)); // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce); assert_eq!(sender_account.info.balance, sender_balance); } @@ -3866,7 +3863,7 @@ fn cant_create_on_static_contexts() { sender_addr, sender_balance, Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); vm.current_call_frame_mut().unwrap().msg_sender = sender_addr; @@ -3880,7 +3877,7 @@ fn cant_create_on_static_contexts() { assert_eq!(create_return_value, U256::from(REVERT_FOR_CREATE)); // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce); assert_eq!(sender_account.info.balance, sender_balance); } @@ -3901,7 +3898,7 @@ fn cant_create_if_transfer_value_bigger_than_balance() { sender_addr, sender_balance, Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); vm.current_call_frame_mut().unwrap().msg_sender = sender_addr; @@ -3914,7 +3911,7 @@ fn cant_create_if_transfer_value_bigger_than_balance() { assert_eq!(create_return_value, U256::from(REVERT_FOR_CREATE)); // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce); assert_eq!(sender_account.info.balance, sender_balance); } @@ -3948,7 +3945,7 @@ fn cant_create_if_sender_nonce_would_overflow() { assert_eq!(create_return_value, U256::from(REVERT_FOR_CREATE)); // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce); assert_eq!(sender_account.info.balance, sender_balance); } @@ -4057,7 +4054,7 @@ fn create2_happy_path() { sender_addr, sender_balance, Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); vm.current_call_frame_mut().unwrap().msg_sender = sender_addr; @@ -4069,15 +4066,12 @@ fn create2_happy_path() { let returned_address = call_frame.stack.pop().unwrap(); assert_eq!(word_to_address(returned_address), expected_address); // check the created account is correct - let new_account = vm - .cache - .get_account(word_to_address(returned_address)) - .unwrap(); + let new_account = cache::get_account(&vm.cache, &word_to_address(returned_address)).unwrap(); assert_eq!(new_account.info.balance, U256::from(value)); assert_eq!(new_account.info.nonce, 0); // I understand new account should have nonce 0, not 1. // Check that the sender account is updated - let sender_account = vm.cache.get_account(sender_addr).unwrap(); + let sender_account = cache::get_account(&vm.cache, &sender_addr).unwrap(); assert_eq!(sender_account.info.nonce, sender_nonce + 1); assert_eq!(sender_account.info.balance, sender_balance - value); } @@ -4125,10 +4119,11 @@ fn caller_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(caller); @@ -4166,10 +4161,11 @@ fn origin_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &msg_sender, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + msg_sender, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(msg_sender); @@ -4209,7 +4205,7 @@ fn balance_op() { Address::from_low_u64_be(address), U256::from(1234), Db::new(), - Cache::default(), + CacheDB::default(), ) .unwrap(); @@ -4234,10 +4230,11 @@ fn address_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(Address::from_low_u64_be(42)); @@ -4277,10 +4274,11 @@ fn selfbalance_op() { .with_balance(balance), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default() + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default() .with_bytecode(ops_to_bytecode(&operations).unwrap()) .with_balance(balance), ); @@ -4321,10 +4319,11 @@ fn callvalue_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(Address::from_low_u64_be(42)); @@ -4362,10 +4361,11 @@ fn codesize_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(Address::from_low_u64_be(42)); @@ -4401,10 +4401,11 @@ fn gasprice_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let mut env = Environment::default_from_address(Address::from_low_u64_be(42)); @@ -4458,10 +4459,11 @@ fn codecopy_op() { Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), )]); - let mut cache = Cache::default(); - cache.add_account( - &address_that_has_the_code, - &Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), + let mut cache = CacheDB::default(); + cache::insert_account( + &mut cache, + address_that_has_the_code, + Account::default().with_bytecode(ops_to_bytecode(&operations).unwrap()), ); let env = Environment::default_from_address(Address::from_low_u64_be(42)); @@ -4696,7 +4698,7 @@ fn revert_sstore() { let mut vm = new_vm_with_ops(&operations).unwrap(); vm.current_call_frame_mut().unwrap().code_address = sender_address; - vm.cache.add_account(&sender_address, &Account::default()); + cache::insert_account(&mut vm.cache, sender_address, Account::default()); let mut current_call_frame = vm.call_frames.pop().unwrap(); diff --git a/crates/vm/vm.rs b/crates/vm/vm.rs index 0c54dff60..fc0349dcd 100644 --- a/crates/vm/vm.rs +++ b/crates/vm/vm.rs @@ -80,7 +80,7 @@ impl From for EvmState { cfg_if::cfg_if! { if #[cfg(feature = "levm")] { use ethrex_levm::{ - db::{Cache, Database as LevmDatabase}, + db::{CacheDB, Database as LevmDatabase}, errors::{TransactionReport, TxResult, VMError}, vm::VM, Environment, @@ -187,7 +187,7 @@ cfg_if::cfg_if! { tx.value(), tx.data().clone(), db, - Cache::default(), + CacheDB::default(), )?; vm.transact() From c6cc5700a114b7fda931fab56aa574343196aa32 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 17:24:55 -0300 Subject: [PATCH 34/49] Add new account implementations --- crates/vm/levm/src/account.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/crates/vm/levm/src/account.rs b/crates/vm/levm/src/account.rs index 2df80233a..dd4837554 100644 --- a/crates/vm/levm/src/account.rs +++ b/crates/vm/levm/src/account.rs @@ -15,6 +15,14 @@ impl AccountInfo { pub fn is_empty(&self) -> bool { self.balance.is_zero() && self.nonce == 0 && self.bytecode.is_empty() } + + pub fn has_code(&self) -> bool { + !(self.bytecode.is_empty() || self.bytecode_hash() == EMPTY_CODE_HASH) + } + + pub fn bytecode_hash(&self) -> H256 { + keccak(self.bytecode.as_ref()).0.into() + } } #[derive(Clone, Default, Debug, PartialEq, Eq)] @@ -55,12 +63,12 @@ impl Account { } } - pub fn has_code(&self) -> Result { - Ok(!(self.info.bytecode.is_empty() || self.bytecode_hash() == EMPTY_CODE_HASH)) + pub fn has_code(&self) -> bool { + self.info.has_code() } pub fn bytecode_hash(&self) -> H256 { - keccak(self.info.bytecode.as_ref()).0.into() + self.info.bytecode_hash() } pub fn is_empty(&self) -> bool { From 7dca7b413970542f9454d13d096edcc06cc1f29b Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 17:25:11 -0300 Subject: [PATCH 35/49] Leftovers --- crates/vm/levm/src/errors.rs | 2 + crates/vm/levm/src/opcode_handlers/block.rs | 2 +- .../levm/src/opcode_handlers/environment.rs | 16 +++--- crates/vm/levm/src/opcode_handlers/system.rs | 53 +++++++------------ 4 files changed, 29 insertions(+), 44 deletions(-) diff --git a/crates/vm/levm/src/errors.rs b/crates/vm/levm/src/errors.rs index ab7257805..6cb58875c 100644 --- a/crates/vm/levm/src/errors.rs +++ b/crates/vm/levm/src/errors.rs @@ -91,6 +91,8 @@ pub enum OutOfGasError { MaxGasLimitExceeded, #[error("Arithmetic operation divided by zero in gas calculation")] ArithmeticOperationDividedByZero, + #[error("Memory Expansion Cost Overflow")] + MemoryExpansionCostOverflow, } #[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)] diff --git a/crates/vm/levm/src/opcode_handlers/block.rs b/crates/vm/levm/src/opcode_handlers/block.rs index d252b4d4a..e9662f61e 100644 --- a/crates/vm/levm/src/opcode_handlers/block.rs +++ b/crates/vm/levm/src/opcode_handlers/block.rs @@ -136,7 +136,7 @@ impl VM { // the current account should have been cached when the contract was called let balance = self - .get_account(¤t_call_frame.code_address) + .get_account(current_call_frame.code_address) .info .balance; diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index c86d91661..62e442ea8 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -32,9 +32,9 @@ impl VM { ) -> Result { let address = word_to_address(current_call_frame.stack.pop()?); - let (account_info, address_is_cold) = self.access_account(address); + let (account_info, address_was_cold) = self.access_account(address); - self.increase_consumed_gas(current_call_frame, gas_cost::balance(address_is_cold)?)?; + self.increase_consumed_gas(current_call_frame, gas_cost::balance(address_was_cold)?)?; current_call_frame.stack.push(account_info.balance)?; @@ -267,9 +267,9 @@ impl VM { ) -> Result { let address = word_to_address(current_call_frame.stack.pop()?); - let (account_info, address_is_cold) = self.access_account(address); + let (account_info, address_was_cold) = self.access_account(address); - self.increase_consumed_gas(current_call_frame, gas_cost::extcodesize(address_is_cold)?)?; + self.increase_consumed_gas(current_call_frame, gas_cost::extcodesize(address_was_cold)?)?; current_call_frame .stack @@ -300,7 +300,7 @@ impl VM { .try_into() .map_err(|_| VMError::VeryLargeNumber)?; - let (account_info, address_is_cold) = self.access_account(address); + let (account_info, address_was_cold) = self.access_account(address); let new_memory_size = dest_offset.checked_add(size).ok_or(VMError::Internal( InternalError::ArithmeticOperationOverflow, @@ -312,7 +312,7 @@ impl VM { gas_cost::extcodecopy( new_memory_size.into(), current_memory_size.into(), - address_is_cold, + address_was_cold, )?, )?; @@ -420,9 +420,9 @@ impl VM { ) -> Result { let address = word_to_address(current_call_frame.stack.pop()?); - let (account_info, address_is_cold) = self.access_account(address); + let (account_info, address_was_cold) = self.access_account(address); - self.increase_consumed_gas(current_call_frame, gas_cost::extcodehash(address_is_cold)?)?; + self.increase_consumed_gas(current_call_frame, gas_cost::extcodehash(address_was_cold)?)?; current_call_frame.stack.push(U256::from_big_endian( keccak(account_info.bytecode).as_fixed_bytes(), diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 6a7e51e73..5909c1176 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -56,14 +56,14 @@ impl VM { let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); - let (account_info, address_is_cold) = self.access_account(callee); + let (account_info, address_was_cold) = self.access_account(callee); self.increase_consumed_gas( current_call_frame, gas_cost::call( new_memory_size.into(), current_memory_size.into(), - address_is_cold, + address_was_cold, account_info.is_empty(), value_to_transfer, )?, @@ -130,14 +130,14 @@ impl VM { let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); - let (_account_info, address_is_cold) = self.access_account(code_address); + let (_account_info, address_was_cold) = self.access_account(code_address); self.increase_consumed_gas( current_call_frame, gas_cost::callcode( new_memory_size.into(), current_memory_size.into(), - address_is_cold, + address_was_cold, value_to_transfer, )?, )?; @@ -231,7 +231,7 @@ impl VM { let to = current_call_frame.to; let is_static = current_call_frame.is_static; - let (_account_info, address_is_cold) = self.access_account(code_address); + let (_account_info, address_was_cold) = self.access_account(code_address); let new_memory_size_for_args = (args_start_offset .checked_add(args_size) @@ -249,7 +249,7 @@ impl VM { gas_cost::delegatecall( new_memory_size.into(), current_memory_size.into(), - address_is_cold, + address_was_cold, )?, )?; @@ -298,7 +298,7 @@ impl VM { .try_into() .map_err(|_err| VMError::VeryLargeNumber)?; - let (_account_info, address_is_cold) = self.access_account(code_address); + let (_account_info, address_was_cold) = self.access_account(code_address); let new_memory_size_for_args = (args_start_offset .checked_add(args_size) @@ -316,7 +316,7 @@ impl VM { gas_cost::staticcall( new_memory_size.into(), current_memory_size.into(), - address_is_cold, + address_was_cold, )?, )?; @@ -461,44 +461,27 @@ impl VM { return Err(VMError::OpcodeNotAllowedInStaticContext); } - // 1. Pop the target address from the stack let target_address = word_to_address(current_call_frame.stack.pop()?); - // 2. Get current account and: Store the balance in a variable, set it's balance to 0 - let mut current_account = self.get_account(¤t_call_frame.to); - let current_account_balance = current_account.info.balance; - - current_account.info.balance = U256::zero(); + let (target_account_info, target_account_is_cold) = self.access_account(target_address); - let is_cached = self.cache.is_account_cached(&target_address); - - // 3 & 4. Get target account and add the balance of the current account to it - let mut target_account = self.get_account(&target_address); - let account_is_empty = target_account.is_empty(); + self.increase_consumed_gas( + current_call_frame, + gas_cost::selfdestruct(target_account_is_cold, target_account_info.is_empty()) + .map_err(VMError::OutOfGas)?, + )?; - let gas_cost = - gas_cost::selfdestruct(is_cached, account_is_empty).map_err(VMError::OutOfGas)?; + let (current_account_info, _current_account_is_cold) = + self.access_account(current_call_frame.to); - target_account.info.balance = target_account - .info - .balance - .checked_add(current_account_balance) - .ok_or(VMError::BalanceOverflow)?; + self.increase_account_balance(target_address, current_account_info.balance)?; + self.decrease_account_balance(current_call_frame.to, current_account_info.balance)?; - // 5. Register account to be destroyed in accrued substate IF executed in the same transaction a contract was created if self.tx_kind == TxKind::Create { self.accrued_substate .selfdestrutct_set .insert(current_call_frame.to); } - // Accounts in SelfDestruct set should be destroyed at the end of the transaction. - - // Update cache after modifying accounts. - self.cache - .add_account(¤t_call_frame.to, ¤t_account); - self.cache.add_account(&target_address, &target_account); - - self.increase_consumed_gas(current_call_frame, gas_cost)?; Ok(OpcodeSuccess::Result(ResultReason::SelfDestruct)) } From 6ad91e589c8824e95620b45d754dfad34c6a6918 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Tue, 26 Nov 2024 17:25:25 -0300 Subject: [PATCH 36/49] Encapsulate account modifications --- crates/vm/levm/src/vm.rs | 380 ++++++++++++++++++++++++--------------- 1 file changed, 234 insertions(+), 146 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index 322ca18c6..cf601babd 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -2,7 +2,7 @@ use crate::{ account::{Account, StorageSlot}, call_frame::CallFrame, constants::*, - db::{Cache, Database}, + db::{cache, CacheDB, Database}, environment::Environment, errors::{ InternalError, OpcodeSuccess, OutOfGasError, ResultReason, TransactionReport, TxResult, @@ -10,6 +10,7 @@ use crate::{ }, gas_cost, opcodes::Opcode, + AccountInfo, }; use bytes::Bytes; use ethrex_core::{types::TxKind, Address, H256, U256}; @@ -42,8 +43,11 @@ pub struct VM { /// Mapping between addresses (160-bit identifiers) and account /// states. pub db: Arc, - pub cache: Cache, + pub cache: CacheDB, pub tx_kind: TxKind, + + pub touched_accounts: HashSet
, + pub touched_storage_slots: HashSet, } pub fn address_to_word(address: Address) -> U256 { @@ -72,22 +76,34 @@ impl VM { value: U256, calldata: Bytes, db: Arc, - mut cache: Cache, + mut cache: CacheDB, ) -> Result { // Maybe this decision should be made in an upper layer // Add sender, coinbase and recipient (in the case of a Call) to cache [https://www.evm.codes/about#access_list] let sender_account_info = db.get_account_info(env.origin); - cache.add_account(&env.origin, &Account::from(sender_account_info.clone())); + cache::insert_account( + &mut cache, + env.origin, + Account::from(sender_account_info.clone()), + ); let coinbase_account_info = db.get_account_info(env.coinbase); - cache.add_account(&env.coinbase, &Account::from(coinbase_account_info)); + cache::insert_account( + &mut cache, + env.coinbase, + Account::from(coinbase_account_info), + ); match to { TxKind::Call(address_to) => { // add address_to to cache let recipient_account_info = db.get_account_info(address_to); - cache.add_account(&address_to, &Account::from(recipient_account_info.clone())); + cache::insert_account( + &mut cache, + address_to, + Account::from(recipient_account_info.clone()), + ); // CALL tx let initial_call_frame = CallFrame::new( @@ -110,6 +126,9 @@ impl VM { accrued_substate: Substate::default(), cache, tx_kind: to, + // FIXME + touched_accounts: HashSet::new(), + touched_storage_slots: HashSet::new(), }) } TxKind::Create => { @@ -123,7 +142,7 @@ impl VM { // (3) let created_contract = Account::new(value, calldata.clone(), 1, HashMap::new()); - cache.add_account(&new_contract_address, &created_contract); + cache::insert_account(&mut cache, new_contract_address, created_contract); // (5) let code: Bytes = calldata.clone(); @@ -148,6 +167,9 @@ impl VM { accrued_substate: Substate::default(), cache, tx_kind: TxKind::Create, + // FIXME + touched_accounts: HashSet::new(), + touched_storage_slots: HashSet::new(), }) } } @@ -168,7 +190,7 @@ impl VM { Err(e) => { return TransactionReport { result: TxResult::Revert(e), - new_state: self.cache.accounts.clone(), + new_state: self.cache.clone(), gas_used: current_call_frame.gas_used.low_u64(), gas_refunded: self.env.refunded_gas.low_u64(), output: current_call_frame.returndata.clone(), // Bytes::new() if error is not RevertOpcode @@ -289,7 +311,7 @@ impl VM { self.call_frames.push(current_call_frame.clone()); return TransactionReport { result: TxResult::Success, - new_state: self.cache.accounts.clone(), + new_state: self.cache.clone(), gas_used: current_call_frame.gas_used.low_u64(), gas_refunded: self.env.refunded_gas.low_u64(), output: current_call_frame.returndata.clone(), @@ -314,7 +336,7 @@ impl VM { return TransactionReport { result: TxResult::Revert(error), - new_state: self.cache.accounts.clone(), + new_state: self.cache.clone(), gas_used: current_call_frame.gas_used.low_u64(), gas_refunded: self.env.refunded_gas.low_u64(), output: current_call_frame.returndata.clone(), // Bytes::new() if error is not RevertOpcode @@ -328,7 +350,7 @@ impl VM { fn restore_state( &mut self, - backup_cache: Cache, + backup_cache: CacheDB, backup_substate: Substate, backup_refunded_gas: U256, ) { @@ -374,7 +396,7 @@ impl VM { } } - let (sender_account_info, _address_is_cold) = self.access_account(self.env.origin); + let (sender_account_info, _address_was_cold) = self.access_account(self.env.origin); self.increment_account_nonce(self.env.origin)?; @@ -409,18 +431,10 @@ impl VM { ))? .clone(); - let sender = call_frame.msg_sender; - let mut sender_account = self.get_account(&sender); - - sender_account.info.nonce = sender_account - .info - .nonce - .checked_sub(1) - .ok_or(VMError::Internal(InternalError::NonceUnderflowed))?; + self.decrement_account_nonce(call_frame.msg_sender)?; let new_contract_address = call_frame.to; - - if self.cache.accounts.remove(&new_contract_address).is_none() { + if cache::remove_account(&mut self.cache, &new_contract_address).is_none() { return Err(VMError::AddressDoesNotMatchAnAccount); // Should not be this error } @@ -454,14 +468,6 @@ impl VM { let sender = initial_call_frame.msg_sender; - let initial_call_frame = self - .call_frames - .last() - .ok_or(VMError::Internal( - InternalError::CouldNotAccessLastCallframe, - ))? - .clone(); - let calldata_cost = gas_cost::tx_calldata(&initial_call_frame.calldata).map_err(VMError::OutOfGas)?; @@ -518,49 +524,26 @@ impl VM { let contract_address = initial_call_frame.to; - let mut created_contract = self.get_account(&contract_address); - - created_contract.info.bytecode = contract_code; - - self.cache.add_account(&contract_address, &created_contract); + self.update_account_bytecode(contract_address, contract_code)?; } - let mut sender_account = self.get_account(&sender); let coinbase_address = self.env.coinbase; - sender_account.info.balance = sender_account - .info - .balance - .checked_sub( - U256::from(report.gas_used) - .checked_mul(self.env.gas_price) - .ok_or(VMError::GasLimitPriceProductOverflow)?, - ) - .ok_or(VMError::BalanceUnderflow)?; + self.decrease_account_balance( + sender, + U256::from(report.gas_used) + .checked_mul(self.env.gas_price) + .ok_or(VMError::GasLimitPriceProductOverflow)?, + )?; let receiver_address = initial_call_frame.to; - let mut receiver_account = self.get_account(&receiver_address); // If execution was successful we want to transfer value from sender to receiver if report.is_success() { // Subtract to the caller the gas sent - sender_account.info.balance = sender_account - .info - .balance - .checked_sub(initial_call_frame.msg_value) - .ok_or(VMError::BalanceUnderflow)?; - receiver_account.info.balance = receiver_account - .info - .balance - .checked_add(initial_call_frame.msg_value) - .ok_or(VMError::BalanceUnderflow)?; + self.decrease_account_balance(sender, initial_call_frame.msg_value)?; + self.increase_account_balance(receiver_address, initial_call_frame.msg_value)?; } - // Note: This is commented because it's used for debugging purposes in development. - // dbg!(&report.gas_refunded); - - self.cache.add_account(&sender, &sender_account); - self.cache.add_account(&receiver_address, &receiver_account); - // Send coinbase fee let priority_fee_per_gas = self .env @@ -571,16 +554,9 @@ impl VM { .checked_mul(priority_fee_per_gas) .ok_or(VMError::BalanceOverflow)?; - let mut coinbase_account = self.get_account(&coinbase_address); - coinbase_account.info.balance = coinbase_account - .info - .balance - .checked_add(coinbase_fee) - .ok_or(VMError::BalanceOverflow)?; - - self.cache.add_account(&coinbase_address, &coinbase_account); + self.increase_account_balance(coinbase_address, coinbase_fee)?; - report.new_state.clone_from(&self.cache.accounts); + report.new_state.clone_from(&self.cache); Ok(report) } @@ -608,30 +584,20 @@ impl VM { ret_offset: usize, ret_size: usize, ) -> Result { - let mut sender_account = self.get_account(¤t_call_frame.msg_sender); + let (sender_account_info, _address_was_cold) = + self.access_account(current_call_frame.msg_sender); - if sender_account.info.balance < value { + if sender_account_info.balance < value { current_call_frame.stack.push(U256::from(REVERT_FOR_CALL))?; return Ok(OpcodeSuccess::Continue); } - let mut recipient_account = self.get_account(&to); - - // transfer value - sender_account.info.balance = sender_account - .info - .balance - .checked_sub(value) - .ok_or(VMError::BalanceUnderflow)?; - recipient_account.info.balance = recipient_account - .info - .balance - .checked_add(value) - .ok_or(VMError::BalanceOverflow)?; + self.decrease_account_balance(current_call_frame.msg_sender, value)?; + self.increase_account_balance(to, value)?; - let code_address_bytecode = self.get_account(&code_address).info.bytecode; + let (code_account_info, _address_was_cold) = self.access_account(code_address); - if code_address_bytecode.is_empty() { + if code_account_info.bytecode.is_empty() { current_call_frame .stack .push(U256::from(SUCCESS_FOR_CALL))?; @@ -666,7 +632,7 @@ impl VM { msg_sender, to, code_address, - code_address_bytecode, + code_account_info.bytecode, value, calldata, is_static, @@ -685,12 +651,6 @@ impl VM { current_call_frame.sub_return_data_offset = ret_offset; current_call_frame.sub_return_data_size = ret_size; - // Update sender account and recipient in cache - self.cache - .add_account(¤t_call_frame.msg_sender, &sender_account); - self.cache.add_account(&to, &recipient_account); - - // self.call_frames.push(new_call_frame.clone()); let tx_report = self.execute(&mut new_call_frame); // Add gas used by the sub-context to the current one after it's execution. @@ -799,29 +759,25 @@ impl VM { return Ok(OpcodeSuccess::Result(ResultReason::Revert)); } - if !self.cache.is_account_cached(¤t_call_frame.msg_sender) { - self.cache_from_db(¤t_call_frame.msg_sender); - }; + let (sender_account_info, _sender_address_was_cold) = + self.access_account(current_call_frame.msg_sender); - let sender_account = self - .cache - .get_mut_account(current_call_frame.msg_sender) - .ok_or(VMError::Internal(InternalError::AccountNotFound))?; - - if sender_account.info.balance < value_in_wei_to_send { + if sender_account_info.balance < value_in_wei_to_send { current_call_frame .stack .push(U256::from(REVERT_FOR_CREATE))?; return Ok(OpcodeSuccess::Result(ResultReason::Revert)); } - let Some(new_nonce) = sender_account.info.nonce.checked_add(1) else { - current_call_frame - .stack - .push(U256::from(REVERT_FOR_CREATE))?; - return Ok(OpcodeSuccess::Result(ResultReason::Revert)); + let new_nonce = match self.increment_account_nonce(current_call_frame.msg_sender) { + Ok(nonce) => nonce, + Err(_) => { + current_call_frame + .stack + .push(U256::from(REVERT_FOR_CREATE))?; + return Ok(OpcodeSuccess::Result(ResultReason::Revert)); + } }; - sender_account.info.nonce = new_nonce; let code_offset_in_memory = code_offset_in_memory .try_into() @@ -835,13 +791,11 @@ impl VM { let new_address = match salt { Some(salt) => Self::calculate_create2_address(current_call_frame.to, &code, salt)?, - None => Self::calculate_create_address( - current_call_frame.msg_sender, - sender_account.info.nonce, - )?, + None => Self::calculate_create_address(current_call_frame.msg_sender, new_nonce)?, }; - if self.cache.accounts.contains_key(&new_address) { + // FIXME: Shouldn't we check against the db? + if cache::is_account_cached(&self.cache, &new_address) { current_call_frame .stack .push(U256::from(REVERT_FOR_CREATE))?; @@ -849,7 +803,7 @@ impl VM { } let new_account = Account::new(U256::zero(), code.clone(), 0, Default::default()); - self.cache.add_account(&new_address, &new_account); + cache::insert_account(&mut self.cache, new_address, new_account); current_call_frame .stack @@ -857,7 +811,7 @@ impl VM { self.generic_call( current_call_frame, - U256::MAX, + U256::MAX, // FIXME: Why we send U256::MAX here? value_in_wei_to_send, current_call_frame.msg_sender, new_address, @@ -888,7 +842,7 @@ impl VM { let potential_consumed_gas = current_call_frame .gas_used .checked_add(gas) - .ok_or(VMError::OutOfGas(OutOfGasError::ConsumedGasOverflow))?; + .ok_or(OutOfGasError::ConsumedGasOverflow)?; if potential_consumed_gas > current_call_frame.gas_limit { return Err(VMError::OutOfGas(OutOfGasError::MaxGasLimitExceeded)); } @@ -898,53 +852,187 @@ impl VM { .env .consumed_gas .checked_add(gas) - .ok_or(VMError::OutOfGas(OutOfGasError::ConsumedGasOverflow))?; + .ok_or(OutOfGasError::ConsumedGasOverflow)?; Ok(()) } - pub fn cache_from_db(&mut self, address: &Address) { - let acc_info = self.db.get_account_info(*address); - self.cache.add_account( + pub fn cache_from_db(&mut self, address: Address) { + let acc_info = self.db.get_account_info(address); + cache::insert_account( + &mut self.cache, address, - &Account { + Account { info: acc_info.clone(), storage: HashMap::new(), }, ); } - /// Gets account, first checking the cache and then the database (caching in the second case) - pub fn get_account(&mut self, address: &Address) -> Account { - match self.cache.get_account(*address) { - Some(acc) => acc.clone(), + /// Accesses to an account's information. + /// + /// Accessed accounts are stored in the `touched_accounts` set. + /// Accessed accounts take place in some gas cost computation. + #[must_use] + pub fn access_account(&mut self, address: Address) -> (AccountInfo, bool) { + let address_was_cold = self.touched_accounts.insert(address); + let account = match cache::get_account(&self.cache, &address) { + Some(account) => account.info.clone(), + None => self.db.get_account_info(address), + }; + (account, address_was_cold) + } + + /// Accesses to an account's storage slot. + /// + /// Accessed storage slots are stored in the `touched_storage_slots` set. + /// Accessed storage slots take place in some gas cost computation. + #[must_use] + pub fn access_storage_slot(&mut self, address: Address, key: H256) -> (StorageSlot, bool) { + let storage_slot_was_cold = self.touched_storage_slots.insert(key); + let storage_slot = match cache::get_account(&self.cache, &address) { + Some(account) => match account.storage.get(&key) { + Some(storage_slot) => storage_slot.clone(), + None => { + let value = self.db.get_storage_slot(address, key); + StorageSlot { + original_value: value, + current_value: value, + } + } + }, None => { - let acc_info = self.db.get_account_info(*address); - let acc = Account { - info: acc_info, - storage: HashMap::new(), - }; - self.cache.add_account(address, &acc); - acc + let value = self.db.get_storage_slot(address, key); + StorageSlot { + original_value: value, + current_value: value, + } } + }; + (storage_slot, storage_slot_was_cold) + } + + pub fn increase_account_balance( + &mut self, + address: Address, + increase: U256, + ) -> Result<(), VMError> { + let account = self.get_account_mut(address)?; + account.info.balance = account + .info + .balance + .checked_add(increase) + .ok_or(VMError::BalanceOverflow)?; + Ok(()) + } + + pub fn decrease_account_balance( + &mut self, + address: Address, + decrease: U256, + ) -> Result<(), VMError> { + let account = self.get_account_mut(address)?; + account.info.balance = account + .info + .balance + .checked_sub(decrease) + .ok_or(VMError::BalanceUnderflow)?; + Ok(()) + } + + pub fn increment_account_nonce(&mut self, address: Address) -> Result { + let account = self.get_account_mut(address)?; + account.info.nonce = account + .info + .nonce + .checked_add(1) + .ok_or(VMError::NonceOverflow)?; + Ok(account.info.nonce) + } + + pub fn decrement_account_nonce(&mut self, address: Address) -> Result<(), VMError> { + let account = self.get_account_mut(address)?; + account.info.nonce = account + .info + .nonce + .checked_sub(1) + .ok_or(VMError::NonceUnderflow)?; + Ok(()) + } + + pub fn update_account_bytecode( + &mut self, + address: Address, + new_bytecode: Bytes, + ) -> Result<(), VMError> { + let account = self.get_account_mut(address)?; + account.info.bytecode = new_bytecode; + Ok(()) + } + + pub fn update_account_storage( + &mut self, + address: Address, + key: H256, + new_value: U256, + ) -> Result<(), VMError> { + let account = self.get_account_mut(address)?; + let account_original_storage_slot_value = account + .storage + .get(&key) + .map_or(U256::zero(), |slot| slot.original_value); + let slot = account.storage.entry(key).or_insert(StorageSlot { + original_value: account_original_storage_slot_value, + current_value: new_value, + }); + slot.current_value = new_value; + Ok(()) + } + + pub fn get_account_mut(&mut self, address: Address) -> Result<&mut Account, VMError> { + if !cache::is_account_cached(&self.cache, &address) { + let account_info = self.db.get_account_info(address); + let account = Account { + info: account_info, + storage: HashMap::new(), + }; + cache::insert_account(&mut self.cache, address, account.clone()); } + cache::get_account_mut(&mut self.cache, &address) + .ok_or(VMError::Internal(InternalError::AccountNotFound)) } - /// Gets storage slot, first checking the cache and then the database (caching in the second case) - pub fn get_storage_slot(&mut self, address: &Address, key: H256) -> StorageSlot { - match self.cache.get_storage_slot(*address, key) { - Some(slot) => slot, + /// Gets account, first checking the cache and then the database (caching in the second case) + pub fn get_account(&mut self, address: Address) -> Account { + match cache::get_account(&self.cache, &address) { + Some(acc) => acc.clone(), None => { - let value = self.db.get_storage_slot(*address, key); - let slot = StorageSlot { - original_value: value, - current_value: value, + let account_info = self.db.get_account_info(address); + let account = Account { + info: account_info, + storage: HashMap::new(), }; - let mut acc = self.get_account(address); - acc.storage.insert(key, slot.clone()); - self.cache.add_account(address, &acc); - slot + cache::insert_account(&mut self.cache, address, account.clone()); + account } } } + + // /// Gets storage slot, first checking the cache and then the database (caching in the second case) + // pub fn get_storage_slot(&mut self, address: Address, key: H256) -> StorageSlot { + // match cache::get_storage_slot(&self.cache, &address, &key) { + // Some(slot) => slot.clone(), + // None => { + // let value = self.db.get_storage_slot(address, key); + // let slot = StorageSlot { + // original_value: value, + // current_value: value, + // }; + // let mut account = self.get_account(address); + // account.storage.insert(key, slot.clone()); + // cache::insert_account(&mut self.cache, address, account); + // slot + // } + // } + // } } From 02517f953f986c774d9a091624a8d9b98c77aade Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 12:22:47 -0300 Subject: [PATCH 37/49] Fix compilation --- cmd/ef_tests/levm/runner/levm_runner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 208b6258b..e95273ffe 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -9,7 +9,7 @@ use ethrex_core::{ H256, U256, }; use ethrex_levm::{ - db::Cache, + db::CacheDB, errors::{TransactionReport, VMError}, vm::VM, Environment, @@ -94,7 +94,7 @@ pub fn prepare_vm_for_tx(vector: &TestVector, test: &EFTest) -> Result Date: Wed, 27 Nov 2024 12:22:56 -0300 Subject: [PATCH 38/49] Use `checked_next_multiple_of` --- crates/vm/levm/src/opcode_handlers/system.rs | 40 ++++++++++++++++---- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/crates/vm/levm/src/opcode_handlers/system.rs b/crates/vm/levm/src/opcode_handlers/system.rs index 5909c1176..ddfbd2251 100644 --- a/crates/vm/levm/src/opcode_handlers/system.rs +++ b/crates/vm/levm/src/opcode_handlers/system.rs @@ -48,11 +48,17 @@ impl VM { let new_memory_size_for_args = (args_start_offset .checked_add(args_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size_for_return_data = (return_data_start_offset .checked_add(return_data_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); @@ -122,11 +128,17 @@ impl VM { let new_memory_size_for_args = (args_start_offset .checked_add(args_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size_for_return_data = (return_data_start_offset .checked_add(return_data_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); @@ -236,11 +248,17 @@ impl VM { let new_memory_size_for_args = (args_start_offset .checked_add(args_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size_for_return_data = (return_data_start_offset .checked_add(return_data_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); @@ -303,11 +321,17 @@ impl VM { let new_memory_size_for_args = (args_start_offset .checked_add(args_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size_for_return_data = (return_data_start_offset .checked_add(return_data_size) .ok_or(InternalError::ArithmeticOperationOverflow)?) - .next_multiple_of(WORD_SIZE_IN_BYTES_USIZE); + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))?; let new_memory_size = new_memory_size_for_args.max(new_memory_size_for_return_data); let current_memory_size = current_call_frame.memory.data.len(); From 1073d38190aa603af9b3a025f52d60ec0bedf76a Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 13:49:59 -0300 Subject: [PATCH 39/49] Fix compilation --- cmd/ef_tests/levm/runner/levm_runner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/ef_tests/levm/runner/levm_runner.rs b/cmd/ef_tests/levm/runner/levm_runner.rs index 4cf9ee66b..953302a68 100644 --- a/cmd/ef_tests/levm/runner/levm_runner.rs +++ b/cmd/ef_tests/levm/runner/levm_runner.rs @@ -9,7 +9,7 @@ use ethrex_core::{ H256, U256, }; use ethrex_levm::{ - db::Cache, + db::CacheDB, errors::{TransactionReport, VMError}, vm::VM, Environment, @@ -98,7 +98,7 @@ pub fn prepare_vm_for_tx(vector: &TestVector, test: &EFTest) -> Result Date: Wed, 27 Nov 2024 14:54:33 -0300 Subject: [PATCH 40/49] Fix extcodecopy --- crates/vm/levm/src/opcode_handlers/environment.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index 1530b10b0..82975541c 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -1,5 +1,6 @@ use crate::{ call_frame::CallFrame, + constants::WORD_SIZE_IN_BYTES_USIZE, errors::{InternalError, OpcodeSuccess, OutOfGasError, VMError}, gas_cost, vm::{word_to_address, VM}, @@ -302,10 +303,12 @@ impl VM { let (account_info, address_was_cold) = self.access_account(address); - let new_memory_size = (((!size).checked_add(1).ok_or(VMError::Internal( - InternalError::ArithmeticOperationOverflow, - ))?) & 31) + let new_memory_size = dest_offset .checked_add(size) + .ok_or(VMError::Internal( + InternalError::ArithmeticOperationOverflow, + ))? + .checked_next_multiple_of(WORD_SIZE_IN_BYTES_USIZE) .ok_or(VMError::Internal( InternalError::ArithmeticOperationOverflow, ))?; @@ -354,6 +357,8 @@ impl VM { } } + dbg!("Post memory copy"); + Ok(OpcodeSuccess::Continue) } From fd23ff2051a71cc83bcee86b5c6669775ddb2fd1 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 15:29:52 -0300 Subject: [PATCH 41/49] remove leftover dbg --- crates/vm/levm/src/opcode_handlers/environment.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/crates/vm/levm/src/opcode_handlers/environment.rs b/crates/vm/levm/src/opcode_handlers/environment.rs index 3cdb14aaa..e558a036c 100644 --- a/crates/vm/levm/src/opcode_handlers/environment.rs +++ b/crates/vm/levm/src/opcode_handlers/environment.rs @@ -361,8 +361,6 @@ impl VM { } } - dbg!("Post memory copy"); - Ok(OpcodeSuccess::Continue) } From 9cf371e4e7666b005a82f164a59425686269c2fc Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 15:45:17 -0300 Subject: [PATCH 42/49] Add todo --- crates/vm/levm/src/vm.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index a55cc6240..ba9afb4f7 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -556,6 +556,10 @@ impl VM { self.increase_account_balance(coinbase_address, coinbase_fee)?; + // TODO: Filter non-updated accounts. An account could have been updated + // twice, but there's no diff between the previous and the current state + // is the same. + // https://github.com/lambdaclass/ethrex/issues/1316. report.new_state.clone_from(&self.cache); Ok(report) From fc469b8cf2323f9f50476dfe2e50c148016800da Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 15:51:33 -0300 Subject: [PATCH 43/49] Remove comment --- crates/vm/levm/src/vm.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index ba9afb4f7..a55cc6240 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -556,10 +556,6 @@ impl VM { self.increase_account_balance(coinbase_address, coinbase_fee)?; - // TODO: Filter non-updated accounts. An account could have been updated - // twice, but there's no diff between the previous and the current state - // is the same. - // https://github.com/lambdaclass/ethrex/issues/1316. report.new_state.clone_from(&self.cache); Ok(report) From 729e035a913b1679ea46a587dc6c5dd587271e7b Mon Sep 17 00:00:00 2001 From: Ivan Litteri <67517699+ilitteri@users.noreply.github.com> Date: Wed, 27 Nov 2024 17:41:15 -0300 Subject: [PATCH 44/49] Update crates/vm/levm/src/vm.rs --- crates/vm/levm/src/vm.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index a55cc6240..dddbbee4b 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -126,7 +126,6 @@ impl VM { accrued_substate: Substate::default(), cache, tx_kind: to, - // FIXME touched_accounts: HashSet::new(), touched_storage_slots: HashSet::new(), }) From a3382c752b957e6b3b0ff44f6d70c682b513f544 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 17:52:24 -0300 Subject: [PATCH 45/49] Fix `touched_storage_slots` --- crates/vm/levm/src/vm.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index dddbbee4b..6bd5277a2 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -47,7 +47,7 @@ pub struct VM { pub tx_kind: TxKind, pub touched_accounts: HashSet
, - pub touched_storage_slots: HashSet, + pub touched_storage_slots: HashMap>, } pub fn address_to_word(address: Address) -> U256 { @@ -127,7 +127,7 @@ impl VM { cache, tx_kind: to, touched_accounts: HashSet::new(), - touched_storage_slots: HashSet::new(), + touched_storage_slots: HashMap::new(), }) } TxKind::Create => { @@ -168,7 +168,7 @@ impl VM { tx_kind: TxKind::Create, // FIXME touched_accounts: HashSet::new(), - touched_storage_slots: HashSet::new(), + touched_storage_slots: HashMap::new(), }) } } @@ -888,7 +888,7 @@ impl VM { /// Accessed storage slots take place in some gas cost computation. #[must_use] pub fn access_storage_slot(&mut self, address: Address, key: H256) -> (StorageSlot, bool) { - let storage_slot_was_cold = self.touched_storage_slots.insert(key); + let storage_slot_was_cold = self.touched_storage_slots.entry(address).or_default().insert(key); let storage_slot = match cache::get_account(&self.cache, &address) { Some(account) => match account.storage.get(&key) { Some(storage_slot) => storage_slot.clone(), From f580d6c60c0a251155c26b757afab13f6ad49021 Mon Sep 17 00:00:00 2001 From: ilitteri Date: Wed, 27 Nov 2024 17:58:29 -0300 Subject: [PATCH 46/49] Caller, callee, and coinbase are warm by default --- crates/vm/levm/src/vm.rs | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index 6bd5277a2..42ab4355a 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -81,22 +81,13 @@ impl VM { // Maybe this decision should be made in an upper layer // Add sender, coinbase and recipient (in the case of a Call) to cache [https://www.evm.codes/about#access_list] - let sender_account_info = db.get_account_info(env.origin); - cache::insert_account( - &mut cache, - env.origin, - Account::from(sender_account_info.clone()), - ); - - let coinbase_account_info = db.get_account_info(env.coinbase); - cache::insert_account( - &mut cache, - env.coinbase, - Account::from(coinbase_account_info), - ); + let mut default_touched_accounts = + HashSet::from_iter([env.origin, env.coinbase].iter().cloned()); match to { TxKind::Call(address_to) => { + default_touched_accounts.insert(address_to); + // add address_to to cache let recipient_account_info = db.get_account_info(address_to); cache::insert_account( @@ -135,9 +126,12 @@ impl VM { // (2) let new_contract_address = - VM::calculate_create_address(env.origin, sender_account_info.nonce).map_err( - |_| VMError::Internal(InternalError::CouldNotComputeCreateAddress), - )?; // TODO: Remove after merging the PR that removes unwraps. + VM::calculate_create_address(env.origin, db.get_account_info(env.origin).nonce) + .map_err(|_| { + VMError::Internal(InternalError::CouldNotComputeCreateAddress) + })?; + + default_touched_accounts.insert(new_contract_address); // (3) let created_contract = Account::new(value, calldata.clone(), 1, HashMap::new()); @@ -166,8 +160,7 @@ impl VM { accrued_substate: Substate::default(), cache, tx_kind: TxKind::Create, - // FIXME - touched_accounts: HashSet::new(), + touched_accounts: default_touched_accounts, touched_storage_slots: HashMap::new(), }) } @@ -888,7 +881,11 @@ impl VM { /// Accessed storage slots take place in some gas cost computation. #[must_use] pub fn access_storage_slot(&mut self, address: Address, key: H256) -> (StorageSlot, bool) { - let storage_slot_was_cold = self.touched_storage_slots.entry(address).or_default().insert(key); + let storage_slot_was_cold = self + .touched_storage_slots + .entry(address) + .or_default() + .insert(key); let storage_slot = match cache::get_account(&self.cache, &address) { Some(account) => match account.storage.get(&key) { Some(storage_slot) => storage_slot.clone(), From c83a4964132f911d081fc4a5ab8bbf71d49480ff Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 28 Nov 2024 13:16:38 -0300 Subject: [PATCH 47/49] Remove commented code --- crates/vm/levm/src/vm.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/crates/vm/levm/src/vm.rs b/crates/vm/levm/src/vm.rs index 42ab4355a..0bc2b752e 100644 --- a/crates/vm/levm/src/vm.rs +++ b/crates/vm/levm/src/vm.rs @@ -1013,22 +1013,4 @@ impl VM { } } } - - // /// Gets storage slot, first checking the cache and then the database (caching in the second case) - // pub fn get_storage_slot(&mut self, address: Address, key: H256) -> StorageSlot { - // match cache::get_storage_slot(&self.cache, &address, &key) { - // Some(slot) => slot.clone(), - // None => { - // let value = self.db.get_storage_slot(address, key); - // let slot = StorageSlot { - // original_value: value, - // current_value: value, - // }; - // let mut account = self.get_account(address); - // account.storage.insert(key, slot.clone()); - // cache::insert_account(&mut self.cache, address, account); - // slot - // } - // } - // } } From 0835db6f1c6bca77cc429ad599e584a2094a4e5b Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 28 Nov 2024 13:19:57 -0300 Subject: [PATCH 48/49] Add constants for SSTORE --- crates/vm/levm/src/gas_cost.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index b681e2143..aecb038e4 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -96,6 +96,9 @@ pub const SLOAD_COLD_DYNAMIC: U256 = U256([2100, 0, 0, 0]); pub const SLOAD_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); pub const SSTORE_STATIC: U256 = U256::zero(); +pub const SSTORE_DEFAULT_DYNAMIC: U256 = U256([100, 0, 0, 0]); +pub const SSTORE_STORAGE_CREATION: U256 = U256([20000, 0, 0, 0]); +pub const SSTORE_STORAGE_MODIFICATION: U256 = U256([5000, 0, 0, 0]); pub const BALANCE_STATIC: U256 = DEFAULT_STATIC; pub const BALANCE_COLD_DYNAMIC: U256 = DEFAULT_COLD_DYNAMIC; @@ -322,15 +325,15 @@ pub fn sstore( let static_gas = SSTORE_STATIC; let mut base_dynamic_gas = if new_value == storage_slot.current_value { - U256::from(100) + SSTORE_DEFAULT_DYNAMIC } else if storage_slot.current_value == storage_slot.original_value { if storage_slot.original_value.is_zero() { - U256::from(20000) + SSTORE_STORAGE_CREATION } else { - U256::from(5000) + SSTORE_STORAGE_MODIFICATION } } else { - U256::from(100) + SSTORE_DEFAULT_DYNAMIC }; if storage_slot_was_cold { From d78436a9feead6a263b05283a58cd34eb45f255b Mon Sep 17 00:00:00 2001 From: ilitteri Date: Thu, 28 Nov 2024 13:20:52 -0300 Subject: [PATCH 49/49] Leftover --- crates/vm/levm/src/gas_cost.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/vm/levm/src/gas_cost.rs b/crates/vm/levm/src/gas_cost.rs index aecb038e4..f73dcb1df 100644 --- a/crates/vm/levm/src/gas_cost.rs +++ b/crates/vm/levm/src/gas_cost.rs @@ -96,6 +96,7 @@ pub const SLOAD_COLD_DYNAMIC: U256 = U256([2100, 0, 0, 0]); pub const SLOAD_WARM_DYNAMIC: U256 = U256([100, 0, 0, 0]); pub const SSTORE_STATIC: U256 = U256::zero(); +pub const SSTORE_COLD_DYNAMIC: U256 = U256([2100, 0, 0, 0]); pub const SSTORE_DEFAULT_DYNAMIC: U256 = U256([100, 0, 0, 0]); pub const SSTORE_STORAGE_CREATION: U256 = U256([20000, 0, 0, 0]); pub const SSTORE_STORAGE_MODIFICATION: U256 = U256([5000, 0, 0, 0]); @@ -338,7 +339,7 @@ pub fn sstore( if storage_slot_was_cold { base_dynamic_gas = base_dynamic_gas - .checked_add(U256::from(2100)) + .checked_add(SSTORE_COLD_DYNAMIC) .ok_or(OutOfGasError::GasCostOverflow)?; }