From 34b29856ac4348916daf13c333ce6e74d3bb5dc8 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Mon, 14 Oct 2024 21:36:55 -0300 Subject: [PATCH 01/30] Add tests to trigger panics on compare_state_diffs function --- Cargo.lock | 1 + crates/telos/node/Cargo.toml | 1 + crates/telos/node/tests/state_bypass.rs | 390 ++++++++++++++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 crates/telos/node/tests/state_bypass.rs diff --git a/Cargo.lock b/Cargo.lock index aaba2eec5fe3..1881a8e1c0ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9417,6 +9417,7 @@ dependencies = [ "reth-rpc", "reth-stages", "reth-telos-rpc", + "reth-telos-rpc-engine-api 1.0.8", "reth-tracing", "reth-transaction-pool", "serde", diff --git a/crates/telos/node/Cargo.toml b/crates/telos/node/Cargo.toml index 3d7b8e01117d..a7e8c74bd718 100644 --- a/crates/telos/node/Cargo.toml +++ b/crates/telos/node/Cargo.toml @@ -34,6 +34,7 @@ reth-stages.workspace = true reth-telos-rpc.workspace = true reth-tracing.workspace = true reth-transaction-pool.workspace = true +reth-telos-rpc-engine-api.workspace = true clap.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs new file mode 100644 index 000000000000..0758473bd4ab --- /dev/null +++ b/crates/telos/node/tests/state_bypass.rs @@ -0,0 +1,390 @@ +use std::{fmt, fs}; +use std::path::PathBuf; +use std::str::FromStr; +use std::sync::Arc; +use alloy_provider::{Provider, ProviderBuilder}; +use reqwest::Url; +use serde_json::json; +use telos_consensus_client::execution_api_client::{ExecutionApiClient, RpcRequest}; +use telos_consensus_client::execution_api_client::ExecutionApiMethod::{ForkChoiceUpdatedV1, NewPayloadV1}; +use tracing::info; +use reth::args::RpcServerArgs; +use reth::builder::NodeConfig; +use alloy_primitives::{Address, B256, hex, U256, Bytes, map::HashMap}; +use alloy_primitives::hex::FromHex; +use alloy_rpc_types::engine::ExecutionPayloadV1; +use reth::primitives::revm_primitives::{Bytecode as RevmBytecode, LegacyAnalyzedBytecode}; +use reth::revm; +use reth::revm::db::{CacheDB, EmptyDBTyped, 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::tasks::TaskManager; +use reth_chainspec::{ChainSpec, ChainSpecBuilder, TEVMTESTNET}; +use reth_e2e_test_utils::node::NodeTestContext; +use reth_node_builder::NodeBuilder; +use reth_node_telos::{TelosArgs, TelosNode}; +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::compare::compare_state_diffs; +use reth_telos_rpc_engine_api::structs::{TelosAccountTableRow, TelosAccountStateTableRow, TelosEngineAPIExtraFields}; +use revm::primitives::Account; + +enum MockDBError { + GenericError(String) +} + +fn init_reth() -> eyre::Result<(NodeConfig, String)> { + let chain_spec = Arc::new( + ChainSpecBuilder::default() + .chain(TEVMTESTNET.chain) + .genesis(TEVMTESTNET.genesis.clone()) + .frontier_activated() + .homestead_activated() + .tangerine_whistle_activated() + .spurious_dragon_activated() + .byzantium_activated() + .constantinople_activated() + .petersburg_activated() + .istanbul_activated() + .berlin_activated() + .build(), + ); + + let mut rpc_config = RpcServerArgs::default().with_unused_ports().with_http(); + rpc_config.auth_jwtsecret = Some(PathBuf::from("tests/assets/jwt.hex")); + + // Node setup + let node_config = NodeConfig::test().with_chain(chain_spec).with_rpc(rpc_config.clone()); + + let jwt = fs::read_to_string(node_config.rpc.auth_jwtsecret.clone().unwrap())?; + Ok((node_config, jwt)) +} + +#[tokio::test] +async fn test_integration_tevm_only() { + tracing_subscriber::fmt::init(); + + let (node_config, jwt_secret) = init_reth().unwrap(); + + let exec = TaskManager::current(); + let exec = exec.executor(); + + reth_tracing::init_test_tracing(); + + let telos_args = TelosArgs { + telos_endpoint: None, + signer_account: Some("rpc.evm".to_string()), + signer_permission: Some("active".to_string()), + signer_key: Some("5Jr65kdYmn33C3UabzhmWDm2PuqbRfPuDStts3ZFNSBLM7TqaiL".to_string()), + gas_cache_seconds: None, + experimental: false, + persistence_threshold: 0, + memory_block_buffer_target: 0, + max_execute_block_batch_size: 0, + }; + + let node_handle = NodeBuilder::new(node_config.clone()) + .testing_node(exec) + .node(TelosNode::new(telos_args.clone())) + .extend_rpc_modules(move |ctx| { + if telos_args.telos_endpoint.is_some() { + ctx.registry.eth_api().set_telos_client(TelosClient::new(telos_args.into())); + } + + Ok(()) + }) + .launch() + .await + .unwrap(); + + let execution_port = node_handle.node.auth_server_handle().local_addr().port(); + let rpc_port = node_handle.node.rpc_server_handles.rpc.http_local_addr().unwrap().port(); + 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 { + parent_hash: B256::from_hex("b25034033c9ca7a40e879ddcc29cf69071a22df06688b5fe8cc2d68b4e0528f9").unwrap(), + fee_recipient: Default::default(), + state_root: EMPTY_ROOT_HASH, + receipts_root: EMPTY_ROOT_HASH, + logs_bloom: Default::default(), + prev_randao: Default::default(), + block_number: 1, + gas_limit: 0x7fffffff, + gas_used: 0, + timestamp: 1728067687, + extra_data: Default::default(), + base_fee_per_gas: U256::try_from(MIN_PROTOCOL_BASE_FEE).unwrap(), + block_hash: B256::from_hex("0a1d73423169c8b4124121d40c0e13eb078621e73effd2d183f9a1d8017537dd").unwrap(), + transactions: vec![], + }; + + let test_addr = Address::from_hex("00000000000000000000000000000000deadbeef").unwrap(); + + let extra_fields = TelosEngineAPIExtraFields { + statediffs_account: Some(vec![TelosAccountTableRow { + removed: false, + address: test_addr, + account: "eosio".to_string(), + nonce: 0, + code: Default::default(), + balance: custom_balance, + }]), + statediffs_accountstate: Some(vec![]), + revision_changes: None, + gasprice_changes: None, + new_addresses_using_create: Some(vec![]), + new_addresses_using_openwallet: Some(vec![]), + receipts: Some(vec![]), + }; + + let block_req = RpcRequest { + method: NewPayloadV1, + params: vec![ + json![execution_payload], + json![extra_fields] + ].into() + }; + + let new_block_result = exec_client.rpc(block_req).await.unwrap(); + + info!("new_block: {:#?}", new_block_result); + + let fork_choice_result = exec_client.rpc(RpcRequest { + method: ForkChoiceUpdatedV1, + params: json![vec![ForkchoiceState { + head_block_hash: execution_payload.block_hash, + safe_block_hash: execution_payload.block_hash, + finalized_block_hash: execution_payload.block_hash + }]] + }).await.unwrap(); + + info!("fork_choice: {:#?}", fork_choice_result); + + + let provider = ProviderBuilder::new() + //.network::() + .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); + + 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(EmptyDBTyped::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(EmptyDBTyped::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(EmptyDBTyped::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 key = U256::from(420); + let value = U256::from(0); + let custom_value = U256::from(80085); + + let mut db = CacheDB::new(EmptyDBTyped::new()); + + let mut storage = HashMap::new(); + storage.insert(key, value); + + let mut state = State::builder().with_database(db).build(); + + state.insert_account_with_storage(test_addr, revm_acc_info, storage); + + 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_value = evm.db_mut().storage(test_addr, key).unwrap(); + assert_eq!(db_value, custom_value); +} +#[test] +fn test_accstate_telos_only() { + let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); + + let key = U256::from(420); + let custom_value = U256::from(80085); + + let mut db = CacheDB::new(EmptyDBTyped::new()); + + let mut state = State::builder().with_database(db).build(); + + // state.insert_not_existing(test_addr); + + 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![] + ); +} From f81ec7f5144370606a8a45b287a9d2b082a1c05a Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Tue, 15 Oct 2024 17:34:48 -0300 Subject: [PATCH 02/30] Fix test suite HashMap::default and finish clean impl of MockDBError in order to maintain traits in compare function --- crates/telos/node/tests/state_bypass.rs | 47 ++++++++++++++++++------- 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs index 0758473bd4ab..60e23cd44688 100644 --- a/crates/telos/node/tests/state_bypass.rs +++ b/crates/telos/node/tests/state_bypass.rs @@ -1,4 +1,5 @@ use std::{fmt, fs}; +use std::fmt::{Display, Formatter}; use std::path::PathBuf; use std::str::FromStr; use std::sync::Arc; @@ -14,6 +15,7 @@ use alloy_primitives::{Address, B256, hex, U256, Bytes, map::HashMap}; use alloy_primitives::hex::FromHex; use alloy_rpc_types::engine::ExecutionPayloadV1; use reth::primitives::revm_primitives::{Bytecode as RevmBytecode, LegacyAnalyzedBytecode}; +use reth::providers::ProviderError; use reth::revm; use reth::revm::db::{CacheDB, EmptyDBTyped, StorageWithOriginalValues, states::StorageSlot}; use reth::revm::{Database, DatabaseCommit, DatabaseRef, Evm, State, TransitionAccount}; @@ -31,10 +33,31 @@ use reth_telos_rpc_engine_api::compare::compare_state_diffs; use reth_telos_rpc_engine_api::structs::{TelosAccountTableRow, TelosAccountStateTableRow, TelosEngineAPIExtraFields}; use revm::primitives::Account; +#[derive(Debug)] enum MockDBError { GenericError(String) } +impl Into for MockDBError { + fn into(self) -> ProviderError { + match self { + MockDBError::GenericError(msg) => { + ProviderError::NippyJar(msg) + } + } + } +} + +impl Display for MockDBError { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + match self { + MockDBError::GenericError(msg) => { + f.write_str(&msg) + } + } + } +} + fn init_reth() -> eyre::Result<(NodeConfig, String)> { let chain_spec = Arc::new( ChainSpecBuilder::default() @@ -199,7 +222,7 @@ fn test_db_both_sides_present_but_dif() { }; - let mut db = CacheDB::new(EmptyDBTyped::new()); + let mut db = CacheDB::new(EmptyDBTyped::::new()); db.insert_account_info(test_addr, revm_acc_info); let mut state = State::builder().with_database(db).build(); @@ -217,7 +240,7 @@ fn test_db_both_sides_present_but_dif() { compare_state_diffs( &mut evm, - HashMap::new(), + HashMap::default(), statediffs_account.clone(), vec![], vec![], @@ -241,15 +264,15 @@ fn test_revm_state_both_sides_present_but_dif() { code: None, }; - let mut revm_state_diffs = HashMap::new(); + let mut revm_state_diffs = HashMap::default(); - let mut transition_account = TransitionAccount::new_empty_eip161(HashMap::new()); + let mut transition_account = TransitionAccount::new_empty_eip161(HashMap::default()); transition_account.info = Some(revm_acc_info); revm_state_diffs.insert(test_addr, transition_account); - let mut db = CacheDB::new(EmptyDBTyped::new()); + let mut db = CacheDB::new(EmptyDBTyped::::new()); let mut state = State::builder().with_database(db).build(); @@ -283,7 +306,7 @@ fn test_revm_state_both_sides_present_but_dif() { fn test_tevm_only() { let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); - let mut db = CacheDB::new(EmptyDBTyped::new()); + let mut db = CacheDB::new(EmptyDBTyped::::new()); let mut state = State::builder().with_database(db).build(); @@ -300,7 +323,7 @@ fn test_tevm_only() { compare_state_diffs( &mut evm, - HashMap::new(), + HashMap::default(), statediffs_account.clone(), vec![], vec![], @@ -327,9 +350,9 @@ fn test_accstate_diff_from_storage() { let value = U256::from(0); let custom_value = U256::from(80085); - let mut db = CacheDB::new(EmptyDBTyped::new()); + let mut db = CacheDB::new(EmptyDBTyped::::new()); - let mut storage = HashMap::new(); + let mut storage = HashMap::default(); storage.insert(key, value); let mut state = State::builder().with_database(db).build(); @@ -347,7 +370,7 @@ fn test_accstate_diff_from_storage() { compare_state_diffs( &mut evm, - HashMap::new(), + HashMap::default(), vec![], statediffs_accountstate.clone(), vec![], @@ -364,7 +387,7 @@ fn test_accstate_telos_only() { let key = U256::from(420); let custom_value = U256::from(80085); - let mut db = CacheDB::new(EmptyDBTyped::new()); + let mut db = CacheDB::new(EmptyDBTyped::::new()); let mut state = State::builder().with_database(db).build(); @@ -381,7 +404,7 @@ fn test_accstate_telos_only() { compare_state_diffs( &mut evm, - HashMap::new(), + HashMap::default(), vec![], statediffs_accountstate.clone(), vec![], From 1b59f02a3c9a6b685a9bc98c0f4e887a863d440a Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Wed, 16 Oct 2024 13:33:38 -0300 Subject: [PATCH 03/30] Add state override mechanism for most cases, need to finish storage --- crates/ethereum/evm/src/execute.rs | 3 +- crates/telos/node/tests/state_bypass.rs | 15 ++- crates/telos/rpc-engine-api/src/compare.rs | 130 ++++++++++++++++++--- 3 files changed, 123 insertions(+), 25 deletions(-) diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 188ca0e21f25..ece46cf016c2 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -271,7 +271,8 @@ where unwrapped_telos_extra_fields.statediffs_account.unwrap_or_default(), unwrapped_telos_extra_fields.statediffs_accountstate.unwrap_or_default(), unwrapped_telos_extra_fields.new_addresses_using_create.unwrap_or_default(), - unwrapped_telos_extra_fields.new_addresses_using_openwallet.unwrap_or_default() + unwrapped_telos_extra_fields.new_addresses_using_openwallet.unwrap_or_default(), + true ) ); } diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs index 60e23cd44688..87e860f822f2 100644 --- a/crates/telos/node/tests/state_bypass.rs +++ b/crates/telos/node/tests/state_bypass.rs @@ -244,7 +244,8 @@ fn test_db_both_sides_present_but_dif() { statediffs_account.clone(), vec![], vec![], - vec![] + vec![], + true ); let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); @@ -293,7 +294,8 @@ fn test_revm_state_both_sides_present_but_dif() { statediffs_account.clone(), vec![], vec![], - vec![] + vec![], + false ); // let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); @@ -327,7 +329,8 @@ fn test_tevm_only() { statediffs_account.clone(), vec![], vec![], - vec![] + vec![], + true ); let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); @@ -374,7 +377,8 @@ fn test_accstate_diff_from_storage() { vec![], statediffs_accountstate.clone(), vec![], - vec![] + vec![], + true ); let db_value = evm.db_mut().storage(test_addr, key).unwrap(); @@ -408,6 +412,7 @@ fn test_accstate_telos_only() { vec![], statediffs_accountstate.clone(), vec![], - vec![] + vec![], + true ); } diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 5c0367763ed0..d6aa88e2b870 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -1,13 +1,95 @@ use std::collections::HashSet; use std::fmt::Display; use alloy_primitives::{Address, B256, U256}; -use revm_primitives::HashMap; -use revm::db::AccountStatus; -use revm::{Database, Evm, State, TransitionAccount}; -use tracing::debug; +use revm_primitives::{Account, AccountInfo, Bytecode, HashMap}; +use revm::{Database, Evm, State, TransitionAccount, db::AccountStatus as DBAccountStatus}; +use revm_primitives::db::DatabaseCommit; +use revm_primitives::state::AccountStatus; +use tracing::{debug, warn}; use reth_storage_errors::provider::ProviderError; use crate::structs::{TelosAccountStateTableRow, TelosAccountTableRow}; +struct StateOverride { + accounts: HashMap +} + +impl StateOverride { + pub fn new() -> Self { + StateOverride { + accounts: HashMap::default() + } + } + + fn maybe_init_account (&mut self, revm_db: &mut &mut State, address: Address) { + let maybe_acc = self.accounts.get_mut(&address); + if maybe_acc.is_none() { + let mut status = AccountStatus::LoadedAsNotExisting; + let info = match revm_db.basic(address) { + Ok(maybe_info) => { + match maybe_info { + None => AccountInfo::default(), + Some(i) => { + status |= AccountStatus::Touched; + i + } + } + }, + Err(_) => AccountInfo::default() + }; + + self.accounts.insert(address, Account { + info, + storage: Default::default(), + status, + }); + } + } + + pub fn override_account (&mut self, revm_db: &mut &mut State, telos_row: &TelosAccountTableRow) { + self.maybe_init_account(revm_db, telos_row.address); + let mut acc = self.accounts.get_mut(&telos_row.address).unwrap(); + acc.info.balance = telos_row.balance; + acc.info.nonce = telos_row.nonce; + if telos_row.code.len() > 0 { + acc.info.code = Some(Bytecode::LegacyRaw(telos_row.code.clone())); + } else { + acc.info.code = None; + } + } + + pub fn override_balance (&mut self, revm_db: &mut &mut State, address: Address, balance: U256) { + self.maybe_init_account(revm_db, address); + let mut acc = self.accounts.get_mut(&address).unwrap(); + acc.info.balance = balance; + } + + pub fn override_nonce (&mut self, revm_db: &mut &mut State, address: Address, nonce: u64) { + self.maybe_init_account(revm_db, address); + let mut acc = self.accounts.get_mut(&address).unwrap(); + acc.info.nonce = nonce; + } + + pub fn override_code (&mut self, revm_db: &mut &mut State, address: Address, code: Option) { + self.maybe_init_account(revm_db, address); + let mut acc = self.accounts.get_mut(&address).unwrap(); + acc.info.code = code; + } + + pub fn apply (&self, revm_db: &mut &mut State) { + revm_db.commit(self.accounts.clone()); + } +} + +macro_rules! maybe_panic { + ($panic_mode:expr, $($arg:tt)*) => { + if $panic_mode { + panic!($($arg)*); + } else { + warn!($($arg)*); + } + }; +} + /// This function compares the state diffs between revm and Telos EVM contract pub fn compare_state_diffs( evm: &mut Evm<'_, Ext, &mut State>, @@ -16,6 +98,7 @@ pub fn compare_state_diffs( statediffs_accountstate: Vec, _new_addresses_using_create: Vec<(u64, U256)>, new_addresses_using_openwallet: Vec<(u64, U256)>, + panic_mode: bool ) -> bool where DB: Database, @@ -34,6 +117,8 @@ where let revm_db: &mut &mut State = evm.db_mut(); + let mut state_override = StateOverride::new(); + let mut new_addresses_using_openwallet_hashset = HashSet::new(); for row in &new_addresses_using_openwallet { new_addresses_using_openwallet_hashset.insert(Address::from_word(B256::from(row.1))); @@ -61,42 +146,47 @@ where if let Some(unwrapped_revm_row) = revm_row { // Check balance inequality if unwrapped_revm_row.balance != row.balance { - panic!("Difference in balance, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.balance,row.balance); + maybe_panic!(panic_mode, "Difference in balance, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.balance,row.balance); + state_override.override_balance(revm_db, row.address, row.balance); } // Check nonce inequality if unwrapped_revm_row.nonce != row.nonce { - panic!("Difference in nonce, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.nonce,row.nonce); + maybe_panic!(panic_mode, "Difference in nonce, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.nonce,row.nonce); + state_override.override_nonce(revm_db, row.address, row.nonce); } // Check code size inequality if unwrapped_revm_row.clone().code.is_none() && row.code.len() != 0 || unwrapped_revm_row.clone().code.is_some() && !unwrapped_revm_row.clone().code.unwrap().is_empty() && row.code.len() == 0 { match revm_db.code_by_hash(unwrapped_revm_row.code_hash) { Ok(code_by_hash) => if (code_by_hash.is_empty() && row.code.len() != 0) || (!code_by_hash.is_empty() && row.code.len() == 0) { - panic!("Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code_by_hash,row.code) + maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code_by_hash,row.code) }, - Err(_) => panic!("Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.code,row.code), + Err(_) => maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.code,row.code), } } // // 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); + // panic!(panic_mode, "Difference in code content, revm: {:?}, tevm: {:?}",unwrapped_revm_row.clone().unwrap().code.unwrap().bytes(),row.code); // } } else { // Skip if address is empty on both sides 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 == AccountStatus::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 !(unwrapped_revm_state_diff.status == DBAccountStatus::Destroyed && row.nonce == 0 && row.balance == U256::ZERO && row.code.len() == 0) { + maybe_panic!(panic_mode, "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); + state_override.override_account(revm_db, &row); } } else { - panic!("A modified `account` table row was found on revm state, but contains no information, address: {:?}",row.address); + maybe_panic!(panic_mode, "A modified `account` table row was found on revm state, but contains no information, address: {:?}",row.address); + state_override.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); + maybe_panic!(panic_mode, "A modified `account` table row was not found on revm state, address: {:?}",row.address); + state_override.override_account(revm_db, &row); } } } @@ -104,10 +194,10 @@ where if let Ok(revm_row) = revm_db.storage(row.address, row.key) { // The values should match, but if it is removed, then the revm value should be zero if !(revm_row == row.value) && !(revm_row != U256::ZERO || row.removed == true) { - panic!("Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}",row.address,row.key,revm_row,row); + maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}",row.address,row.key,revm_row,row); } } else { - panic!("Key was not found on revm storage, address: {:?}, key: {:?}",row.address,row.key); + maybe_panic!(panic_mode, "Key was not found on revm storage, address: {:?}, key: {:?}",row.address,row.key); } } @@ -115,20 +205,22 @@ where if let (Some(info),Some(previous_info)) = (account.info.clone(),account.previous_info.clone()) { if !(info.balance == previous_info.balance && info.nonce == previous_info.nonce && info.code_hash == previous_info.code_hash) { if statediffs_account_hashmap.get(address).is_none() { - panic!("A modified address was not found on tevm state diffs, address: {:?}",address); + maybe_panic!(panic_mode, "A modified address was not found on tevm state diffs, address: {:?}",address); } } } else { if statediffs_account_hashmap.get(address).is_none() { - panic!("A modified address was not found on tevm state diffs, address: {:?}",address); + maybe_panic!(panic_mode, "A modified address was not found on tevm state diffs, address: {:?}",address); } } for (key,_) in account.storage.clone() { if statediffs_accountstate_hashmap.get(&(*address,key)).is_none() { - panic!("A modified storage slot was not found on tevm state diffs, address: {:?}",address); + maybe_panic!(panic_mode, "A modified storage slot was not found on tevm state diffs, address: {:?}",address); } } } - + + state_override.apply(revm_db); + return true } From 7db24f017416360d870b892ea9c806bc7ac02473 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Wed, 16 Oct 2024 15:04:48 -0300 Subject: [PATCH 04/30] Add code override based on original_size and separate override code test --- Cargo.lock | 1 + crates/telos/node/tests/state_bypass.rs | 51 +++++++++++++++++++--- crates/telos/rpc-engine-api/Cargo.toml | 1 + crates/telos/rpc-engine-api/src/compare.rs | 29 +++++++++--- 4 files changed, 70 insertions(+), 12 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1881a8e1c0ce..ab256d57437b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -10620,6 +10620,7 @@ dependencies = [ "revm", "revm-primitives", "serde", + "sha2 0.10.8", "tracing", ] diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs index 87e860f822f2..73e4debd033f 100644 --- a/crates/telos/node/tests/state_bypass.rs +++ b/crates/telos/node/tests/state_bypass.rs @@ -211,9 +211,6 @@ fn test_db_both_sides_present_but_dif() { 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, @@ -221,7 +218,6 @@ fn test_db_both_sides_present_but_dif() { code: None, }; - let mut db = CacheDB::new(EmptyDBTyped::::new()); db.insert_account_info(test_addr, revm_acc_info); @@ -234,7 +230,7 @@ fn test_db_both_sides_present_but_dif() { address: test_addr, account: "eosio".to_string(), nonce: custom_nonce, - code: custom_code.clone(), + code: Default::default(), balance: custom_balance, }]; @@ -245,12 +241,55 @@ fn test_db_both_sides_present_but_dif() { vec![], vec![], vec![], - true + false ); 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_db_both_sides_only_code() { + let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); + + let custom_code = Bytes::from(&hex!("ffff")); + let custom_bytecode = RevmBytecode::LegacyRaw(custom_code.clone()); + + let revm_acc_info = AccountInfo { + balance: U256::from(0), + nonce: 0, + code_hash: Default::default(), + code: None, + }; + + let mut db = CacheDB::new(EmptyDBTyped::::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: 0, + code: custom_code.clone(), + balance: U256::from(0), + }]; + + compare_state_diffs( + &mut evm, + HashMap::default(), + statediffs_account.clone(), + vec![], + vec![], + vec![], + false + ); + + let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); assert_eq!(db_acc.code, Some(custom_bytecode)); } diff --git a/crates/telos/rpc-engine-api/Cargo.toml b/crates/telos/rpc-engine-api/Cargo.toml index 30e0d56832e9..417f4537c268 100644 --- a/crates/telos/rpc-engine-api/Cargo.toml +++ b/crates/telos/rpc-engine-api/Cargo.toml @@ -16,6 +16,7 @@ reth-storage-errors.workspace = true revm.workspace = true revm-primitives.workspace = true tracing.workspace = true +sha2.workspace = true [lints] workspace = true diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index d6aa88e2b870..089d9cd7c302 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -1,10 +1,11 @@ use std::collections::HashSet; use std::fmt::Display; -use alloy_primitives::{Address, B256, U256}; +use alloy_primitives::{Address, B256, Bytes, U256}; use revm_primitives::{Account, AccountInfo, Bytecode, HashMap}; use revm::{Database, Evm, State, TransitionAccount, db::AccountStatus as DBAccountStatus}; use revm_primitives::db::DatabaseCommit; use revm_primitives::state::AccountStatus; +use sha2::{Digest, Sha256}; use tracing::{debug, warn}; use reth_storage_errors::provider::ProviderError; use crate::structs::{TelosAccountStateTableRow, TelosAccountTableRow}; @@ -51,8 +52,10 @@ impl StateOverride { acc.info.balance = telos_row.balance; acc.info.nonce = telos_row.nonce; if telos_row.code.len() > 0 { + acc.info.code_hash = B256::from_slice(Sha256::digest(telos_row.code.as_ref()).as_slice()); acc.info.code = Some(Bytecode::LegacyRaw(telos_row.code.clone())); } else { + acc.info.code_hash = Default::default(); acc.info.code = None; } } @@ -69,10 +72,19 @@ impl StateOverride { acc.info.nonce = nonce; } - pub fn override_code (&mut self, revm_db: &mut &mut State, address: Address, code: Option) { + pub fn override_code (&mut self, revm_db: &mut &mut State, address: Address, maybe_code: Option) { self.maybe_init_account(revm_db, address); let mut acc = self.accounts.get_mut(&address).unwrap(); - acc.info.code = code; + match maybe_code { + None => { + acc.info.code_hash = Default::default(); + acc.info.code = None; + } + Some(code) => { + acc.info.code_hash = B256::from_slice(Sha256::digest(code.as_ref()).as_slice()); + acc.info.code = Some(Bytecode::LegacyRaw(code)); + } + } } pub fn apply (&self, revm_db: &mut &mut State) { @@ -155,13 +167,18 @@ where state_override.override_nonce(revm_db, row.address, row.nonce); } // Check code size inequality - if unwrapped_revm_row.clone().code.is_none() && row.code.len() != 0 || unwrapped_revm_row.clone().code.is_some() && !unwrapped_revm_row.clone().code.unwrap().is_empty() && row.code.len() == 0 { + if (unwrapped_revm_row.clone().code.is_none() && row.code.len() != 0) || + (unwrapped_revm_row.clone().code.is_some() && !unwrapped_revm_row.clone().code.unwrap().original_bytes().len() != row.code.len()) { match revm_db.code_by_hash(unwrapped_revm_row.code_hash) { Ok(code_by_hash) => if (code_by_hash.is_empty() && row.code.len() != 0) || (!code_by_hash.is_empty() && row.code.len() == 0) { - maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code_by_hash,row.code) + maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code_by_hash,row.code); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); }, - Err(_) => maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.code,row.code), + Err(_) => { + maybe_panic!(panic_mode, "Difference in code existence (Err while searching by code_hash), address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.code,row.code); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); + }, } } // // Check code content inequality From 3162f0d42de8443ace48debd3239f14e033ca703 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Wed, 16 Oct 2024 16:11:11 -0300 Subject: [PATCH 05/30] Changes to make all tests pass except the one commented out due to hitting unreachable code panic --- crates/telos/node/tests/state_bypass.rs | 85 +++++++++++----------- crates/telos/rpc-engine-api/src/compare.rs | 24 +++--- 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/crates/telos/node/tests/state_bypass.rs b/crates/telos/node/tests/state_bypass.rs index 73e4debd033f..88bfc9fd0d03 100644 --- a/crates/telos/node/tests/state_bypass.rs +++ b/crates/telos/node/tests/state_bypass.rs @@ -298,8 +298,8 @@ 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, + balance: U256::from(1), + nonce: 0, code_hash: Default::default(), code: None, }; @@ -334,13 +334,12 @@ fn test_revm_state_both_sides_present_but_dif() { vec![], vec![], vec![], - false + false ); - // 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)); + 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] @@ -369,7 +368,7 @@ fn test_tevm_only() { vec![], vec![], vec![], - true + false ); let db_acc = evm.db_mut().basic(test_addr).unwrap().unwrap(); @@ -382,8 +381,8 @@ 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, + balance: U256::from(1), + nonce: 0, code_hash: Default::default(), code: None, }; @@ -417,41 +416,41 @@ fn test_accstate_diff_from_storage() { statediffs_accountstate.clone(), vec![], vec![], - true + false ); let db_value = evm.db_mut().storage(test_addr, key).unwrap(); assert_eq!(db_value, custom_value); } -#[test] -fn test_accstate_telos_only() { - let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); - - let key = U256::from(420); - let custom_value = U256::from(80085); - - let mut db = CacheDB::new(EmptyDBTyped::::new()); - - let mut state = State::builder().with_database(db).build(); - - // state.insert_not_existing(test_addr); - - 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::default(), - vec![], - statediffs_accountstate.clone(), - vec![], - vec![], - true - ); -} +// #[test] +// fn test_accstate_telos_only() { +// let test_addr = Address::from_str("00000000000000000000000000000000deadbeef").unwrap(); +// +// let key = U256::from(420); +// let custom_value = U256::from(80085); +// +// let mut db = CacheDB::new(EmptyDBTyped::::new()); +// +// let mut state = State::builder().with_database(db).build(); +// +// // state.insert_not_existing(test_addr); +// +// 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::default(), +// vec![], +// statediffs_accountstate.clone(), +// vec![], +// vec![], +// true +// ); +// } diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 089d9cd7c302..90b796bbd50c 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -1,7 +1,7 @@ use std::collections::HashSet; use std::fmt::Display; use alloy_primitives::{Address, B256, Bytes, U256}; -use revm_primitives::{Account, AccountInfo, Bytecode, HashMap}; +use revm_primitives::{Account, AccountInfo, Bytecode, EvmStorageSlot, HashMap}; use revm::{Database, Evm, State, TransitionAccount, db::AccountStatus as DBAccountStatus}; use revm_primitives::db::DatabaseCommit; use revm_primitives::state::AccountStatus; @@ -24,16 +24,10 @@ impl StateOverride { fn maybe_init_account (&mut self, revm_db: &mut &mut State, address: Address) { let maybe_acc = self.accounts.get_mut(&address); if maybe_acc.is_none() { - let mut status = AccountStatus::LoadedAsNotExisting; + let mut status = AccountStatus::LoadedAsNotExisting | AccountStatus::Touched; let info = match revm_db.basic(address) { Ok(maybe_info) => { - match maybe_info { - None => AccountInfo::default(), - Some(i) => { - status |= AccountStatus::Touched; - i - } - } + maybe_info.unwrap_or_else(|| AccountInfo::default()) }, Err(_) => AccountInfo::default() }; @@ -87,6 +81,16 @@ impl StateOverride { } } + pub fn override_storage (&mut self, revm_db: &mut &mut State, address: Address, key: U256, val: U256) { + self.maybe_init_account(revm_db, address); + let mut acc = self.accounts.get_mut(&address).unwrap(); + acc.storage.insert(key, EvmStorageSlot { + original_value: Default::default(), + present_value: val, + is_cold: false + }); + } + pub fn apply (&self, revm_db: &mut &mut State) { revm_db.commit(self.accounts.clone()); } @@ -212,9 +216,11 @@ where // The values should match, but if it is removed, then the revm value should be zero if !(revm_row == row.value) && !(revm_row != U256::ZERO || row.removed == true) { maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}",row.address,row.key,revm_row,row); + state_override.override_storage(revm_db, row.address, row.key, row.value); } } else { maybe_panic!(panic_mode, "Key was not found on revm storage, address: {:?}, key: {:?}",row.address,row.key); + state_override.override_storage(revm_db, row.address, row.key, row.value); } } From 4c8af52ad2a8f4cf850a9b9b1792d2387fd7ff9b Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Wed, 16 Oct 2024 16:25:23 -0300 Subject: [PATCH 06/30] Bring amirs rebase fixes in execute.rs --- Cargo.lock | 1 + crates/ethereum/evm/Cargo.toml | 1 + crates/ethereum/evm/src/execute.rs | 160 +++++++++++++++++++---------- 3 files changed, 109 insertions(+), 53 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ab256d57437b..c1f315ad01c5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -8755,6 +8755,7 @@ dependencies = [ "revm-primitives", "secp256k1", "serde_json", + "sha2 0.10.8", ] [[package]] diff --git a/crates/ethereum/evm/Cargo.toml b/crates/ethereum/evm/Cargo.toml index 413a3e75411c..7a7989f8e05d 100644 --- a/crates/ethereum/evm/Cargo.toml +++ b/crates/ethereum/evm/Cargo.toml @@ -20,6 +20,7 @@ reth-revm.workspace = true reth-ethereum-consensus.workspace = true reth-prune-types.workspace = true reth-execution-types.workspace = true +sha2.workspace = true # Ethereum revm-primitives.workspace = true diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index ece46cf016c2..de7e91602165 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -38,6 +38,8 @@ use reth_telos_rpc_engine_api::compare::compare_state_diffs; use revm_primitives::{Address, Account, AccountInfo, AccountStatus, Bytecode, HashMap, KECCAK_EMPTY}; #[cfg(feature = "telos")] use alloy_primitives::B256; +#[cfg(feature = "telos")] +use sha2::{Sha256, Digest}; /// Provides executors to execute regular ethereum blocks #[derive(Debug, Clone)] @@ -66,12 +68,12 @@ impl EthExecutorProvider { } impl EthExecutorProvider -where - EvmConfig: ConfigureEvm
, + where + EvmConfig: ConfigureEvm
, { fn eth_executor(&self, db: DB) -> EthBlockExecutor - where - DB: Database>, + where + DB: Database>, { EthBlockExecutor::new( self.chain_spec.clone(), @@ -82,25 +84,25 @@ where } impl BlockExecutorProvider for EthExecutorProvider -where - EvmConfig: ConfigureEvm
, + where + EvmConfig: ConfigureEvm
, { type Executor + Display>> = - EthBlockExecutor; + EthBlockExecutor; type BatchExecutor + Display>> = - EthBatchExecutor; + EthBatchExecutor; fn executor(&self, db: DB) -> Self::Executor - where - DB: Database + Display>, + where + DB: Database + Display>, { self.eth_executor(db) } fn batch_executor(&self, db: DB) -> Self::BatchExecutor - where - DB: Database + Display>, + where + DB: Database + Display>, { let executor = self.eth_executor(db); EthBatchExecutor { executor, batch_record: BlockBatchRecord::default() } @@ -125,8 +127,8 @@ struct EthEvmExecutor { } impl EthEvmExecutor -where - EvmConfig: ConfigureEvm
, + where + EvmConfig: ConfigureEvm
, { /// Executes the transactions in the block and returns the receipts of the transactions in the /// block, the total gas used and the list of EIP-7685 [requests](Request). @@ -145,20 +147,20 @@ where #[cfg(feature = "telos")] telos_extra_fields: Option, ) -> Result - where - DB: Database, - DB::Error: Into + Display, + where + DB: Database, + DB::Error: Into + Display, { let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec); system_caller.apply_pre_execution_changes(block, &mut evm)?; #[cfg(feature = "telos")] - let mut tx_index = 0; + let mut tx_index = 0; #[cfg(feature = "telos")] - let unwrapped_telos_extra_fields = telos_extra_fields.unwrap_or_default(); + let unwrapped_telos_extra_fields = telos_extra_fields.unwrap_or_default(); #[cfg(feature = "telos")] - let mut new_addresses_using_create_iter = unwrapped_telos_extra_fields.new_addresses_using_create.as_ref().unwrap().into_iter().peekable(); + let mut new_addresses_using_create_iter = unwrapped_telos_extra_fields.new_addresses_using_create.as_ref().unwrap().into_iter().peekable(); // #[cfg(feature = "telos")] // let parent_telos_ext; // #[cfg(feature = "telos")] @@ -192,7 +194,8 @@ where // execute transactions let mut cumulative_gas_used = 0; - let mut receipts = Vec::with_capacity(block.body.transactions.len()); + #[cfg(not(feature = "telos"))] + let mut receipts = Vec::with_capacity(block.body.transactions.len()); for (sender, transaction) in block.transactions_with_sender() { #[cfg(feature = "telos")] while new_addresses_using_create_iter.peek().is_some() && new_addresses_using_create_iter.peek().unwrap().0 == tx_index { @@ -214,7 +217,7 @@ where transaction_gas_limit: transaction.gas_limit(), block_available_gas, } - .into()) + .into()) } self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender, #[cfg(feature = "telos")] block.header.telos_block_extension.tx_env_at(tx_index)); @@ -233,6 +236,7 @@ where // append gas used cumulative_gas_used += result.gas_used(); + #[cfg(not(feature = "telos"))] // Push transaction changeset and calculate header bloom filter for receipt. receipts.push( #[allow(clippy::needless_update)] // side-effect of optimism fields @@ -259,24 +263,32 @@ where new_addresses_using_create_iter.next(); } - #[cfg(feature = "telos")] { - // Perform state diff comparision - let revm_state_diffs = evm.db_mut().transition_state.clone().unwrap_or_default().transitions; - let block_num = block.block.header.number; - println!( - "Compare: block {block_num} {}", - compare_state_diffs( - &mut evm, - revm_state_diffs, - unwrapped_telos_extra_fields.statediffs_account.unwrap_or_default(), - unwrapped_telos_extra_fields.statediffs_accountstate.unwrap_or_default(), - unwrapped_telos_extra_fields.new_addresses_using_create.unwrap_or_default(), - unwrapped_telos_extra_fields.new_addresses_using_openwallet.unwrap_or_default(), - true - ) - ); + #[cfg(feature = "telos")] + { + // Perform state diff comparision + let revm_state_diffs = evm.db_mut().transition_state.clone().unwrap_or_default().transitions; + let block_num = block.block.header.number; + println!( + "Compare: block {block_num} {}", + compare_state_diffs( + &mut evm, + revm_state_diffs, + unwrapped_telos_extra_fields.statediffs_account.clone().unwrap_or_default(), + unwrapped_telos_extra_fields.statediffs_accountstate.clone().unwrap_or_default(), + unwrapped_telos_extra_fields.new_addresses_using_create.clone().unwrap_or_default(), + unwrapped_telos_extra_fields.new_addresses_using_openwallet.clone().unwrap_or_default(), + false + ) + ); } + #[cfg(feature = "telos")] + let receipts = if unwrapped_telos_extra_fields.receipts.is_some() { + unwrapped_telos_extra_fields.receipts.clone().unwrap() + } else { + vec![] + }; + let requests = if self.chain_spec.is_prague_active_at_timestamp(block.timestamp) { // Collect all EIP-6110 deposits let deposit_requests = @@ -289,7 +301,44 @@ where vec![] }; - Ok(EthExecuteOutput { receipts, requests, gas_used: cumulative_gas_used }) + // #[cfg(feature = "telos")] + // { + // let mut addr_to_accstate: HashMap> = HashMap::new(); + + // for sdiff_accstate in unwrapped_telos_extra_fields.clone().statediffs_accountstate.unwrap_or(vec![]) { + // if !addr_to_accstate.contains_key(&sdiff_accstate.address) { + // addr_to_accstate.insert(sdiff_accstate.address, HashMap::new()); + // } + // let mut acc_storage = addr_to_accstate.get_mut(&sdiff_accstate.address).unwrap(); + // acc_storage.insert(sdiff_accstate.key, EvmStorageSlot { original_value: Default::default(), present_value: sdiff_accstate.value, is_cold: false }); + // } + + // let mut state: HashMap = HashMap::new(); + + // for sdiff_acc in unwrapped_telos_extra_fields.clone().statediffs_account.unwrap_or(vec![]) { + // state.insert( + // sdiff_acc.address, + // Account { + // info: AccountInfo { + // balance: sdiff_acc.balance, + // nonce: sdiff_acc.nonce, + // code_hash: B256::from(Sha256::digest(sdiff_acc.code.as_ref()).as_ref()), + // code: Some(Bytecode::LegacyRaw(sdiff_acc.code)), + // }, + // storage: addr_to_accstate.get(&sdiff_acc.address).unwrap_or(&HashMap::new()).clone(), + // status: AccountStatus::Touched | AccountStatus::LoadedAsNotExisting, + // } + // ); + // } + + // evm.db_mut().commit(state); + // } + + Ok(EthExecuteOutput { + receipts, + requests, + gas_used: cumulative_gas_used + }) } } @@ -325,9 +374,9 @@ impl EthBlockExecutor { } impl EthBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, + where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { /// Configures a new evm configuration and block environment for the given block. /// @@ -367,7 +416,12 @@ where let env = self.evm_env_for_block(&block.header, total_difficulty); let output = { let evm = self.executor.evm_config.evm_with_env(&mut self.state, env); - self.executor.execute_state_transitions(block, evm, #[cfg(feature = "telos")] telos_extra_fields) + self.executor.execute_state_transitions( + block, + evm, + #[cfg(feature = "telos")] + telos_extra_fields + ) }?; // 3. apply post execution changes @@ -416,9 +470,9 @@ where } impl Executor for EthBlockExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, + where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = BlockExecutionOutput; @@ -447,8 +501,8 @@ where #[cfg(feature = "telos")] telos_extra_fields: Option ) -> Result - where - F: FnMut(&State), + where + F: FnMut(&State), { let BlockExecutionInput { block, total_difficulty } = input; let EthExecuteOutput { receipts, requests, gas_used } = @@ -482,9 +536,9 @@ impl EthBatchExecutor { } impl BatchExecutor for EthBatchExecutor -where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, + where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = ExecutionOutcome; @@ -1338,8 +1392,8 @@ mod tests { header, body: BlockBody { transactions: vec![tx], ..Default::default() }, } - .with_recovered_senders() - .unwrap(), + .with_recovered_senders() + .unwrap(), U256::ZERO, ) .into(), From a0bde85a5164b436a20e3ded7c11cf1e893907f2 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Thu, 17 Oct 2024 09:23:36 -0300 Subject: [PATCH 07/30] Reverting unnecesary changes --- crates/ethereum/evm/src/execute.rs | 70 +++++++++++++++--------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index de7e91602165..bf0943f02091 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -68,12 +68,12 @@ impl EthExecutorProvider { } impl EthExecutorProvider - where - EvmConfig: ConfigureEvm
, +where + EvmConfig: ConfigureEvm
, { fn eth_executor(&self, db: DB) -> EthBlockExecutor - where - DB: Database>, + where + DB: Database>, { EthBlockExecutor::new( self.chain_spec.clone(), @@ -84,25 +84,25 @@ impl EthExecutorProvider } impl BlockExecutorProvider for EthExecutorProvider - where - EvmConfig: ConfigureEvm
, +where + EvmConfig: ConfigureEvm
, { type Executor + Display>> = - EthBlockExecutor; + EthBlockExecutor; type BatchExecutor + Display>> = - EthBatchExecutor; + EthBatchExecutor; fn executor(&self, db: DB) -> Self::Executor - where - DB: Database + Display>, + where + DB: Database + Display>, { self.eth_executor(db) } fn batch_executor(&self, db: DB) -> Self::BatchExecutor - where - DB: Database + Display>, + where + DB: Database + Display>, { let executor = self.eth_executor(db); EthBatchExecutor { executor, batch_record: BlockBatchRecord::default() } @@ -127,8 +127,8 @@ struct EthEvmExecutor { } impl EthEvmExecutor - where - EvmConfig: ConfigureEvm
, +where + EvmConfig: ConfigureEvm
, { /// Executes the transactions in the block and returns the receipts of the transactions in the /// block, the total gas used and the list of EIP-7685 [requests](Request). @@ -147,20 +147,20 @@ impl EthEvmExecutor #[cfg(feature = "telos")] telos_extra_fields: Option, ) -> Result - where - DB: Database, - DB::Error: Into + Display, + where + DB: Database, + DB::Error: Into + Display, { let mut system_caller = SystemCaller::new(&self.evm_config, &self.chain_spec); system_caller.apply_pre_execution_changes(block, &mut evm)?; #[cfg(feature = "telos")] - let mut tx_index = 0; + let mut tx_index = 0; #[cfg(feature = "telos")] - let unwrapped_telos_extra_fields = telos_extra_fields.unwrap_or_default(); + let unwrapped_telos_extra_fields = telos_extra_fields.unwrap_or_default(); #[cfg(feature = "telos")] - let mut new_addresses_using_create_iter = unwrapped_telos_extra_fields.new_addresses_using_create.as_ref().unwrap().into_iter().peekable(); + let mut new_addresses_using_create_iter = unwrapped_telos_extra_fields.new_addresses_using_create.as_ref().unwrap().into_iter().peekable(); // #[cfg(feature = "telos")] // let parent_telos_ext; // #[cfg(feature = "telos")] @@ -195,7 +195,7 @@ impl EthEvmExecutor // execute transactions let mut cumulative_gas_used = 0; #[cfg(not(feature = "telos"))] - let mut receipts = Vec::with_capacity(block.body.transactions.len()); + let mut receipts = Vec::with_capacity(block.body.transactions.len()); for (sender, transaction) in block.transactions_with_sender() { #[cfg(feature = "telos")] while new_addresses_using_create_iter.peek().is_some() && new_addresses_using_create_iter.peek().unwrap().0 == tx_index { @@ -217,7 +217,7 @@ impl EthEvmExecutor transaction_gas_limit: transaction.gas_limit(), block_available_gas, } - .into()) + .into()) } self.evm_config.fill_tx_env(evm.tx_mut(), transaction, *sender, #[cfg(feature = "telos")] block.header.telos_block_extension.tx_env_at(tx_index)); @@ -374,9 +374,9 @@ impl EthBlockExecutor { } impl EthBlockExecutor - where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, +where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { /// Configures a new evm configuration and block environment for the given block. /// @@ -470,9 +470,9 @@ impl EthBlockExecutor } impl Executor for EthBlockExecutor - where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, +where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = BlockExecutionOutput; @@ -501,8 +501,8 @@ impl Executor for EthBlockExecutor #[cfg(feature = "telos")] telos_extra_fields: Option ) -> Result - where - F: FnMut(&State), + where + F: FnMut(&State), { let BlockExecutionInput { block, total_difficulty } = input; let EthExecuteOutput { receipts, requests, gas_used } = @@ -536,9 +536,9 @@ impl EthBatchExecutor { } impl BatchExecutor for EthBatchExecutor - where - EvmConfig: ConfigureEvm
, - DB: Database + Display>, +where + EvmConfig: ConfigureEvm
, + DB: Database + Display>, { type Input<'a> = BlockExecutionInput<'a, BlockWithSenders>; type Output = ExecutionOutcome; @@ -1392,8 +1392,8 @@ mod tests { header, body: BlockBody { transactions: vec![tx], ..Default::default() }, } - .with_recovered_senders() - .unwrap(), + .with_recovered_senders() + .unwrap(), U256::ZERO, ) .into(), From c5e487143faf3c46509fd38be5f9f204407e1d44 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Thu, 17 Oct 2024 08:39:00 -0700 Subject: [PATCH 08/30] Load account into cache before we attempt to read/write it's storage --- crates/telos/rpc-engine-api/src/compare.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 90b796bbd50c..8e1754285037 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -212,6 +212,9 @@ where } } for row in &statediffs_accountstate { + if let None = revm_db.cache.accounts.get_mut(&row.address) { + let _ = revm_db.load_cache_account(row.address); + } if let Ok(revm_row) = revm_db.storage(row.address, row.key) { // The values should match, but if it is removed, then the revm value should be zero if !(revm_row == row.value) && !(revm_row != U256::ZERO || row.removed == true) { From c450dc5d7e9b6d1e950f93fbd6d9c220bb653276 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Thu, 17 Oct 2024 09:31:00 -0700 Subject: [PATCH 09/30] More strict on cache loading so we don't think we are writing to storage when the account was missing --- crates/telos/rpc-engine-api/src/compare.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 8e1754285037..f20e68b57a3d 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -213,12 +213,17 @@ where } for row in &statediffs_accountstate { if let None = revm_db.cache.accounts.get_mut(&row.address) { - let _ = revm_db.load_cache_account(row.address); + let cached_account = revm_db.load_cache_account(row.address); + if let Some(cached_account) = cached_account { + if cached_account.is_none() { + panic!("An account state modification was made for an account that is not in revm storage, address: {:?}", row.address); + } + } } if let Ok(revm_row) = revm_db.storage(row.address, row.key) { // The values should match, but if it is removed, then the revm value should be zero if !(revm_row == row.value) && !(revm_row != U256::ZERO || row.removed == true) { - maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}",row.address,row.key,revm_row,row); + maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); state_override.override_storage(revm_db, row.address, row.key, row.value); } } else { @@ -231,17 +236,17 @@ where if let (Some(info),Some(previous_info)) = (account.info.clone(),account.previous_info.clone()) { if !(info.balance == previous_info.balance && info.nonce == previous_info.nonce && info.code_hash == previous_info.code_hash) { if statediffs_account_hashmap.get(address).is_none() { - maybe_panic!(panic_mode, "A modified address was not found on tevm state diffs, address: {:?}",address); + panic!("A modified address was not found on tevm state diffs, address: {:?}",address); } } } else { if statediffs_account_hashmap.get(address).is_none() { - maybe_panic!(panic_mode, "A modified address was not found on tevm state diffs, address: {:?}",address); + panic!("A modified address was not found on tevm state diffs, address: {:?}",address); } } for (key,_) in account.storage.clone() { if statediffs_accountstate_hashmap.get(&(*address,key)).is_none() { - maybe_panic!(panic_mode, "A modified storage slot was not found on tevm state diffs, address: {:?}",address); + panic!("A modified storage slot was not found on tevm state diffs, address: {:?}",address); } } } From 9c3c2ca7dcf10e249c772af323aa91afe86f3d38 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Thu, 17 Oct 2024 09:37:47 -0700 Subject: [PATCH 10/30] Disambiguate error messages and fix Option/Result for cached account loading --- crates/telos/rpc-engine-api/src/compare.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index f20e68b57a3d..8538ed8f7ac3 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -214,9 +214,14 @@ where for row in &statediffs_accountstate { if let None = revm_db.cache.accounts.get_mut(&row.address) { let cached_account = revm_db.load_cache_account(row.address); - if let Some(cached_account) = cached_account { - if cached_account.is_none() { - panic!("An account state modification was made for an account that is not in revm storage, address: {:?}", row.address); + match cached_account { + Ok(cached_account) => { + if cached_account.account.is_none() { + panic!("An account state modification was made for an account that is not in revm storage, address: {:?}", row.address); + } + }, + Err(_) => { + panic!("An account state modification was made for an account that returned Err from load_cache_account, address: {:?}", row.address); } } } @@ -241,7 +246,7 @@ where } } else { if statediffs_account_hashmap.get(address).is_none() { - panic!("A modified address was not found on tevm state diffs, address: {:?}",address); + panic!("A modified address was not found on tevm state diffs, info/previous_info were not Some, address: {:?}",address); } } for (key,_) in account.storage.clone() { From 86218822693b35eb234173197c941727b6fbbefc Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Fri, 18 Oct 2024 10:34:12 -0700 Subject: [PATCH 11/30] If the account is not in reth_db then set status to Created|Touched --- crates/ethereum/evm/src/execute.rs | 2 +- crates/telos/rpc-engine-api/src/compare.rs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index bf0943f02091..3b2bb0d26890 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -283,7 +283,7 @@ where } #[cfg(feature = "telos")] - let receipts = if unwrapped_telos_extra_fields.receipts.is_some() { + let receipts = if unwrapped_telos_extra_fields.receipts.is_some() { unwrapped_telos_extra_fields.receipts.clone().unwrap() } else { vec![] diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 8538ed8f7ac3..aff94d374ead 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -29,7 +29,10 @@ impl StateOverride { Ok(maybe_info) => { maybe_info.unwrap_or_else(|| AccountInfo::default()) }, - Err(_) => AccountInfo::default() + Err(_) => { + status = AccountStatus::Created | AccountStatus::Touched; + AccountInfo::default() + } }; self.accounts.insert(address, Account { @@ -156,6 +159,7 @@ where } // Skip if row is removed if row.removed { + // TODO: Does the Telos EVM contract ever remove account rows, or should this be a panic?? continue } if let Ok(revm_row) = revm_db.basic(row.address) { @@ -258,5 +262,5 @@ where state_override.apply(revm_db); - return true + true } From 714095915f569ffd4b8304022af361c214f4b3b9 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Sat, 19 Oct 2024 15:17:32 -0700 Subject: [PATCH 12/30] Adding the start of a storage compare script --- crates/telos/node/tests/main.rs | 5 +- crates/telos/node/tests/storage_compare.rs | 58 ++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 crates/telos/node/tests/storage_compare.rs diff --git a/crates/telos/node/tests/main.rs b/crates/telos/node/tests/main.rs index 43614c668a2c..a23cd12d3808 100644 --- a/crates/telos/node/tests/main.rs +++ b/crates/telos/node/tests/main.rs @@ -1,4 +1,5 @@ -mod integration; -pub mod live_test_runner; +// mod integration; +// pub mod live_test_runner; +pub mod storage_compare; const fn main() {} \ No newline at end of file diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs new file mode 100644 index 000000000000..9aa8c61584f9 --- /dev/null +++ b/crates/telos/node/tests/storage_compare.rs @@ -0,0 +1,58 @@ +use alloy_provider::ProviderBuilder; +use antelope::api::client::{APIClient, DefaultProvider}; +use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; +use antelope::chain::name::Name; +use antelope::name; +use reqwest::Url; +use telos_translator_rs::types::evm_types::AccountRow; + +// #[tokio::test] +pub async fn compare() { + let evm_rpc = "http://localhost:8545"; + // let telos_rpc = "http://192.168.0.20:8884"; + let telos_rpc = "http://38.91.106.49:9000"; + let block_delta = 57; + + let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); + let info = api_client.v1_chain.get_info().await.unwrap(); + + + // let provider = ProviderBuilder::new() + // .on_http(Url::from_str(evm_rpc).unwrap()); + + println!("Telos chain info: {:?}", info); + let evm_block_num = info.head_block_num - block_delta; + + let mut has_more = true; + let mut lower_bound = Some(TableIndexType::UINT64(0)); + + let mut count = 0; + + while has_more { + let mut query_params = GetTableRowsParams { + code: name!("eosio.evm"), + table: name!("account"), + scope: None, + lower_bound, + upper_bound: None, + limit: Some(5000), + reverse: None, + index_position: None, + show_payer: None, + }; + let account_rows = api_client.v1_chain.get_table_rows::(query_params).await; + if let Ok(account_rows) = account_rows { + lower_bound = account_rows.next_key; + has_more = lower_bound.is_some(); + for account_row in account_rows.rows { + println!("Account: {:?}", account_row.address.as_string()); + count += 1; + lower_bound = Some(TableIndexType::UINT64(account_row.index + 1)); + } + } else { + panic!("Failed to fetch account row"); + } + } + + println!("Total account rows: {}", count); +} \ No newline at end of file From 4cd6a5c45028f7487eea23bca23e599ffb2d04ef Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Sat, 19 Oct 2024 16:49:23 -0700 Subject: [PATCH 13/30] Starting of compare_account --- crates/telos/node/tests/storage_compare.rs | 24 ++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index 9aa8c61584f9..fb976f5d0f2a 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -1,4 +1,6 @@ -use alloy_provider::ProviderBuilder; +use std::str::FromStr; +use alloy_primitives::{Address, U256}; +use alloy_provider::{Provider, ProviderBuilder, ReqwestProvider}; use antelope::api::client::{APIClient, DefaultProvider}; use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; use antelope::chain::name::Name; @@ -6,7 +8,7 @@ use antelope::name; use reqwest::Url; use telos_translator_rs::types::evm_types::AccountRow; -// #[tokio::test] +#[tokio::test] pub async fn compare() { let evm_rpc = "http://localhost:8545"; // let telos_rpc = "http://192.168.0.20:8884"; @@ -16,9 +18,8 @@ pub async fn compare() { let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); let info = api_client.v1_chain.get_info().await.unwrap(); - - // let provider = ProviderBuilder::new() - // .on_http(Url::from_str(evm_rpc).unwrap()); + let provider = ProviderBuilder::new() + .on_http(Url::from_str(evm_rpc).unwrap()); println!("Telos chain info: {:?}", info); let evm_block_num = info.head_block_num - block_delta; @@ -45,7 +46,8 @@ pub async fn compare() { lower_bound = account_rows.next_key; has_more = lower_bound.is_some(); for account_row in account_rows.rows { - println!("Account: {:?}", account_row.address.as_string()); + let address = Address::from_slice(account_row.address.data.as_slice()); + println!("Account: {:?}", address); count += 1; lower_bound = Some(TableIndexType::UINT64(account_row.index + 1)); } @@ -55,4 +57,14 @@ pub async fn compare() { } println!("Total account rows: {}", count); +} + +async fn compare_account(account_row: AccountRow, provider: &ReqwestProvider) { + let address = Address::from_slice(account_row.address.data.as_slice()); + let telos_balance = U256::from(account_row.balance.data.as_slice()); + let telos_nonce = U256::from(account_row.nonce); + let telos_code = account_row.code; + let reth_balance = provider.get_balance(address).await.unwrap(); + + println!("Account: {:?}", address); } \ No newline at end of file From bc434d9ecca11811dcf38e5e11dd99609bd84721 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Sun, 20 Oct 2024 07:49:11 -0700 Subject: [PATCH 14/30] A little more on compare_account function --- crates/telos/node/tests/storage_compare.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index fb976f5d0f2a..8235ef526875 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use alloy_primitives::{Address, U256}; use alloy_provider::{Provider, ProviderBuilder, ReqwestProvider}; +use alloy_rpc_types::BlockId; use antelope::api::client::{APIClient, DefaultProvider}; use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; use antelope::chain::name::Name; @@ -59,12 +60,16 @@ pub async fn compare() { println!("Total account rows: {}", count); } -async fn compare_account(account_row: AccountRow, provider: &ReqwestProvider) { +async fn compare_account(account_row: AccountRow, provider: &ReqwestProvider, at_block: BlockId) { let address = Address::from_slice(account_row.address.data.as_slice()); - let telos_balance = U256::from(account_row.balance.data.as_slice()); + let telos_balance = U256::from_be_slice(account_row.balance.data.as_slice()); let telos_nonce = U256::from(account_row.nonce); let telos_code = account_row.code; - let reth_balance = provider.get_balance(address).await.unwrap(); + + let reth_balance = provider.get_balance(address).block_id(at_block).await.unwrap(); + let reth_nonce = provider.get_transaction_count(address).block_id(at_block).await.unwrap(); + let reth_code = provider.get_code_at(address).block_id(at_block).await.unwrap(); + println!("Account: {:?}", address); } \ No newline at end of file From e93c822d33f0fad6059a6e4facf64b59832cbe80 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Mon, 21 Oct 2024 07:42:02 -0700 Subject: [PATCH 15/30] Almost working storage_compare --- Cargo.lock | 41 ++++++++++-- Cargo.toml | 2 +- crates/telos/node/tests/storage_compare.rs | 76 +++++++++++++++++++--- 3 files changed, 105 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1f315ad01c5..d4fa41e00eae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1255,6 +1255,39 @@ dependencies = [ "tokio", ] +[[package]] +name = "antelope-client" +version = "0.3.0" +source = "git+https://github.com/telosnetwork/antelope-rs#ccd4422e8ce77a57a1f2945785d809348443d327" +dependencies = [ + "antelope-client-macros", + "async-trait", + "base64 0.21.7", + "bs58", + "chrono", + "digest 0.10.7", + "ecdsa", + "flate2", + "hex", + "hmac 0.12.1", + "k256", + "log", + "once_cell", + "p256", + "rand 0.8.5", + "rand_core 0.6.4", + "reqwest 0.11.27", + "ripemd", + "serde", + "serde-big-array", + "serde_json", + "sha2 0.10.8", + "signature", + "thiserror", + "tokio", + "tracing", +] + [[package]] name = "antelope-client-macros" version = "0.2.0" @@ -9392,7 +9425,7 @@ dependencies = [ "alloy-signer-local", "alloy-sol-types", "alloy-transport-http 0.4.2", - "antelope-client", + "antelope-client 0.3.0", "clap", "derive_more 1.0.0", "env_logger 0.11.5", @@ -10575,7 +10608,7 @@ dependencies = [ "alloy-network 0.4.2", "alloy-primitives", "alloy-rpc-types 0.4.2", - "antelope-client", + "antelope-client 0.3.0", "async-trait", "derive_more 1.0.0", "jsonrpsee-types", @@ -12048,7 +12081,7 @@ dependencies = [ "alloy", "alloy-consensus 0.3.6", "alloy-rlp", - "antelope-client", + "antelope-client 0.2.1", "arrowbatch", "base64 0.22.1", "bytes", @@ -12100,7 +12133,7 @@ dependencies = [ "alloy-consensus 0.3.6", "alloy-eips 0.3.6", "alloy-rlp", - "antelope-client", + "antelope-client 0.2.1", "bytes", "clap", "dashmap 5.5.3", diff --git a/Cargo.toml b/Cargo.toml index 710e36b25017..746a9b99303b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -601,7 +601,7 @@ reth-node-telos = { path = "crates/telos/node" } reth-telos-rpc = { path = "crates/telos/rpc" } reth-telos-primitives-traits = { path = "crates/telos/primitives-traits" } reth-telos-rpc-engine-api = { path = "crates/telos/rpc-engine-api" } -antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", branch = "development" } +antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", ref = "1989a54ba5adb1f145c0458dba27f15c2d317446" } [patch.crates-io] #alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index 8235ef526875..393c1f194b09 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -7,7 +7,7 @@ use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; use antelope::chain::name::Name; use antelope::name; use reqwest::Url; -use telos_translator_rs::types::evm_types::AccountRow; +use telos_translator_rs::types::evm_types::{AccountRow, AccountStateRow}; #[tokio::test] pub async fn compare() { @@ -31,7 +31,7 @@ pub async fn compare() { let mut count = 0; while has_more { - let mut query_params = GetTableRowsParams { + let query_params = GetTableRowsParams { code: name!("eosio.evm"), table: name!("account"), scope: None, @@ -48,9 +48,9 @@ pub async fn compare() { has_more = lower_bound.is_some(); for account_row in account_rows.rows { let address = Address::from_slice(account_row.address.data.as_slice()); - println!("Account: {:?}", address); - count += 1; lower_bound = Some(TableIndexType::UINT64(account_row.index + 1)); + compare_account(&account_row, &api_client, &provider, BlockId::from(evm_block_num as u64)).await; + count += 1; } } else { panic!("Failed to fetch account row"); @@ -60,16 +60,74 @@ pub async fn compare() { println!("Total account rows: {}", count); } -async fn compare_account(account_row: AccountRow, provider: &ReqwestProvider, at_block: BlockId) { +async fn compare_account(account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider, at_block: BlockId) { let address = Address::from_slice(account_row.address.data.as_slice()); let telos_balance = U256::from_be_slice(account_row.balance.data.as_slice()); - let telos_nonce = U256::from(account_row.nonce); - let telos_code = account_row.code; let reth_balance = provider.get_balance(address).block_id(at_block).await.unwrap(); let reth_nonce = provider.get_transaction_count(address).block_id(at_block).await.unwrap(); - let reth_code = provider.get_code_at(address).block_id(at_block).await.unwrap(); + let reth_code = provider.get_code_at(address).block_id(at_block).await.unwrap().to_vec(); + + let balance_missmatch = telos_balance != reth_balance; + let nonce_missmatch = account_row.nonce != reth_nonce; + let code_missmatch = account_row.code != reth_code; + + if balance_missmatch || nonce_missmatch || code_missmatch { + println!("Account: {:?}", address); + println!("Telos balance: {:?}", telos_balance); + println!("Telos nonce: {:?}", account_row.nonce); + println!("Telos code: {:?}", account_row.code); + println!("Reth balance: {:?}", reth_balance); + println!("Reth nonce: {:?}", reth_nonce); + println!("Reth code: {:?}", reth_code); + } + + compare_account_storage(account_row, api_client, provider, at_block).await; +} +async fn compare_account_storage(account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider, at_block: BlockId) { + let address = Address::from_slice(account_row.address.data.as_slice()); - println!("Account: {:?}", address); + let mut has_more = true; + let mut lower_bound = Some(TableIndexType::UINT64(0)); + + let mut count = 0; + + while has_more { + let scope = if account_row.index == 0 { + Some(name!("")) + } else { + Some(Name::from_u64(account_row.index)) + }; + let query_params = GetTableRowsParams { + code: name!("eosio.evm"), + table: name!("accountstate"), + scope: Some(scope.unwrap()), + lower_bound, + upper_bound: None, + limit: Some(5000), + reverse: None, + index_position: None, + show_payer: None, + }; + let account_state_rows = api_client.v1_chain.get_table_rows::(query_params).await; + if let Ok(account_state_rows) = account_state_rows { + lower_bound = account_state_rows.next_key; + has_more = lower_bound.is_some(); + for account_state_row in account_state_rows.rows { + let key = U256::from_be_slice(account_state_row.key.data.as_slice()); + let telos_value = U256::from_be_slice(account_state_row.value.data.as_slice()); + let reth_value = provider.get_storage_at(address, key).block_id(at_block).await.unwrap(); + if telos_value != reth_value { + println!("Storage key: {:?}", key); + println!("Telos Storage value: {:?}", telos_value); + println!("Reth storage value: {:?}", reth_value); + } + lower_bound = Some(TableIndexType::UINT64(account_state_row.index + 1)); + count += 1; + } + } else { + panic!("Failed to fetch account state row"); + } + } } \ No newline at end of file From e70287bf7d5736e080f051975781f68ca8ec9496 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Mon, 21 Oct 2024 09:35:30 -0700 Subject: [PATCH 16/30] Fix for Packer trait mismatch with dependency versions, still not working for accountstate table lookup --- crates/telos/node/tests/storage_compare.rs | 25 +++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index 393c1f194b09..e18e66833cb2 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -5,9 +5,28 @@ use alloy_rpc_types::BlockId; use antelope::api::client::{APIClient, DefaultProvider}; use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; use antelope::chain::name::Name; -use antelope::name; +use antelope::{name, StructPacker}; +use antelope::chain::{Encoder, Decoder, Packer}; +use antelope::chain::checksum::{Checksum160, Checksum256}; use reqwest::Url; -use telos_translator_rs::types::evm_types::{AccountRow, AccountStateRow}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize, StructPacker)] +pub struct AccountRow { + pub index: u64, + pub address: Checksum160, + pub account: Name, + pub nonce: u64, + pub code: Vec, + pub balance: Checksum256, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, StructPacker)] +pub struct AccountStateRow { + pub index: u64, + pub key: Checksum256, + pub value: Checksum256, +} #[tokio::test] pub async fn compare() { @@ -22,8 +41,8 @@ pub async fn compare() { let provider = ProviderBuilder::new() .on_http(Url::from_str(evm_rpc).unwrap()); - println!("Telos chain info: {:?}", info); let evm_block_num = info.head_block_num - block_delta; + println!("Telos EVM Block Number: {:?}", evm_block_num); let mut has_more = true; let mut lower_bound = Some(TableIndexType::UINT64(0)); From a1150f05ba24fd8e350a880ecd1dc67383c2f04f Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Mon, 21 Oct 2024 12:13:14 -0700 Subject: [PATCH 17/30] Bumping version of antelope-rs --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 746a9b99303b..420cf747e1a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -601,7 +601,7 @@ reth-node-telos = { path = "crates/telos/node" } reth-telos-rpc = { path = "crates/telos/rpc" } reth-telos-primitives-traits = { path = "crates/telos/primitives-traits" } reth-telos-rpc-engine-api = { path = "crates/telos/rpc-engine-api" } -antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", ref = "1989a54ba5adb1f145c0458dba27f15c2d317446" } +antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", ref = "2c4df386b36a79a7613e899febb3e32334e714dd" } [patch.crates-io] #alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} From e389b4861b151f9cb607d4e08443d0c24b95b4c8 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Mon, 21 Oct 2024 14:54:06 -0700 Subject: [PATCH 18/30] Moving to use latest master of antelope-client --- Cargo.lock | 2 +- Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d4fa41e00eae..caf8b5b1ba8f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1258,7 +1258,7 @@ dependencies = [ [[package]] name = "antelope-client" version = "0.3.0" -source = "git+https://github.com/telosnetwork/antelope-rs#ccd4422e8ce77a57a1f2945785d809348443d327" +source = "git+https://github.com/telosnetwork/antelope-rs?branch=master#fea2203b2edb5cfcedd5365031e5286b47dc5c66" dependencies = [ "antelope-client-macros", "async-trait", diff --git a/Cargo.toml b/Cargo.toml index 420cf747e1a0..62025e2ea230 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -601,7 +601,7 @@ reth-node-telos = { path = "crates/telos/node" } reth-telos-rpc = { path = "crates/telos/rpc" } reth-telos-primitives-traits = { path = "crates/telos/primitives-traits" } reth-telos-rpc-engine-api = { path = "crates/telos/rpc-engine-api" } -antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", ref = "2c4df386b36a79a7613e899febb3e32334e714dd" } +antelope-client = { git = "https://github.com/telosnetwork/antelope-rs", branch = "master" } [patch.crates-io] #alloy-consensus = { git = "https://github.com/alloy-rs/alloy", rev = "8c499409"} From 9fd65d2f152890ccb7b4aebecc1a3dad0f2a7731 Mon Sep 17 00:00:00 2001 From: Amir Pasha Motamed Date: Tue, 22 Oct 2024 18:23:37 +0000 Subject: [PATCH 19/30] Refactor and fix for storage comparision logic --- crates/telos/rpc-engine-api/src/compare.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index aff94d374ead..d063592ab0c9 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -231,9 +231,15 @@ where } if let Ok(revm_row) = revm_db.storage(row.address, row.key) { // The values should match, but if it is removed, then the revm value should be zero - if !(revm_row == row.value) && !(revm_row != U256::ZERO || row.removed == true) { - maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); - state_override.override_storage(revm_db, row.address, row.key, row.value); + if revm_row != row.value { + if revm_row != U256::ZERO && row.removed == true { + maybe_panic!(panic_mode, "Difference in value on revm storage, removed on Telos, non-ZERO on revm, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); + state_override.override_storage(revm_db, row.address, row.key, U256::ZERO); + } + if row.removed == false { + maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); + state_override.override_storage(revm_db, row.address, row.key, row.value); + } } } else { maybe_panic!(panic_mode, "Key was not found on revm storage, address: {:?}, key: {:?}",row.address,row.key); From c1640acad20fd8350523cac514b681babde1696f Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Tue, 22 Oct 2024 13:59:38 -0700 Subject: [PATCH 20/30] Modifications to storage_compare --- crates/telos/node/tests/storage_compare.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index e18e66833cb2..f46336c0605e 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -1,5 +1,5 @@ use std::str::FromStr; -use alloy_primitives::{Address, U256}; +use alloy_primitives::{Address, StorageValue, U256}; use alloy_provider::{Provider, ProviderBuilder, ReqwestProvider}; use alloy_rpc_types::BlockId; use antelope::api::client::{APIClient, DefaultProvider}; @@ -30,9 +30,10 @@ pub struct AccountStateRow { #[tokio::test] pub async fn compare() { - let evm_rpc = "http://localhost:8545"; + let evm_rpc = "http://38.91.106.49:9545"; + // let evm_rpc = "http://localhost:8545"; // let telos_rpc = "http://192.168.0.20:8884"; - let telos_rpc = "http://38.91.106.49:9000"; + let telos_rpc = "http://38.91.106.49:8899"; let block_delta = 57; let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); @@ -135,12 +136,15 @@ async fn compare_account_storage(account_row: &AccountRow, api_client: &APIClien has_more = lower_bound.is_some(); for account_state_row in account_state_rows.rows { let key = U256::from_be_slice(account_state_row.key.data.as_slice()); - let telos_value = U256::from_be_slice(account_state_row.value.data.as_slice()); - let reth_value = provider.get_storage_at(address, key).block_id(at_block).await.unwrap(); + let telos_value: U256 = U256::from_be_slice(account_state_row.value.data.as_slice()); + let reth_value: U256 = provider.get_storage_at(address, key).block_id(at_block).await.unwrap(); if telos_value != reth_value { - println!("Storage key: {:?}", key); + println!("STORAGE MISMATCH!!!"); + println!("Storage account: {:?} with scope: {:?} and key: {:?}", address, scope.unwrap(), key); println!("Telos Storage value: {:?}", telos_value); - println!("Reth storage value: {:?}", reth_value); + println!("Reth storage value: {:?}", reth_value); + } else { + println!(">>>>>>Storage match!!!!!<<<<<<"); } lower_bound = Some(TableIndexType::UINT64(account_state_row.index + 1)); count += 1; From 05e5941312fb0eec68519720a19a95e926c8d715 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Wed, 23 Oct 2024 11:02:22 -0700 Subject: [PATCH 21/30] Cleanup compare function --- crates/telos/node/tests/storage_compare.rs | 85 +++++++++++++++++++--- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index f46336c0605e..e68033b2c04e 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -28,14 +28,71 @@ pub struct AccountStateRow { pub value: Checksum256, } +#[derive(Debug)] +struct MatchCounter { + evm_block_number: BlockId, + total_accounts: u64, + total_storage_rows: u64, + mismatched_accounts: u64, + mismatched_storage_rows: u64, +} + +impl MatchCounter { + pub fn new(evm_block_number: BlockId) -> Self { + Self { + evm_block_number, + total_accounts: 0, + total_storage_rows: 0, + mismatched_accounts: 0, + mismatched_storage_rows: 0, + } + } + + pub fn print(&self) { + println!("Mismatched accounts: {}", self.mismatched_accounts); + println!("Mismatched storage rows: {}", self.mismatched_storage_rows); + println!("Matching accounts: {}", self.total_accounts - self.mismatched_accounts); + println!("Matching storage rows: {}", self.total_storage_rows - self.mismatched_storage_rows); + println!("Total accounts: {}", self.total_accounts); + println!("Total storage rows: {}", self.total_storage_rows); + } + + pub fn add_matching_account(&mut self) { + self.total_accounts += 1; + } + + pub fn add_matching_account_storage(&mut self) { + self.total_storage_rows += 1; + } + + pub fn add_mismatched_account(&mut self) { + self.total_accounts += 1; + self.mismatched_accounts += 1; + } + + pub fn add_mismatched_account_storage(&mut self) { + self.total_storage_rows += 1; + self.mismatched_storage_rows += 1; + } + + pub fn matches(&self) -> bool { + self.mismatched_accounts == 0 && self.mismatched_storage_rows == 0 + } + +} + #[tokio::test] pub async fn compare() { - let evm_rpc = "http://38.91.106.49:9545"; - // let evm_rpc = "http://localhost:8545"; + // let evm_rpc = "http://38.91.106.49:9545"; + let evm_rpc = "http://localhost:8545"; // let telos_rpc = "http://192.168.0.20:8884"; let telos_rpc = "http://38.91.106.49:8899"; let block_delta = 57; + assert!(storage_matches(evm_rpc, telos_rpc, block_delta).await); +} + +pub async fn storage_matches(evm_rpc: &str, telos_rpc: &str, block_delta: u32) -> bool { let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); let info = api_client.v1_chain.get_info().await.unwrap(); @@ -50,6 +107,9 @@ pub async fn compare() { let mut count = 0; + let evm_block_id = BlockId::from(evm_block_num as u64); + let mut match_counter = MatchCounter::new(evm_block_id); + while has_more { let query_params = GetTableRowsParams { code: name!("eosio.evm"), @@ -69,7 +129,7 @@ pub async fn compare() { for account_row in account_rows.rows { let address = Address::from_slice(account_row.address.data.as_slice()); lower_bound = Some(TableIndexType::UINT64(account_row.index + 1)); - compare_account(&account_row, &api_client, &provider, BlockId::from(evm_block_num as u64)).await; + compare_account(&mut match_counter, &account_row, &api_client, &provider).await; count += 1; } } else { @@ -77,10 +137,12 @@ pub async fn compare() { } } - println!("Total account rows: {}", count); + match_counter.print(); + match_counter.matches() } -async fn compare_account(account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider, at_block: BlockId) { +async fn compare_account(match_counter: &mut MatchCounter, account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider) { + let at_block = match_counter.evm_block_number; let address = Address::from_slice(account_row.address.data.as_slice()); let telos_balance = U256::from_be_slice(account_row.balance.data.as_slice()); @@ -93,6 +155,7 @@ async fn compare_account(account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider, at_block: BlockId) { +async fn compare_account_storage(match_counter: &mut MatchCounter, account_row: &AccountRow, api_client: &APIClient, provider: &ReqwestProvider) { let address = Address::from_slice(account_row.address.data.as_slice()); let mut has_more = true; @@ -137,14 +203,15 @@ async fn compare_account_storage(account_row: &AccountRow, api_client: &APIClien for account_state_row in account_state_rows.rows { let key = U256::from_be_slice(account_state_row.key.data.as_slice()); let telos_value: U256 = U256::from_be_slice(account_state_row.value.data.as_slice()); - let reth_value: U256 = provider.get_storage_at(address, key).block_id(at_block).await.unwrap(); + let reth_value: U256 = provider.get_storage_at(address, key).block_id(match_counter.evm_block_number).await.unwrap(); if telos_value != reth_value { + match_counter.add_mismatched_account_storage(); println!("STORAGE MISMATCH!!!"); println!("Storage account: {:?} with scope: {:?} and key: {:?}", address, scope.unwrap(), key); println!("Telos Storage value: {:?}", telos_value); println!("Reth storage value: {:?}", reth_value); } else { - println!(">>>>>>Storage match!!!!!<<<<<<"); + match_counter.add_matching_account_storage(); } lower_bound = Some(TableIndexType::UINT64(account_state_row.index + 1)); count += 1; From fe9843cdb801325b926b0337e35d621243952070 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Wed, 23 Oct 2024 11:05:45 -0700 Subject: [PATCH 22/30] Add block number to print output --- crates/telos/node/tests/storage_compare.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/telos/node/tests/storage_compare.rs b/crates/telos/node/tests/storage_compare.rs index e68033b2c04e..2fc40cf1cd7b 100644 --- a/crates/telos/node/tests/storage_compare.rs +++ b/crates/telos/node/tests/storage_compare.rs @@ -49,6 +49,7 @@ impl MatchCounter { } pub fn print(&self) { + println!("Compared at block: {:?}", self.evm_block_number); println!("Mismatched accounts: {}", self.mismatched_accounts); println!("Mismatched storage rows: {}", self.mismatched_storage_rows); println!("Matching accounts: {}", self.total_accounts - self.mismatched_accounts); From 766632f01d877f658f741247622fb443945f3195 Mon Sep 17 00:00:00 2001 From: Amir Pasha Motamed Date: Wed, 23 Oct 2024 20:57:45 +0000 Subject: [PATCH 23/30] Set correct original_value while overriding storage --- crates/telos/rpc-engine-api/src/compare.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index d063592ab0c9..01eb0e873ccf 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -84,12 +84,12 @@ impl StateOverride { } } - pub fn override_storage (&mut self, revm_db: &mut &mut State, address: Address, key: U256, val: U256) { + pub fn override_storage (&mut self, revm_db: &mut &mut State, address: Address, key: U256, new_val: U256, old_val: U256) { self.maybe_init_account(revm_db, address); let mut acc = self.accounts.get_mut(&address).unwrap(); acc.storage.insert(key, EvmStorageSlot { - original_value: Default::default(), - present_value: val, + original_value: old_val, + present_value: new_val, is_cold: false }); } @@ -234,16 +234,16 @@ where if revm_row != row.value { if revm_row != U256::ZERO && row.removed == true { maybe_panic!(panic_mode, "Difference in value on revm storage, removed on Telos, non-ZERO on revm, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); - state_override.override_storage(revm_db, row.address, row.key, U256::ZERO); + state_override.override_storage(revm_db, row.address, row.key, U256::ZERO, revm_row); } if row.removed == false { maybe_panic!(panic_mode, "Difference in value on revm storage, address: {:?}, key: {:?}, revm-value: {:?}, tevm-row: {:?}", row.address, row.key, revm_row, row); - state_override.override_storage(revm_db, row.address, row.key, row.value); + state_override.override_storage(revm_db, row.address, row.key, row.value, revm_row); } } } else { maybe_panic!(panic_mode, "Key was not found on revm storage, address: {:?}, key: {:?}",row.address,row.key); - state_override.override_storage(revm_db, row.address, row.key, row.value); + state_override.override_storage(revm_db, row.address, row.key, row.value, U256::ZERO); } } From 9fd0003079cbdccad7631eb1a04eb243e9cd3874 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Thu, 24 Oct 2024 21:07:21 -0300 Subject: [PATCH 24/30] Refactor code compraison by size --- crates/telos/rpc-engine-api/src/compare.rs | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 01eb0e873ccf..5e23f5faf4e8 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -175,18 +175,32 @@ where state_override.override_nonce(revm_db, row.address, row.nonce); } // Check code size inequality - if (unwrapped_revm_row.clone().code.is_none() && row.code.len() != 0) || - (unwrapped_revm_row.clone().code.is_some() && !unwrapped_revm_row.clone().code.unwrap().original_bytes().len() != row.code.len()) { - match revm_db.code_by_hash(unwrapped_revm_row.code_hash) { - Ok(code_by_hash) => - if (code_by_hash.is_empty() && row.code.len() != 0) || (!code_by_hash.is_empty() && row.code.len() == 0) { - maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code_by_hash,row.code); - state_override.override_code(revm_db, row.address, Some(row.code.clone())); - }, - Err(_) => { - maybe_panic!(panic_mode, "Difference in code existence (Err while searching by code_hash), address: {:?} - revm: {:?} - tevm: {:?}",row.address,unwrapped_revm_row.code,row.code); + let maybe_revm_code = unwrapped_revm_row.clone().code; + + match maybe_revm_code { + None => { + if row.code.len() != 0 { + maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: None - tevm: {:?}",row.address,row.code); state_override.override_code(revm_db, row.address, Some(row.code.clone())); - }, + } + } + Some(revm_bytecode) => { + match revm_bytecode { + Bytecode::LegacyRaw(code) => { + if code.len() != row.code.len() { + maybe_panic!(panic_mode, "Difference in legacy code size, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code.len(),row.code.len()); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); + } + } + Bytecode::LegacyAnalyzed(code) => { + if code.original_len() != row.code.len() { + maybe_panic!(panic_mode, "Difference in legacy (analyzed) code size, address: {:?} - revm: {:?} - tevm: {:?}",row.address,code.original_len(),row.code.len()); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); + } + } + Bytecode::Eof(_) => panic!("Eof not implemented!"), + Bytecode::Eip7702(_) => panic!("EIP7702 not implemented!") + } } } // // Check code content inequality From ced4d2df409b5b45d1adcb2e9752e3b5dcebd5d0 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Fri, 25 Oct 2024 15:43:31 -0300 Subject: [PATCH 25/30] Add back in search by code hash for case where we hit the de-duplicator --- crates/telos/rpc-engine-api/src/compare.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/telos/rpc-engine-api/src/compare.rs b/crates/telos/rpc-engine-api/src/compare.rs index 5e23f5faf4e8..3597685c2143 100644 --- a/crates/telos/rpc-engine-api/src/compare.rs +++ b/crates/telos/rpc-engine-api/src/compare.rs @@ -180,8 +180,18 @@ where match maybe_revm_code { None => { if row.code.len() != 0 { - maybe_panic!(panic_mode, "Difference in code existence, address: {:?} - revm: None - tevm: {:?}",row.address,row.code); - state_override.override_code(revm_db, row.address, Some(row.code.clone())); + match revm_db.code_by_hash(unwrapped_revm_row.code_hash()) { + Ok(bytecode) => { + if bytecode.len() != row.code.len() { + maybe_panic!(panic_mode, "Difference in code size, address: {:?} - revm: {} - tevm: {}",row.address,bytecode.len(),row.code.len()); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); + } + } + Err(_) => { + maybe_panic!(panic_mode, "Difference in code existence, error while fetching db, address: {:?} - revm: Err - tevm: {}",row.address,row.code.len()); + state_override.override_code(revm_db, row.address, Some(row.code.clone())); + } + } } } Some(revm_bytecode) => { From ae4a3b7d7903b5fffb8893e6ccd9dba6d63de2b6 Mon Sep 17 00:00:00 2001 From: Amir Pasha Motamed Date: Thu, 7 Nov 2024 12:20:15 +0000 Subject: [PATCH 26/30] Add two-way storage compare --- Cargo.lock | 3 + crates/telos/bin/Cargo.toml | 2 + crates/telos/bin/src/main.rs | 35 ++ crates/telos/node/Cargo.toml | 2 + crates/telos/node/src/args.rs | 8 + crates/telos/node/src/lib.rs | 1 + .../telos/node/src/two_way_storage_compare.rs | 360 ++++++++++++++++++ 7 files changed, 411 insertions(+) create mode 100644 crates/telos/node/src/two_way_storage_compare.rs diff --git a/Cargo.lock b/Cargo.lock index caf8b5b1ba8f..312c21631695 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9436,6 +9436,7 @@ dependencies = [ "reth-basic-payload-builder", "reth-beacon-consensus", "reth-chainspec", + "reth-db", "reth-e2e-test-utils", "reth-ethereum-engine-primitives", "reth-ethereum-payload-builder", @@ -12113,10 +12114,12 @@ dependencies = [ name = "telos-reth" version = "1.0.8" dependencies = [ + "alloy-primitives", "clap", "reth", "reth-chainspec", "reth-cli-util", + "reth-db", "reth-node-builder", "reth-node-telos", "reth-provider", diff --git a/crates/telos/bin/Cargo.toml b/crates/telos/bin/Cargo.toml index 462451dfb839..1669f380b311 100644 --- a/crates/telos/bin/Cargo.toml +++ b/crates/telos/bin/Cargo.toml @@ -16,6 +16,8 @@ reth-chainspec.workspace = true reth-provider.workspace = true reth-node-telos.workspace = true reth-telos-rpc.workspace = true +reth-db.workspace = true +alloy-primitives.workspace = true clap = { workspace = true, features = ["derive", "env"] } diff --git a/crates/telos/bin/src/main.rs b/crates/telos/bin/src/main.rs index 6e73d37e15bb..617ad27eadc8 100644 --- a/crates/telos/bin/src/main.rs +++ b/crates/telos/bin/src/main.rs @@ -4,6 +4,7 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); use clap::Parser; +use tracing::{info, warn}; use reth::args::utils::EthereumChainSpecParser; use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; use reth::cli::Cli; @@ -11,6 +12,10 @@ use reth_node_telos::{TelosArgs, TelosNode}; use reth_node_telos::node::TelosAddOns; use reth_provider::providers::BlockchainProvider2; use reth_telos_rpc::TelosClient; +use reth::primitives::BlockId; +use reth::rpc::types::BlockNumberOrTag; +use reth_provider::{DatabaseProviderFactory, StateProviderFactory}; +use reth_db::{PlainAccountState, PlainStorageState}; #[cfg(feature = "telos")] @@ -45,6 +50,10 @@ fn main() { handle.node_exit_future.await }, false => { + let two_way_storage_compare = telos_args.two_way_storage_compare.clone(); + let telos_rpc = telos_args.telos_endpoint.clone(); + let block_delta = telos_args.block_delta.clone(); + let handle = builder .node(TelosNode::new(telos_args.clone())) .extend_rpc_modules(move |ctx| { @@ -59,6 +68,32 @@ fn main() { .launch() .await?; + match two_way_storage_compare { + true => { + if telos_rpc.is_none() { + warn!("Telos RPC Endpoint is not specified, skipping two-way storage compare"); + } else if block_delta.is_none() { + warn!("Block delta is not specified, skipping two-way storage compare"); + } else { + info!("Fetching account and accountstate from Telos native RPC (Can take a long time)..."); + + let (account_table, accountstate_table, block_number) = reth_node_telos::two_way_storage_compare::get_telos_tables(telos_rpc.unwrap().as_str(), block_delta.unwrap()).await; + + info!("Two-way comparing state (Reth vs. Telos) at height: {:?}", block_number); + + let state_at_specific_height = handle.node.provider.state_by_block_id(BlockId::Number(BlockNumberOrTag::Number(block_number.as_u64().unwrap()))).unwrap(); + let plain_account_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); + let plain_storage_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); + + let match_counter = reth_node_telos::two_way_storage_compare::two_side_state_compare(account_table, accountstate_table, state_at_specific_height, plain_account_state, plain_storage_state).await; + match_counter.print(); + + info!("Comparing done"); + } + } + _ => {} + } + handle.node_exit_future.await } } diff --git a/crates/telos/node/Cargo.toml b/crates/telos/node/Cargo.toml index a7e8c74bd718..a530dd01d848 100644 --- a/crates/telos/node/Cargo.toml +++ b/crates/telos/node/Cargo.toml @@ -35,6 +35,8 @@ reth-telos-rpc.workspace = true reth-tracing.workspace = true reth-transaction-pool.workspace = true reth-telos-rpc-engine-api.workspace = true +alloy-primitives.workspace = true +reth-db.workspace = true clap.workspace = true serde = { workspace = true, features = ["derive"] } diff --git a/crates/telos/node/src/args.rs b/crates/telos/node/src/args.rs index f65912512ca2..8c66a55b837d 100644 --- a/crates/telos/node/src/args.rs +++ b/crates/telos/node/src/args.rs @@ -45,6 +45,14 @@ pub struct TelosArgs { /// a batch of downloaded blocks. #[arg(long = "engine.max-execute-block-batch-size", requires = "experimental", default_value_t = DEFAULT_MAX_EXECUTE_BLOCK_BATCH_SIZE)] pub max_execute_block_batch_size: usize, + + /// Enable Two-way storage compare between reth and telos + #[arg(long = "telos.two_way_storage_compare", default_value = "false")] + pub two_way_storage_compare: bool, + + /// Block delta between native and EVM + #[arg(long = "telos.block_delta")] + pub block_delta: Option, } impl From for TelosClientArgs { diff --git a/crates/telos/node/src/lib.rs b/crates/telos/node/src/lib.rs index 962a444aa9c1..3f432da1b519 100644 --- a/crates/telos/node/src/lib.rs +++ b/crates/telos/node/src/lib.rs @@ -11,6 +11,7 @@ pub mod args; pub mod node; +pub mod two_way_storage_compare; pub use crate::args::TelosArgs; pub use crate::node::TelosNode; diff --git a/crates/telos/node/src/two_way_storage_compare.rs b/crates/telos/node/src/two_way_storage_compare.rs new file mode 100644 index 000000000000..a88f1cadf784 --- /dev/null +++ b/crates/telos/node/src/two_way_storage_compare.rs @@ -0,0 +1,360 @@ +//! Two-way storage compare between Reth and Telos + +use antelope::serializer::{Decoder, Encoder, Packer}; +use antelope::chain::name::Name; +use std::collections::HashMap; +use alloy_primitives::{Address, B256, U256}; +use antelope::api::client::{APIClient, DefaultProvider}; +use antelope::api::v1::structs::{GetTableRowsParams, TableIndexType}; +use antelope::{name, StructPacker}; +use antelope::chain::checksum::{Checksum160, Checksum256}; +use serde::{Deserialize, Serialize}; +use tracing::{error, info}; +use reth::primitives::{Account, BlockId}; +use reth_db::common::KeyValue; +use reth::providers::StateProviderBox; +use reth_db::{PlainAccountState, PlainStorageState}; + +#[derive(Debug, Clone, Default, Serialize, Deserialize, StructPacker)] +struct AccountRow { + index: u64, + address: Checksum160, + account: Name, + nonce: u64, + code: Vec, + balance: Checksum256, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, StructPacker)] +struct AccountStateRow { + index: u64, + key: Checksum256, + value: Checksum256, +} + +/// This struct holds matching statistics +#[derive(Debug, Clone, Default)] +pub struct MatchCounter { + total_telos_accounts: u64, + total_telos_storages: u64, + mismatched_telos_accounts: u64, + mismatched_telos_storages: u64, + total_reth_accounts: u64, + total_reth_storages: u64, + mismatched_reth_accounts: u64, + mismatched_reth_storages: u64, +} +impl MatchCounter { + /// Creates a new match counter for two-way storage compare + pub fn new() -> Self { + Self { + total_telos_accounts: 0, + total_telos_storages: 0, + mismatched_telos_accounts: 0, + mismatched_telos_storages: 0, + total_reth_accounts: 0, + total_reth_storages: 0, + mismatched_reth_accounts: 0, + mismatched_reth_storages: 0, + } + } + + /// Prints the match counter + pub fn print(&self) { + info!("Comparing results:"); + info!("Total telos accounts: {}", self.total_telos_accounts); + info!("Total telos storages: {}", self.total_telos_storages); + info!("Mismatched telos accounts: {}", self.mismatched_telos_accounts); + info!("Mismatched telos storages: {}", self.mismatched_telos_storages); + info!("Total reth accounts: {}", self.total_reth_accounts); + info!("Total reth storages: {}", self.total_reth_storages); + info!("Mismatched reth accounts: {}", self.mismatched_reth_accounts); + info!("Mismatched reth storages: {}", self.mismatched_reth_storages); + info!("Matching result: {}",self.matches()); + } + + fn add_telos_total_account(&mut self) { + self.total_telos_accounts += 1; + } + + fn add_telos_total_storage(&mut self) { + self.total_telos_storages += 1; + } + + fn add_telos_mismatched_account(&mut self) { + self.mismatched_telos_accounts += 1; + } + + fn add_telos_mismatched_storage(&mut self) { + self.mismatched_telos_storages += 1; + } + + fn add_reth_total_account(&mut self) { + self.total_reth_accounts += 1; + } + + fn add_reth_total_storage(&mut self) { + self.total_reth_storages += 1; + } + + fn add_reth_mismatched_account(&mut self) { + self.mismatched_reth_accounts += 1; + } + + fn add_reth_mismatched_storage(&mut self) { + self.mismatched_reth_storages += 1; + } + + /// Check whether both sides matches + pub fn matches(&self) -> bool { + self.mismatched_telos_accounts == 0 + && self.mismatched_telos_storages == 0 + && self.mismatched_reth_accounts == 0 + && self.mismatched_reth_storages == 0 + } +} + +/// This function compares reth and telos state against each other at specific height +pub async fn two_side_state_compare( + account_table: HashMap, + accountstate_table: HashMap<(Address, B256), U256>, + state_at_specific_height: StateProviderBox, + plain_account_state: Vec>, + plain_storage_state: Vec>, +) -> MatchCounter { + + let mut match_counter = MatchCounter::new(); + + for (address, telos_account) in &account_table { + let account_at_specific_height = state_at_specific_height.basic_account(*address); + match account_at_specific_height { + Ok(reth_account) => { + match reth_account { + Some(reth_account) => { + if reth_account.balance != telos_account.balance || reth_account.nonce != telos_account.nonce { + match_counter.add_telos_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account); + error!("Reth side: {:?}", reth_account); + } + }, + None => { + if telos_account.balance != U256::ZERO || telos_account.nonce != 0 { + match_counter.add_telos_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account); + error!("Reth side: None"); + } + }, + } + }, + Err(_) => { + match_counter.add_telos_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account); + error!("Reth side: None"); + }, + } + match_counter.add_telos_total_account(); + } + + for ((address, key), telos_value) in &accountstate_table { + let storage_at_specific_height = state_at_specific_height.storage(*address, *key); + match storage_at_specific_height { + Ok(storage) => { + match storage { + Some(reth_value) => { + if reth_value != *telos_value { + match_counter.add_telos_mismatched_storage(); + error!("Difference in accountstate: {:?}, key: {:?}", address, key); + error!("Telos side: {:?}", telos_value); + error!("Reth side: {:?}", reth_value); + } + }, + None => { + match_counter.add_telos_mismatched_storage(); + error!("Difference in accountstate: {:?}, key: {:?}", address, key); + error!("Telos side: {:?}", telos_value); + error!("Reth side: None"); + }, + } + }, + Err(_) => { + match_counter.add_telos_mismatched_storage(); + error!("Difference in accountstate: {:?}, key: {:?}", address, key); + error!("Telos side: {:?}", telos_value); + error!("Reth side: None"); + }, + } + match_counter.add_telos_total_storage(); + } + + + for (address, _) in plain_account_state.iter() { + let account_at_specific_height = state_at_specific_height.basic_account(*address); + let telos_account = account_table.get(address); + match account_at_specific_height { + Ok(account) => { + match account { + Some(reth_account) => { + if telos_account.is_none() { + match_counter.add_reth_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: None"); + error!("Reth side: {:?}", reth_account); + } else { + let telos_account_unwrapped = telos_account.unwrap(); + if reth_account.balance != telos_account_unwrapped.balance || reth_account.nonce != telos_account_unwrapped.nonce { + match_counter.add_reth_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account); + error!("Reth side: {:?}", reth_account); + } + } + + }, + None => { + if telos_account.is_some() { + match_counter.add_reth_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account.unwrap()); + error!("Reth side: None"); + } + }, + } + }, + Err(_) => { + if telos_account.is_some() { + match_counter.add_reth_mismatched_account(); + error!("Difference in account: {:?}", address); + error!("Telos side: {:?}", telos_account.unwrap()); + error!("Reth side: None"); + } + }, + } + match_counter.add_reth_total_account(); + } + + for (address,storage_entry) in plain_storage_state.iter() { + let storage_at_specific_height = state_at_specific_height.storage(*address, storage_entry.key); + let telos_accountstate = accountstate_table.get(&(*address, storage_entry.key)); + match storage_at_specific_height { + Ok(storage) => { + match storage { + Some(reth_value) => { + if telos_accountstate.is_none() { + if reth_value != U256::ZERO { + match_counter.add_reth_mismatched_storage(); + error!("Difference in accountstate: {:?}", address); + error!("Telos side: None"); + error!("Reth side: {:?}", reth_value); + } + } else { + let telos_value = *telos_accountstate.unwrap(); + if reth_value != telos_value { + match_counter.add_reth_mismatched_storage(); + error!("Difference in accountstate: {:?}", address); + error!("Telos side: {:?}", telos_value); + error!("Reth side: {:?}", reth_value); + } + } + }, + None => { + if telos_accountstate.is_some() { + match_counter.add_reth_mismatched_storage(); + error!("Difference in accountstate: {:?}", address); + error!("Telos side: {:?}", telos_accountstate.unwrap()); + error!("Reth side: None"); + } + }, + } + }, + Err(_) => { + if telos_accountstate.is_some() { + match_counter.add_reth_mismatched_storage(); + error!("Difference in accountstate: {:?}", address); + error!("Telos side: {:?}", telos_accountstate.unwrap()); + error!("Reth side: None"); + } + }, + } + match_counter.add_reth_total_storage(); + } + + match_counter +} + +/// This function retrieves account and accountstate tables from native RPC +pub async fn get_telos_tables(telos_rpc: &str, block_delta: u32) -> (HashMap, HashMap<(Address, B256), U256>, BlockId) { + + let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); + let info = api_client.v1_chain.get_info().await.unwrap(); + + let evm_block_num = info.head_block_num - block_delta; + + let mut has_more_account = true; + let mut lower_bound_account = Some(TableIndexType::UINT64(0)); + + let evm_block_id = BlockId::from(evm_block_num as u64); + + let mut account_table = HashMap::default(); + let mut accountstate_table = HashMap::default(); + + while has_more_account { + let query_params_account = GetTableRowsParams { + code: name!("eosio.evm"), + table: name!("account"), + scope: None, + lower_bound: lower_bound_account, + upper_bound: None, + limit: Some(5000), + reverse: None, + index_position: None, + show_payer: None, + }; + let account_rows = api_client.v1_chain.get_table_rows::(query_params_account).await; + if let Ok(account_rows) = account_rows { + lower_bound_account = account_rows.next_key; + has_more_account = lower_bound_account.is_some(); + for account_row in account_rows.rows { + let address = Address::from_slice(account_row.address.data.as_slice()); + lower_bound_account = Some(TableIndexType::UINT64(account_row.index + 1)); + account_table.insert(address,Account { + nonce: account_row.nonce, + balance: U256::from_be_bytes(account_row.balance.data), + bytecode_hash: None, + }); + let mut has_more_accountstate = true; + let mut lower_bound_accountstate = Some(TableIndexType::UINT64(0)); + while has_more_accountstate { + let query_params_accountstate = GetTableRowsParams { + code: name!("eosio.evm"), + table: name!("accountstate"), + scope: Some(Name::from_u64(account_row.index)), + lower_bound: lower_bound_accountstate, + upper_bound: None, + limit: Some(5000), + reverse: None, + index_position: None, + show_payer: None, + }; + let accountstate_rows = api_client.v1_chain.get_table_rows::(query_params_accountstate).await; + if let Ok(accountstate_rows) = accountstate_rows { + lower_bound_accountstate = accountstate_rows.next_key; + has_more_accountstate = lower_bound_accountstate.is_some(); + for accountstate_row in accountstate_rows.rows { + lower_bound_accountstate = Some(TableIndexType::UINT64(accountstate_row.index + 1)); + accountstate_table.insert((address,B256::from(accountstate_row.key.data)),U256::from_be_bytes(accountstate_row.value.data)); + } + } else { + panic!("Failed to fetch accountstate row"); + } + } + } + } else { + panic!("Failed to fetch account row"); + } + } + + (account_table, accountstate_table, evm_block_id) +} \ No newline at end of file From ebfd5f4d1d928bef5f2b65565cdfcfb3c62b6858 Mon Sep 17 00:00:00 2001 From: Guillermo Rodriguez Date: Thu, 7 Nov 2024 16:00:31 -0300 Subject: [PATCH 27/30] Dont call compare function due to bugs on it and new two-way-compare script --- crates/ethereum/evm/src/execute.rs | 36 +++++++++++++++--------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/crates/ethereum/evm/src/execute.rs b/crates/ethereum/evm/src/execute.rs index 3b2bb0d26890..9c3f43237b57 100644 --- a/crates/ethereum/evm/src/execute.rs +++ b/crates/ethereum/evm/src/execute.rs @@ -263,24 +263,24 @@ where new_addresses_using_create_iter.next(); } - #[cfg(feature = "telos")] - { - // Perform state diff comparision - let revm_state_diffs = evm.db_mut().transition_state.clone().unwrap_or_default().transitions; - let block_num = block.block.header.number; - println!( - "Compare: block {block_num} {}", - compare_state_diffs( - &mut evm, - revm_state_diffs, - unwrapped_telos_extra_fields.statediffs_account.clone().unwrap_or_default(), - unwrapped_telos_extra_fields.statediffs_accountstate.clone().unwrap_or_default(), - unwrapped_telos_extra_fields.new_addresses_using_create.clone().unwrap_or_default(), - unwrapped_telos_extra_fields.new_addresses_using_openwallet.clone().unwrap_or_default(), - false - ) - ); - } + // #[cfg(feature = "telos")] + // { + // // Perform state diff comparision + // let revm_state_diffs = evm.db_mut().transition_state.clone().unwrap_or_default().transitions; + // let block_num = block.block.header.number; + // println!( + // "Compare: block {block_num} {}", + // compare_state_diffs( + // &mut evm, + // revm_state_diffs, + // unwrapped_telos_extra_fields.statediffs_account.clone().unwrap_or_default(), + // unwrapped_telos_extra_fields.statediffs_accountstate.clone().unwrap_or_default(), + // unwrapped_telos_extra_fields.new_addresses_using_create.clone().unwrap_or_default(), + // unwrapped_telos_extra_fields.new_addresses_using_openwallet.clone().unwrap_or_default(), + // false + // ) + // ); + // } #[cfg(feature = "telos")] let receipts = if unwrapped_telos_extra_fields.receipts.is_some() { From ca62b6a07790146bd324ba6888f744809f0be064 Mon Sep 17 00:00:00 2001 From: Jesse Schulman Date: Tue, 12 Nov 2024 10:01:39 -0800 Subject: [PATCH 28/30] Fix getTransactionByhash --- crates/telos/rpc/src/eth/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/telos/rpc/src/eth/mod.rs b/crates/telos/rpc/src/eth/mod.rs index fd3227b6a0fa..42e4c8f06aeb 100644 --- a/crates/telos/rpc/src/eth/mod.rs +++ b/crates/telos/rpc/src/eth/mod.rs @@ -26,7 +26,7 @@ use reth_provider::{ BlockIdReader, BlockNumReader, BlockReaderIdExt, ChainSpecProvider, HeaderProvider, StageCheckpointReader, StateProviderFactory, }; -use reth_rpc::eth::{core::EthApiInner, DevSigner}; +use reth_rpc::eth::{core::EthApiInner, DevSigner, EthTxBuilder}; use reth_rpc_eth_api::{ helpers::{ AddDevSigners, EthApiSpec, EthFees, EthSigner, EthState, LoadBlock, LoadFee, LoadState, @@ -104,7 +104,7 @@ where { type Error = TelosEthApiError; type NetworkTypes = AnyNetwork; - type TransactionCompat = (); + type TransactionCompat = EthTxBuilder; } impl EthApiSpec for TelosEthApi From 2e91fe129ee53d702f0d9a2e98a8253a55be04ed Mon Sep 17 00:00:00 2001 From: Amir Pasha Motamed Date: Wed, 13 Nov 2024 15:10:53 +0000 Subject: [PATCH 29/30] Add more checks to two-way storage compare --- crates/telos/bin/src/main.rs | 22 ++++++++++++------- .../telos/node/src/two_way_storage_compare.rs | 13 ++++++++--- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/crates/telos/bin/src/main.rs b/crates/telos/bin/src/main.rs index 617ad27eadc8..7f660ee0ee31 100644 --- a/crates/telos/bin/src/main.rs +++ b/crates/telos/bin/src/main.rs @@ -4,7 +4,7 @@ static ALLOC: reth_cli_util::allocator::Allocator = reth_cli_util::allocator::new_allocator(); use clap::Parser; -use tracing::{info, warn}; +use tracing::{error, info, warn}; use reth::args::utils::EthereumChainSpecParser; use reth_node_builder::{engine_tree_config::TreeConfig, EngineNodeLauncher}; use reth::cli::Cli; @@ -20,6 +20,8 @@ use reth_db::{PlainAccountState, PlainStorageState}; #[cfg(feature = "telos")] fn main() { + use reth_provider::BlockNumReader; + reth_cli_util::sigsegv_handler::install(); // Enable backtraces unless a RUST_BACKTRACE value has already been explicitly provided. @@ -79,16 +81,20 @@ fn main() { let (account_table, accountstate_table, block_number) = reth_node_telos::two_way_storage_compare::get_telos_tables(telos_rpc.unwrap().as_str(), block_delta.unwrap()).await; - info!("Two-way comparing state (Reth vs. Telos) at height: {:?}", block_number); + if block_number.as_u64().unwrap() <= handle.node.provider.best_block_number().unwrap() { + info!("Two-way comparing state (Reth vs. Telos) at height: {:?}", block_number); - let state_at_specific_height = handle.node.provider.state_by_block_id(BlockId::Number(BlockNumberOrTag::Number(block_number.as_u64().unwrap()))).unwrap(); - let plain_account_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); - let plain_storage_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); + let state_at_specific_height = handle.node.provider.state_by_block_id(BlockId::Number(BlockNumberOrTag::Number(block_number.as_u64().unwrap()))).unwrap(); + let plain_account_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); + let plain_storage_state = handle.node.provider.database_provider_ro().unwrap().table::().unwrap(); - let match_counter = reth_node_telos::two_way_storage_compare::two_side_state_compare(account_table, accountstate_table, state_at_specific_height, plain_account_state, plain_storage_state).await; - match_counter.print(); + let match_counter = reth_node_telos::two_way_storage_compare::two_side_state_compare(account_table, accountstate_table, state_at_specific_height, plain_account_state, plain_storage_state).await; + match_counter.print(); - info!("Comparing done"); + info!("Comparing done"); + } else { + error!("Nodeos is ahead of reth, failed to compare state"); + } } } _ => {} diff --git a/crates/telos/node/src/two_way_storage_compare.rs b/crates/telos/node/src/two_way_storage_compare.rs index a88f1cadf784..e39c03d63f6d 100644 --- a/crates/telos/node/src/two_way_storage_compare.rs +++ b/crates/telos/node/src/two_way_storage_compare.rs @@ -288,14 +288,14 @@ pub async fn two_side_state_compare( pub async fn get_telos_tables(telos_rpc: &str, block_delta: u32) -> (HashMap, HashMap<(Address, B256), U256>, BlockId) { let api_client = APIClient::::default_provider(telos_rpc.into(), Some(5)).unwrap(); - let info = api_client.v1_chain.get_info().await.unwrap(); + let info_start = api_client.v1_chain.get_info().await.unwrap(); - let evm_block_num = info.head_block_num - block_delta; + let evm_block_num_start = info_start.head_block_num - block_delta; let mut has_more_account = true; let mut lower_bound_account = Some(TableIndexType::UINT64(0)); - let evm_block_id = BlockId::from(evm_block_num as u64); + let evm_block_id = BlockId::from(evm_block_num_start as u64); let mut account_table = HashMap::default(); let mut accountstate_table = HashMap::default(); @@ -356,5 +356,12 @@ pub async fn get_telos_tables(telos_rpc: &str, block_delta: u32) -> (HashMap Date: Wed, 13 Nov 2024 15:57:43 +0000 Subject: [PATCH 30/30] Fill missing telos args --- crates/telos/node/tests/integration.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/telos/node/tests/integration.rs b/crates/telos/node/tests/integration.rs index 268fde39928c..e6f09a9d715d 100644 --- a/crates/telos/node/tests/integration.rs +++ b/crates/telos/node/tests/integration.rs @@ -160,6 +160,8 @@ async fn testing_chain_sync() { persistence_threshold: 0, memory_block_buffer_target: 1, max_execute_block_batch_size: 100, + two_way_storage_compare: false, + block_delta: None, }; let node_handle = NodeBuilder::new(node_config.clone())