diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs index 1444d312bd446..b288fcc152e69 100644 --- a/crates/telos/node/tests/state_bypass.rs +++ b/crates/telos/node/tests/state_bypass.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::fs; use std::path::PathBuf; use std::str::FromStr; @@ -10,8 +11,13 @@ use telos_consensus_client::execution_api_client::ExecutionApiMethod::{ForkChoic use tracing::info; use reth::args::RpcServerArgs; use reth::builder::NodeConfig; -use reth::primitives::{Address, B256, U256}; +use reth::primitives::{Address, B256, Bytecode, hex, JumpTable, U256}; use reth::primitives::hex::FromHex; +use reth::primitives::revm_primitives::{Bytecode as RevmBytecode, LegacyAnalyzedBytecode}; +use reth::revm; +use reth::revm::db::{CacheDB, EmptyDB, StorageWithOriginalValues, states::StorageSlot}; +use reth::revm::{Database, DatabaseCommit, DatabaseRef, Evm, State, TransitionAccount}; +use reth::revm::primitives::{AccountInfo, EvmStorageSlot}; use reth::rpc::types::engine::ForkchoiceState; use reth::rpc::types::ExecutionPayloadV1; use reth::tasks::TaskManager; @@ -19,9 +25,13 @@ use reth_chainspec::{ChainSpecBuilder, TEVMTESTNET}; use reth_e2e_test_utils::node::NodeTestContext; use reth_node_builder::NodeBuilder; use reth_node_telos::{TelosArgs, TelosNode}; +use reth_primitives::Bytes; use reth_primitives::constants::{EMPTY_ROOT_HASH, MIN_PROTOCOL_BASE_FEE}; +use reth_primitives::revm_primitives::AccountStatus; use reth_telos_rpc::TelosClient; -use reth_telos_rpc_engine_api::structs::{TelosAccountTableRow, TelosEngineAPIExtraFields}; +use reth_telos_rpc_engine_api::compare::compare_state_diffs; +use reth_telos_rpc_engine_api::structs::{TelosAccountTableRow, TelosAccountStateTableRow, TelosEngineAPIExtraFields}; +use revm::primitives::Account; fn init_reth() -> eyre::Result<(NodeConfig, String)> { let chain_spec = Arc::new( @@ -51,7 +61,7 @@ fn init_reth() -> eyre::Result<(NodeConfig, String)> { } #[tokio::test] -async fn test_state_bypass_balance_override() { +async fn test_integration_tevm_only() { tracing_subscriber::fmt::init(); let (node_config, jwt_secret) = init_reth().unwrap(); @@ -88,6 +98,8 @@ async fn test_state_bypass_balance_override() { println!("Starting Reth on RPC port {}!", rpc_port); let _ = NodeTestContext::new(node_handle.node.clone()).await.unwrap(); + let custom_balance = U256::from(80085); + let exec_client = ExecutionApiClient::new(&format!("http://127.0.0.1:{}", execution_port), &jwt_secret).unwrap(); let execution_payload = ExecutionPayloadV1 { @@ -114,9 +126,9 @@ async fn test_state_bypass_balance_override() { removed: false, address: test_addr, account: "eosio".to_string(), - nonce: 1, + nonce: 0, code: Default::default(), - balance: U256::try_from(80085).unwrap(), + balance: custom_balance, }]), statediffs_accountstate: Some(vec![]), revision_changes: None, @@ -155,5 +167,243 @@ async fn test_state_bypass_balance_override() { .on_http(Url::from_str(format!("http://localhost:{}", rpc_port).as_str()).unwrap()); let balance = provider.get_balance(test_addr).await.unwrap(); - info!("BALANCE: {:#?}", balance); -} \ No newline at end of file + info!("balance: {:#?}", balance); + + assert_eq!(balance, custom_balance); +} + +#[test] +fn test_db_both_sides_present_but_dif() { + let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); + + let init_balance = U256::from(0); + let custom_balance = U256::from(80085); + + let init_nonce = 0; + let custom_nonce = 69; + + let custom_code = Bytes::from(&hex!("ffff")); + let custom_bytecode = RevmBytecode::LegacyRaw(custom_code.clone()); + + let revm_acc_info = AccountInfo { + balance: init_balance, + nonce: init_nonce, + code_hash: Default::default(), + code: None, + }; + + let mut db = CacheDB::new(EmptyDB::new()); + db.insert_account_info(test_addr, revm_acc_info); + + let mut state = State::builder().with_database(db).build(); + + let mut evm = Evm::builder().with_db(&mut state).build(); + + let statediffs_account = vec![TelosAccountTableRow { + removed: false, + address: test_addr, + account: "eosio".to_string(), + nonce: custom_nonce, + code: custom_code.clone(), + balance: custom_balance, + }]; + + compare_state_diffs( + &mut evm, + HashMap::new(), + statediffs_account.clone(), + vec![], + vec![], + vec![] + ); + + let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); + assert_eq!(db_acc.nonce, statediffs_account[0].nonce); + assert_eq!(db_acc.balance, statediffs_account[0].balance); + assert_eq!(db_acc.code, Some(custom_bytecode)); +} + +#[test] +fn test_revm_state_both_sides_present_but_dif() { + let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); + + let revm_acc_info = AccountInfo { + balance: U256::from(80085), + nonce: 69, + code_hash: Default::default(), + code: None, + }; + + let mut revm_state_diffs = HashMap::new(); + + let mut transition_account = TransitionAccount::new_empty_eip161(HashMap::new()); + + transition_account.info = Some(revm_acc_info); + + revm_state_diffs.insert(test_addr, transition_account); + + let mut db = CacheDB::new(EmptyDB::new()); + + let mut state = State::builder().with_database(db).build(); + + let mut evm = Evm::builder().with_db(&mut state).build(); + + let statediffs_account = vec![TelosAccountTableRow { + removed: false, + address: test_addr, + account: "eosio".to_string(), + nonce: 1, + code: Default::default(), + balance: U256::from(80085), + }]; + + compare_state_diffs( + &mut evm, + revm_state_diffs, + statediffs_account.clone(), + vec![], + vec![], + vec![] + ); + + // let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); + // assert_eq!(db_acc.nonce, statediffs_account[0].nonce); + // assert_eq!(db_acc.balance, statediffs_account[0].balance); + // assert_eq!(db_acc.code, Some(custom_bytecode)); +} + +#[test] +fn test_tevm_only() { + let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); + + let mut db = CacheDB::new(EmptyDB::new()); + + let mut state = State::builder().with_database(db).build(); + + let mut evm = Evm::builder().with_db(&mut state).build(); + + let statediffs_account = vec![TelosAccountTableRow { + removed: false, + address: test_addr, + account: "eosio".to_string(), + nonce: 1, + code: Default::default(), + balance: U256::from(80085), + }]; + + compare_state_diffs( + &mut evm, + HashMap::new(), + statediffs_account.clone(), + vec![], + vec![], + vec![] + ); + + let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); + assert_eq!(db_acc.nonce, statediffs_account[0].nonce); + assert_eq!(db_acc.balance, statediffs_account[0].balance); +} + +// #[test] +// fn test_accstate_diff_from_storage() { +// let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); +// +// let revm_acc_info = AccountInfo { +// balance: U256::from(80085), +// nonce: 69, +// code_hash: Default::default(), +// code: None, +// }; +// +// let mut state = HashMap::new(); +// +// let mut storage = HashMap::new(); +// let key = U256::from(420); +// let value = U256::from(0); +// storage.insert(key, EvmStorageSlot { original_value: Default::default(), present_value: value, is_cold: false }); +// let custom_value = U256::from(80085); +// +// let revm_acc = Account { +// info: revm_acc_info.clone(), +// storage, +// status: AccountStatus::Touched | AccountStatus::LoadedAsNotExisting, +// }; +// state.insert(test_addr, revm_acc); +// +// // let mut transition_account = TransitionAccount::new_empty_eip161(storage); +// +// // transition_account.info = Some(revm_acc_info.clone()); +// +// // revm_state_diffs.insert(test_addr, transition_account); +// +// let mut db = CacheDB::new(EmptyDB::new()); +// +// // db.insert_account_info(test_addr, revm_acc_info); +// db.commit(state); +// +// let mut state = State::builder().with_database(db).build(); +// +// let mut evm = Evm::builder().with_db(&mut state).build(); +// +// let statediffs_accountstate = vec![TelosAccountStateTableRow { +// removed: false, +// address: test_addr, +// key, +// value: custom_value +// }]; +// +// compare_state_diffs( +// &mut evm, +// HashMap::new(), +// vec![], +// statediffs_accountstate.clone(), +// vec![], +// vec![] +// ); +// +// // let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); +// // assert_eq!(db_acc.nonce, statediffs_account[0].nonce); +// // assert_eq!(db_acc.balance, statediffs_account[0].balance); +// // assert_eq!(db_acc.code, Some(custom_bytecode)); +// } + +// #[test] +// fn test_acc_is_none_on_revm_db() { +// let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); +// +// let mut revm_state_diffs = HashMap::new(); +// +// // let transition_account = TransitionAccount::new_empty_eip161(HashMap::new()); +// // revm_state_diffs.insert(test_addr, transition_account); +// +// let db = CacheDB::new(EmptyDB::new()); +// let refm = db.basic_ref(test_addr).unwrap(); +// +// let mut state = State::builder().with_database(db).build(); +// +// let mut evm = Evm::builder().with_db(&mut state).build(); +// +// let statediffs_account = vec![TelosAccountTableRow { +// removed: false, +// address: test_addr, +// account: "eosio".to_string(), +// nonce: 1, +// code: Default::default(), +// balance: U256::from(80085), +// }]; +// +// compare_state_diffs( +// &mut evm, +// revm_state_diffs, +// statediffs_account.clone(), +// vec![], +// vec![], +// vec![] +// ); +// +// // let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); +// // assert_eq!(db_acc.nonce, statediffs_account[0].nonce); +// // assert_eq!(db_acc.balance, statediffs_account[0].balance); +// // assert_eq!(db_acc.code, Some(custom_bytecode)); +// } diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 114959f759d4a..97b839f85140f 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -10,6 +10,24 @@ use crate::structs::{TelosAccountStateTableRow, TelosAccountTableRow}; use tracing::info; use sha2::{Sha256, Digest}; +fn override_account( + revm_db: &mut &mut State, + row: &TelosAccountTableRow +) where DB: Database { + let mut mod_state = HashMap::new(); + mod_state.insert(row.address, Account { + info: AccountInfo { + balance: row.balance, + nonce: row.nonce, + code_hash: B256::from(Sha256::digest(row.code.as_ref()).as_ref()), + code: Some(Bytecode::LegacyRaw(row.code.clone())), + }, + storage: HashMap::new(), + status: AccountStatus::Touched | AccountStatus::LoadedAsNotExisting, + }); + revm_db.commit(mod_state); +} + /// This function compares the state diffs between revm and Telos EVM contract pub fn compare_state_diffs( evm: &mut Evm<'_, Ext, &mut State>, @@ -21,7 +39,6 @@ pub fn compare_state_diffs( ) -> bool where DB: Database, - DB::Error: Into + Display, { if !revm_state_diffs.is_empty() || !statediffs_account.is_empty() @@ -114,10 +131,14 @@ where }, } } - // // Check code content inequality - // if unwrapped_revm_row.clone().unwrap().code.is_some() && !unwrapped_revm_row.clone().unwrap().code.unwrap().is_empty() && unwrapped_revm_row.clone().unwrap().code.unwrap().bytes() != row.code { - // panic!("Difference in code content, revm: {:?}, tevm: {:?}",unwrapped_revm_row.clone().unwrap().code.unwrap().bytes(),row.code); - // } + // Check code content inequality + if unwrapped_revm_row.clone().code.is_some_and(|code| code.bytes() != row.code) { + if panic_mode { + panic!("Difference in code content, revm: {:?}, tevm: {:?}", unwrapped_revm_row.clone().code.unwrap().bytes(), row.code); + } + mod_acc.info.code = Some(Bytecode::LegacyRaw(row.code.clone())); + modded = true; + } if modded { let mut mod_state = HashMap::new(); mod_state.insert(row.address, mod_acc); @@ -128,31 +149,26 @@ where if !(row.balance == U256::ZERO && row.nonce == 0 && row.code.len() == 0) { if let Some(unwrapped_revm_state_diff) = revm_state_diffs.get(&row.address) { if !(unwrapped_revm_state_diff.status == DBAccountStatus::Destroyed && row.nonce == 0 && row.balance == U256::ZERO && row.code.len() == 0) { - panic!("A modified `account` table row was found on both revm state and revm state diffs, but seems to be destroyed on just one side, address: {:?}",row.address); + if panic_mode { + panic!("A modified `account` table row was found on both revm state and revm state diffs, but seems to be destroyed on just one side, address: {:?}",row.address); + } + override_account(revm_db, row); } } else { if panic_mode { panic!("A modified `account` table row was found on revm state, but contains no information, address: {:?}", row.address); } - let mut mod_state = HashMap::new(); - mod_state.insert(row.address, Account { - info: AccountInfo { - balance: row.balance, - nonce: row.nonce, - code_hash: B256::from(Sha256::digest(row.code.as_ref()).as_ref()), - code: Some(Bytecode::LegacyRaw(row.code.clone())), - }, - storage: HashMap::new(), - status: AccountStatus::Touched | AccountStatus::LoadedAsNotExisting, - }); - revm_db.commit(mod_state); + override_account(revm_db, row); } } } } else { // Skip if address is empty on both sides if !(row.balance == U256::ZERO && row.nonce == 0 && row.code.len() == 0) { - panic!("A modified `account` table row was not found on revm state, address: {:?}",row.address); + if panic_mode { + panic!("A modified `account` table row was not found on revm state, address: {:?}",row.address); + } + override_account(revm_db, row); } } }