From 45514c600df83e16311c2db76328a7852fa3ee79 Mon Sep 17 00:00:00 2001 From: Pana Date: Mon, 30 Dec 2024 17:06:26 +0800 Subject: [PATCH 1/4] impl eth_call stateoverride feature --- Cargo.lock | 1 + .../consensus_inner/consensus_executor/mod.rs | 50 ++++++++-- crates/cfxcore/core/src/consensus/mod.rs | 12 ++- crates/cfxcore/execute-helper/Cargo.toml | 2 +- crates/cfxcore/executor/Cargo.toml | 1 + .../executor/src/executive/fresh_executive.rs | 4 +- crates/cfxcore/executor/src/executive/mod.rs | 35 ++++++- .../src/state/overlay_account/factory.rs | 3 + .../executor/src/state/overlay_account/mod.rs | 6 ++ .../src/state/overlay_account/storage.rs | 37 +++++++- .../executor/src/state/state_object/mod.rs | 13 ++- .../src/state/state_object/state_override.rs | 91 +++++++++++++++++++ .../client/src/rpc/impls/cfx/cfx_handler.rs | 7 +- .../client/src/rpc/impls/eth/eth_handler.rs | 24 +++-- crates/client/src/rpc/traits/eth_space/eth.rs | 8 +- crates/client/src/rpc/types/eth/mod.rs | 5 +- crates/rpc/rpc-eth-api/src/eth.rs | 19 ++-- crates/rpc/rpc-eth-impl/src/eth.rs | 42 ++++++--- 18 files changed, 302 insertions(+), 58 deletions(-) create mode 100644 crates/cfxcore/executor/src/state/state_object/state_override.rs diff --git a/Cargo.lock b/Cargo.lock index b012d99a3b..d3bf6ddb4e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,6 +1235,7 @@ dependencies = [ "cfx-math", "cfx-parameters", "cfx-parity-trace-types", + "cfx-rpc-eth-types", "cfx-statedb", "cfx-storage", "cfx-types", diff --git a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs index 0da8ddae5c..9f2e34d34e 100644 --- a/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs +++ b/crates/cfxcore/core/src/consensus/consensus_inner/consensus_executor/mod.rs @@ -62,7 +62,7 @@ use cfx_execute_helper::estimation::{ EstimateExt, EstimateRequest, EstimationContext, }; use cfx_executor::{ - executive::ExecutionOutcome, + executive::{ExecutionOutcome, ExecutiveContext}, machine::Machine, state::{ distribute_pos_interest, update_pos_status, CleanupMode, State, @@ -73,6 +73,7 @@ use cfx_vm_types::{Env, Spec}; use geth_tracer::GethTraceWithHash; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; +use cfx_rpc_eth_types::EvmOverrides; use self::epoch_execution::{GethTask, VirtualCall}; @@ -636,9 +637,15 @@ impl ConsensusExecutor { pub fn call_virtual( &self, tx: &SignedTransaction, epoch_id: &H256, epoch_size: usize, - request: EstimateRequest, + request: EstimateRequest, evm_overrides: EvmOverrides, ) -> CoreResult<(ExecutionOutcome, EstimateExt)> { - self.handler.call_virtual(tx, epoch_id, epoch_size, request) + self.handler.call_virtual( + tx, + epoch_id, + epoch_size, + request, + evm_overrides, + ) } pub fn collect_blocks_geth_trace( @@ -1578,7 +1585,7 @@ impl ConsensusExecutionHandler { pub fn call_virtual( &self, tx: &SignedTransaction, epoch_id: &H256, epoch_size: usize, - request: EstimateRequest, + request: EstimateRequest, evm_overrides: EvmOverrides, ) -> CoreResult<(ExecutionOutcome, EstimateExt)> { let best_block_header = self.data_man.block_header_by_hash(epoch_id); if best_block_header.is_none() { @@ -1617,11 +1624,20 @@ impl ConsensusExecutionHandler { Space::Native => None, Space::Ethereum => Some(Space::Ethereum), }; - let mut state = self.get_state_by_epoch_id_and_space( + let statedb = self.get_statedb_by_epoch_id_and_space( epoch_id, best_block_header.height(), state_space, )?; + let mut state = if evm_overrides.has_state() { + State::new_with_override( + statedb, + &evm_overrides.state.as_ref().unwrap(), + tx.space(), + )? + } else { + State::new(statedb)? + }; let time_stamp = best_block_header.timestamp(); @@ -1637,7 +1653,7 @@ impl ConsensusExecutionHandler { let burnt_gas_price = base_gas_price.map_all(|x| state.burnt_gas_price(x)); - let env = Env { + let mut env = Env { chain_id: self.machine.params().chain_id_map(block_height), number: start_block_number, author: miner, @@ -1655,6 +1671,12 @@ impl ConsensusExecutionHandler { base_gas_price, burnt_gas_price, }; + if evm_overrides.has_block() { + ExecutiveContext::apply_env_overrides( + &mut env, + evm_overrides.block.unwrap(), + ); + } let spec = self.machine.spec(env.number, env.epoch_height); let mut ex = EstimationContext::new( &mut state, @@ -1706,6 +1728,19 @@ impl ConsensusExecutionHandler { fn get_state_by_epoch_id_and_space( &self, epoch_id: &H256, epoch_height: u64, state_space: Option, ) -> DbResult { + let state_db = self.get_statedb_by_epoch_id_and_space( + epoch_id, + epoch_height, + state_space, + )?; + let state = State::new(state_db)?; + + Ok(state) + } + + fn get_statedb_by_epoch_id_and_space( + &self, epoch_id: &H256, epoch_height: u64, state_space: Option, + ) -> DbResult { // Keep the lock until we get the desired State, otherwise the State may // expire. let state_availability_boundary = @@ -1734,11 +1769,10 @@ impl ConsensusExecutionHandler { )? .ok_or("state deleted")?, ); - let state = State::new(state_db)?; drop(state_availability_boundary); - Ok(state) + Ok(state_db) } } diff --git a/crates/cfxcore/core/src/consensus/mod.rs b/crates/cfxcore/core/src/consensus/mod.rs index f0b5b33a1e..7abde25379 100644 --- a/crates/cfxcore/core/src/consensus/mod.rs +++ b/crates/cfxcore/core/src/consensus/mod.rs @@ -49,6 +49,7 @@ use cfx_execute_helper::{ use cfx_executor::{ executive::ExecutionOutcome, spec::CommonParams, state::State, }; +use cfx_rpc_eth_types::EvmOverrides; use geth_tracer::GethTraceWithHash; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; @@ -1429,7 +1430,7 @@ impl ConsensusGraph { pub fn call_virtual( &self, tx: &SignedTransaction, epoch: EpochNumber, - request: EstimateRequest, + request: EstimateRequest, evm_overrides: EvmOverrides, ) -> CoreResult<(ExecutionOutcome, EstimateExt)> { // only allow to call against stated epoch self.validate_stated_epoch(&epoch)?; @@ -1440,8 +1441,13 @@ impl ConsensusGraph { } else { bail!("cannot get block hashes in the specified epoch, maybe it does not exist?"); }; - self.executor - .call_virtual(tx, &epoch_id, epoch_size, request) + self.executor.call_virtual( + tx, + &epoch_id, + epoch_size, + request, + evm_overrides, + ) } pub fn collect_epoch_geth_trace( diff --git a/crates/cfxcore/execute-helper/Cargo.toml b/crates/cfxcore/execute-helper/Cargo.toml index 8ee5b9a374..30dcc0597a 100644 --- a/crates/cfxcore/execute-helper/Cargo.toml +++ b/crates/cfxcore/execute-helper/Cargo.toml @@ -30,7 +30,7 @@ serde_json = { workspace = true, default-features = false, features = [ solidity-abi = { workspace = true } strum_macros = { workspace = true } pow-types = { workspace = true } -typemap = { package = "typemap-ors", version = "1.0"} +typemap = { package = "typemap-ors", version = "1.0" } alloy-primitives = { workspace = true } alloy-sol-types = "0.7.1" diff --git a/crates/cfxcore/executor/Cargo.toml b/crates/cfxcore/executor/Cargo.toml index c07adcf8ed..42442118c7 100644 --- a/crates/cfxcore/executor/Cargo.toml +++ b/crates/cfxcore/executor/Cargo.toml @@ -54,6 +54,7 @@ c-kzg = { version = "1.0.2", default-features = false} once_cell = { workspace = true } rayon = { workspace = true } cfx-parity-trace-types = { workspace = true } +cfx-rpc-eth-types = { workspace = true } [dev-dependencies] cfx-statedb = { workspace = true, features = ["testonly_code"]} diff --git a/crates/cfxcore/executor/src/executive/fresh_executive.rs b/crates/cfxcore/executor/src/executive/fresh_executive.rs index a411ed78c2..fb5b792500 100644 --- a/crates/cfxcore/executor/src/executive/fresh_executive.rs +++ b/crates/cfxcore/executor/src/executive/fresh_executive.rs @@ -63,7 +63,9 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> { context: ExecutiveContext<'a>, tx: &'a SignedTransaction, options: TransactOptions, ) -> Self { - let TransactOptions { observer, settings } = options; + let TransactOptions { + observer, settings, .. + } = options; let base_gas = gas_required_for( tx.action() == &Action::Create, &tx.data(), diff --git a/crates/cfxcore/executor/src/executive/mod.rs b/crates/cfxcore/executor/src/executive/mod.rs index 7f1df81f6f..2b257ed139 100644 --- a/crates/cfxcore/executor/src/executive/mod.rs +++ b/crates/cfxcore/executor/src/executive/mod.rs @@ -9,10 +9,11 @@ mod pre_checked_executive; mod tests; pub mod transact_options; +use cfx_rpc_eth_types::BlockOverrides; use cfx_statedb::Result as DbResult; use cfx_types::{ - address_util::AddressUtil, AddressSpaceUtil, AddressWithSpace, Space, H256, - U256, + address_util::AddressUtil, AddressSpaceUtil, AddressWithSpace, Space, + SpaceMap, H256, U256, }; use cfx_vm_types::{CreateContractAddress, Env, Spec}; use primitives::{AccessList, SignedTransaction}; @@ -61,6 +62,36 @@ impl<'a> ExecutiveContext<'a> { Err(execution_outcome) => execution_outcome, }) } + + pub fn apply_env_overrides( + env: &mut Env, block_override: Box, + ) { + if let Some(number) = block_override.number { + env.number = number.as_u64(); + } + if let Some(difficulty) = block_override.difficulty { + env.difficulty = difficulty; + } + if let Some(timestamp) = block_override.time { + env.timestamp = timestamp; + } + if let Some(gas_limit) = block_override.gas_limit { + env.gas_limit = U256::from(gas_limit); + } + if let Some(author) = block_override.coinbase { + env.author = author; + } + if let Some(_random) = block_override.random { + // conflux doesn't have random(prevRandao) + } + if let Some(base_fee) = block_override.base_fee { + env.base_gas_price = SpaceMap::new(base_fee, base_fee); // use same base_fee for both spaces + } + + if let Some(_block_hash) = &block_override.block_hash { + // TODO impl + } + } } pub fn gas_required_for( diff --git a/crates/cfxcore/executor/src/state/overlay_account/factory.rs b/crates/cfxcore/executor/src/state/overlay_account/factory.rs index a915074687..72ec00e19c 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/factory.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/factory.rs @@ -30,6 +30,7 @@ impl Default for OverlayAccount { code: None, is_newly_created_contract: false, pending_db_clear: false, + storage_overrided: false, } } } @@ -157,6 +158,7 @@ impl OverlayAccount { WriteCheckpointLayer::new_empty(checkpoint_id), ), storage_layout_change: self.storage_layout_change.clone(), + storage_overrided: self.storage_overrided, } } @@ -188,6 +190,7 @@ impl OverlayAccount { )), transient_storage_checkpoint: None, storage_layout_change: self.storage_layout_change.clone(), + storage_overrided: self.storage_overrided, } } } diff --git a/crates/cfxcore/executor/src/state/overlay_account/mod.rs b/crates/cfxcore/executor/src/state/overlay_account/mod.rs index 7b4556cbe8..99bf0f058e 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/mod.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/mod.rs @@ -156,6 +156,12 @@ pub struct OverlayAccount { /// cleared later. It will be set when such a contract has been killed /// since last commit. pending_db_clear: bool, + + /// Indicates whether the storage cache entries of this account have been + /// overrided by the passed-in storage entries. + /// When this flag is set, the storage entries will only be read from the + /// cache + storage_overrided: bool, } impl OverlayAccount { diff --git a/crates/cfxcore/executor/src/state/overlay_account/storage.rs b/crates/cfxcore/executor/src/state/overlay_account/storage.rs index 07b14fb68b..d80b647c67 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/storage.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/storage.rs @@ -7,12 +7,12 @@ use cfx_parameters::{ staking::COLLATERAL_UNITS_PER_STORAGE_KEY, }; use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric}; -use cfx_types::{Address, Space, U256}; +use cfx_types::{Address, Space, H256, U256}; use primitives::{ SkipInputCheck, StorageKey, StorageKeyWithSpace, StorageValue, }; -use std::collections::hash_map::Entry::*; +use std::collections::{hash_map::Entry::*, HashMap}; use super::OverlayAccount; @@ -196,9 +196,9 @@ impl OverlayAccount { pub fn storage_entry_at( &self, db: &StateDbGeneric, key: &[u8], ) -> DbResult { - Ok(if let Some(owner) = self.cached_entry_at(key) { - owner - } else if self.fresh_storage() { + Ok(if let Some(value) = self.cached_entry_at(key) { + value + } else if self.fresh_storage() || self.storage_overrided { StorageValue::default() } else { self.get_and_cache_storage(db, key)? @@ -240,6 +240,33 @@ impl OverlayAccount { && self.address.address != SYSTEM_STORAGE_ADDRESS } + // used for state override.diff + pub fn update_storage_read_cache(&mut self, key: Vec, value: U256) { + let owner = if self.address.space == Space::Native { + Some(self.address.address) + } else { + None + }; + let storage_value = StorageValue { owner, value }; + self.storage_read_cache + .write() + .insert(key.to_vec(), storage_value); + } + + pub fn override_storage_read_cache( + &mut self, state: &HashMap, complete_override: bool, + ) { + if complete_override { + self.storage_read_cache.write().clear(); + self.storage_overrided = true; + } + for (key, value) in state.iter() { + let key = key.as_bytes().to_vec(); + let value = U256::from_big_endian(value.as_bytes()); + self.update_storage_read_cache(key, value); + } + } + pub fn change_storage_value( &mut self, db: &StateDbGeneric, key: &[u8], value: U256, ) -> DbResult<()> { diff --git a/crates/cfxcore/executor/src/state/state_object/mod.rs b/crates/cfxcore/executor/src/state/state_object/mod.rs index 3d0cd6c571..be05034840 100644 --- a/crates/cfxcore/executor/src/state/state_object/mod.rs +++ b/crates/cfxcore/executor/src/state/state_object/mod.rs @@ -43,6 +43,8 @@ mod storage_entry; mod reward; +mod state_override; + #[cfg(test)] mod tests; @@ -64,8 +66,9 @@ use super::{ overlay_account::{AccountEntry, OverlayAccount, RequireFields}, }; use crate::substate::Substate; +use cfx_rpc_eth_types::StateOverride; use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric as StateDb}; -use cfx_types::AddressWithSpace; +use cfx_types::{AddressWithSpace, Space}; use parking_lot::RwLock; use std::collections::{BTreeSet, HashMap}; @@ -109,6 +112,14 @@ impl State { }) } + pub fn new_with_override( + db: StateDb, state_override: &StateOverride, space: Space, + ) -> DbResult { + let mut state = Self::new(db)?; + state.apply_override(state_override, space)?; + Ok(state) + } + pub fn prefetch_accounts( &self, addresses: BTreeSet, pool: &rayon::ThreadPool, ) -> DbResult<()> { diff --git a/crates/cfxcore/executor/src/state/state_object/state_override.rs b/crates/cfxcore/executor/src/state/state_object/state_override.rs new file mode 100644 index 0000000000..8fe4604f03 --- /dev/null +++ b/crates/cfxcore/executor/src/state/state_object/state_override.rs @@ -0,0 +1,91 @@ +use std::collections::HashMap; + +use super::State; +use crate::state::{ + overlay_account::{AccountEntry, OverlayAccount, RequireFields}, + CleanupMode, +}; +use cfx_rpc_eth_types::StateOverride; +use cfx_statedb::Result as DbResult; +use cfx_types::{AddressWithSpace, Space, H256, U256}; + +/// Apply the state override to the state object, only used for rpc call eg +/// eth_call, eth_estimateGas etc. +impl State { + pub(super) fn apply_override( + &mut self, state_override: &StateOverride, space: Space, + ) -> DbResult<()> { + assert!(self.checkpoints.read().is_empty()); + + for (address, account) in state_override.iter() { + let addr_with_space = AddressWithSpace { + address: address.to_owned(), + space, + }; + + self.prefetch(&addr_with_space, RequireFields::Code)?; + + // apply the overrides + if let Some(balance) = account.balance { + let mut cleanup_mode = CleanupMode::NoEmpty; + let current_balance = self.balance(&addr_with_space)?; + if current_balance > U256::zero() { + self.sub_balance( + &addr_with_space, + ¤t_balance, + &mut cleanup_mode, + )?; + } + self.add_balance(&addr_with_space, &balance, cleanup_mode)?; + } + + if let Some(nonce) = account.nonce { + self.set_nonce(&addr_with_space, &U256::from(nonce))?; + } + + if let Some(code) = account.code.clone() { + self.init_code(&addr_with_space, code, address.to_owned())?; + } + + match (account.state.clone(), account.state_diff.clone()) { + (Some(state), None) => { + self.override_state(&addr_with_space, &state, true)?; + } + (None, Some(diff)) => { + self.override_state(&addr_with_space, &diff, false)?; + } + (Some(_state), Some(_diff)) => unreachable!(), /* the rpc layer will check this, so it should not happen here */ + (None, None) => {} + } + + if account.move_precompile_to.is_some() { + // TODO: impl move precompile to logic + } + } + Ok(()) + } + + /// Override the storage read cache of an account. + /// If `complete_override` is true, the cache will be replaced by the given + /// state. Otherwise, the cache will be updated by the given state. + fn override_state( + &mut self, address: &AddressWithSpace, state: &HashMap, + complete_override: bool, + ) -> DbResult<()> { + let mut cache = self.cache.write(); + match cache.get_mut(address) { + None | Some(AccountEntry::DbAbsent) => { + let mut overlay_acc = OverlayAccount::default(); + overlay_acc + .override_storage_read_cache(state, complete_override); + cache.insert(*address, AccountEntry::new_dirty(overlay_acc)); + } + Some(acc) => { + let acc = acc.account_mut().unwrap(); + acc.override_storage_read_cache(state, complete_override); + } + } + + Ok(()) + } +} diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index 7851187701..764a97ea3f 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -1504,7 +1504,12 @@ impl RpcImpl { )?; trace!("call tx {:?}", signed_tx); - consensus_graph.call_virtual(&signed_tx, epoch.into(), estimate_request) + consensus_graph.call_virtual( + &signed_tx, + epoch.into(), + estimate_request, + Default::default(), + ) } fn current_sync_phase(&self) -> CoreResult { diff --git a/crates/client/src/rpc/impls/eth/eth_handler.rs b/crates/client/src/rpc/impls/eth/eth_handler.rs index a87d438467..c915e1cdd0 100644 --- a/crates/client/src/rpc/impls/eth/eth_handler.rs +++ b/crates/client/src/rpc/impls/eth/eth_handler.rs @@ -9,8 +9,8 @@ use crate::rpc::{ types::{ eth::{ AccountPendingTransactions, Block as RpcBlock, BlockNumber, - EthRpcLogFilter, Log, Receipt, SyncStatus, Transaction, - TransactionRequest, + BlockOverrides, EthRpcLogFilter, EvmOverrides, Log, Receipt, + StateOverride, SyncStatus, Transaction, TransactionRequest, }, Bytes, FeeHistory, Index, U64 as HexU64, }, @@ -263,14 +263,21 @@ impl Eth for EthHandler { fn call( &self, request: TransactionRequest, block_number_or_hash: Option, + state_overrides: Option, + block_overrides: Option>, ) -> RpcResult { debug!( "RPC Request: eth_call(request={:?}, block_num={:?})", request, block_number_or_hash ); - let (execution, _estimation) = - self.inner.exec_transaction(request, block_number_or_hash)?; + let evm_overrides = EvmOverrides::new(state_overrides, block_overrides); + + let (execution, _estimation) = self.inner.exec_transaction( + request, + block_number_or_hash, + evm_overrides, + )?; Ok(execution.output.into()) } @@ -278,13 +285,18 @@ impl Eth for EthHandler { fn estimate_gas( &self, request: TransactionRequest, block_number_or_hash: Option, + state_override: Option, ) -> RpcResult { debug!( "RPC Request: eth_estimateGas(request={:?}, block_num={:?})", request, block_number_or_hash ); - let (_, estimated_gas) = - self.inner.exec_transaction(request, block_number_or_hash)?; + let evm_overrides = EvmOverrides::new(state_override, None); + let (_, estimated_gas) = self.inner.exec_transaction( + request, + block_number_or_hash, + evm_overrides, + )?; Ok(estimated_gas) } diff --git a/crates/client/src/rpc/traits/eth_space/eth.rs b/crates/client/src/rpc/traits/eth_space/eth.rs index 65701b7832..e4d21fa4de 100644 --- a/crates/client/src/rpc/traits/eth_space/eth.rs +++ b/crates/client/src/rpc/traits/eth_space/eth.rs @@ -26,8 +26,9 @@ use jsonrpc_derive::rpc; use crate::rpc::types::{ eth::{ - AccountPendingTransactions, Block, BlockNumber, EthRpcLogFilter, Log, - Receipt, SyncStatus, Transaction, TransactionRequest, + AccountPendingTransactions, Block, BlockNumber, BlockOverrides, + EthRpcLogFilter, Log, Receipt, StateOverride, SyncStatus, Transaction, + TransactionRequest, }, Bytes, FeeHistory, Index, }; @@ -163,12 +164,15 @@ pub trait Eth { #[rpc(name = "eth_call")] fn call( &self, transaction: TransactionRequest, block: Option, + state_overrides: Option, + block_overrides: Option>, ) -> Result; /// Estimate gas needed for execution of given contract. #[rpc(name = "eth_estimateGas")] fn estimate_gas( &self, transaction: TransactionRequest, block: Option, + state_override: Option, ) -> Result; /// Get transaction by its hash. diff --git a/crates/client/src/rpc/types/eth/mod.rs b/crates/client/src/rpc/types/eth/mod.rs index 4488166769..6b012aad07 100644 --- a/crates/client/src/rpc/types/eth/mod.rs +++ b/crates/client/src/rpc/types/eth/mod.rs @@ -4,8 +4,9 @@ pub use cfx_rpc_eth_types::{ eth_pubsub, trace_filter::TraceFilter, AccountPendingTransactions, Block, - BlockNumber, EthRpcLogFilter, FilterChanges, Header, Log, Receipt, - SyncInfo, SyncStatus, Transaction, TransactionRequest, + BlockNumber, BlockOverrides, EthRpcLogFilter, EvmOverrides, FilterChanges, + Header, Log, Receipt, StateOverride, SyncInfo, SyncStatus, Transaction, + TransactionRequest, }; pub use cfx_rpc_cfx_types::trace_eth::{LocalizedTrace, Res}; diff --git a/crates/rpc/rpc-eth-api/src/eth.rs b/crates/rpc/rpc-eth-api/src/eth.rs index 98023e443b..48596abd4c 100644 --- a/crates/rpc/rpc-eth-api/src/eth.rs +++ b/crates/rpc/rpc-eth-api/src/eth.rs @@ -1,6 +1,7 @@ use cfx_rpc_eth_types::{ - Block, BlockNumber as BlockId, EthRpcLogFilter as Filter, FeeHistory, - Header, Log, Receipt, SyncStatus, Transaction, TransactionRequest, + Block, BlockNumber as BlockId, BlockOverrides, EthRpcLogFilter as Filter, + FeeHistory, Header, Log, Receipt, StateOverride, SyncStatus, Transaction, + TransactionRequest, }; use cfx_rpc_primitives::{Bytes, Index}; use cfx_types::{Address, H256, H64, U256, U64}; @@ -199,11 +200,9 @@ pub trait EthApi { /// on the block chain. #[method(name = "call")] async fn call( - &self, - request: TransactionRequest, - block_number: Option, - // state_overrides: Option, - // block_overrides: Option>, + &self, request: TransactionRequest, block_number: Option, + state_overrides: Option, + block_overrides: Option>, ) -> RpcResult; /// Simulate arbitrary number of transactions at an arbitrary blockchain @@ -243,10 +242,8 @@ pub trait EthApi { /// the transaction to complete. #[method(name = "estimateGas")] async fn estimate_gas( - &self, - request: TransactionRequest, - block_number: Option, - // state_override: Option, + &self, request: TransactionRequest, block_number: Option, + state_override: Option, ) -> RpcResult; /// Returns the current price per gas in wei. diff --git a/crates/rpc/rpc-eth-impl/src/eth.rs b/crates/rpc/rpc-eth-impl/src/eth.rs index 5dfe181faf..e3f9ff1324 100644 --- a/crates/rpc/rpc-eth-impl/src/eth.rs +++ b/crates/rpc/rpc-eth-impl/src/eth.rs @@ -10,9 +10,9 @@ use cfx_rpc_cfx_types::{ }; use cfx_rpc_eth_api::EthApiServer; use cfx_rpc_eth_types::{ - Block, BlockNumber as BlockId, EthRpcLogFilter, EthRpcLogFilter as Filter, - FeeHistory, Header, Log, Receipt, SyncInfo, SyncStatus, Transaction, - TransactionRequest, + Block, BlockNumber as BlockId, BlockOverrides, EthRpcLogFilter, + EthRpcLogFilter as Filter, EvmOverrides, FeeHistory, Header, Log, Receipt, + StateOverride, SyncInfo, SyncStatus, Transaction, TransactionRequest, }; use cfx_rpc_primitives::{Bytes, Index, U64 as HexU64}; use cfx_rpc_utils::error::{ @@ -99,7 +99,7 @@ impl EthApi { pub fn exec_transaction( &self, mut request: TransactionRequest, - block_number_or_hash: Option, + block_number_or_hash: Option, evm_overrides: EvmOverrides, ) -> CoreResult<(Executed, U256)> { let consensus_graph = self.consensus_graph(); @@ -125,6 +125,19 @@ impl EthApi { } } + if evm_overrides.has_state() { + let state_override = evm_overrides.state.as_ref().unwrap(); + let invalid_item = state_override.iter().any(|(_, item)| { + item.state.is_some() && item.state_diff.is_some() + }); + if invalid_item { + return Err(invalid_params_msg( + "Cannot set both state and stateDiff in stateOverride", + ) + .into()); + } + } + let epoch = match block_number_or_hash.unwrap_or_default() { BlockNumber::Hash { hash, .. } => { match consensus_graph.get_block_epoch_number(&hash) { @@ -167,6 +180,7 @@ impl EthApi { &signed_tx, epoch, estimate_request, + evm_overrides, )?; let executed = match execution_outcome { @@ -1236,14 +1250,13 @@ impl EthApiServer for EthApi { /// Executes a new message call immediately without creating a transaction /// on the block chain. async fn call( - &self, - request: TransactionRequest, - block_number: Option, - // state_overrides: Option, - // block_overrides: Option>, + &self, request: TransactionRequest, block_number: Option, + state_overrides: Option, + block_overrides: Option>, ) -> RpcResult { + let evm_overrides = EvmOverrides::new(state_overrides, block_overrides); let (execution, _estimation) = - self.exec_transaction(request, block_number)?; + self.exec_transaction(request, block_number, evm_overrides)?; Ok(execution.output.into()) } @@ -1282,13 +1295,12 @@ impl EthApiServer for EthApi { /// Generates and returns an estimate of how much gas is necessary to allow /// the transaction to complete. async fn estimate_gas( - &self, - request: TransactionRequest, - block_number: Option, - // state_override: Option, + &self, request: TransactionRequest, block_number: Option, + state_override: Option, ) -> RpcResult { + let evm_overrides = EvmOverrides::new(state_override, None); let (_, estimated_gas) = - self.exec_transaction(request, block_number)?; + self.exec_transaction(request, block_number, evm_overrides)?; Ok(estimated_gas) } From beb33fcd58f46e142307dfcfb9467475c73f292a Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 8 Jan 2025 14:36:17 +0800 Subject: [PATCH 2/4] optimize override apply code --- .../state/overlay_account/account_entry.rs | 16 ++++ .../src/state/overlay_account/factory.rs | 48 +++++++++++ .../executor/src/state/overlay_account/mod.rs | 1 + .../src/state/overlay_account/storage.rs | 3 +- .../src/state/state_object/state_override.rs | 79 +++---------------- 5 files changed, 77 insertions(+), 70 deletions(-) diff --git a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs index dade5e6516..b9a5fb33eb 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs @@ -17,6 +17,7 @@ pub enum AccountEntry { } use cfx_parameters::genesis::GENESIS_ACCOUNT_ADDRESS; +use cfx_rpc_eth_types::AccountOverride; use cfx_types::AddressWithSpace; use primitives::Account; use AccountEntry::*; @@ -37,6 +38,21 @@ impl AccountEntry { } } + pub fn from_loaded_and_override( + address: &AddressWithSpace, account: Option, + acc_overrides: &AccountOverride, + ) -> Self { + let acc = account.unwrap_or_else(|| Account::new_empty(address)); + Cached( + OverlayAccount::from_loaded_and_override( + address, + acc, + acc_overrides, + ), + true, + ) + } + pub fn is_dirty(&self) -> bool { matches!(self, Cached(_, true)) } pub fn is_db_absent(&self) -> bool { matches!(self, DbAbsent) } diff --git a/crates/cfxcore/executor/src/state/overlay_account/factory.rs b/crates/cfxcore/executor/src/state/overlay_account/factory.rs index 72ec00e19c..b90a1999e0 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/factory.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/factory.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use cfx_rpc_eth_types::AccountOverride; use cfx_types::{Address, AddressSpaceUtil, AddressWithSpace, Space, U256}; use keccak_hash::KECCAK_EMPTY; use parking_lot::RwLock; @@ -54,6 +55,53 @@ impl OverlayAccount { overlay_account } + pub fn from_loaded_and_override( + address: &AddressWithSpace, account: Account, + acc_overrides: &AccountOverride, + ) -> Self { + let mut acc = Self::from_loaded(address, account); + + if let Some(balance) = acc_overrides.balance { + let curr_balance = acc.balance().clone(); + if curr_balance > U256::zero() { + acc.sub_balance(&curr_balance); + } + acc.add_balance(&balance); + } + + if let Some(nonce) = acc_overrides.nonce { + acc.set_nonce(&U256::from(nonce)); + } + + if let Some(code) = acc_overrides.code.clone() { + acc.init_code(code, address.address); + } + + match ( + acc_overrides.state.clone(), + acc_overrides.state_diff.clone(), + ) { + (Some(state), None) => { + acc.override_storage_read_cache(&state, true); + } + (None, Some(diff)) => { + acc.override_storage_read_cache(&diff, false); + } + (Some(_state), Some(_diff)) => unreachable!(), /* the rpc layer */ + // will check + // this, so it + // should not + // happen here + (None, None) => {} + } + + if acc_overrides.move_precompile_to.is_some() { + // TODO: impl move precompile to logic + } + + acc + } + /// Create an OverlayAccount of basic account when the account doesn't exist /// before. pub fn new_basic(address: &AddressWithSpace, balance: U256) -> Self { diff --git a/crates/cfxcore/executor/src/state/overlay_account/mod.rs b/crates/cfxcore/executor/src/state/overlay_account/mod.rs index 99bf0f058e..550945fabf 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/mod.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/mod.rs @@ -189,6 +189,7 @@ impl OverlayAccount { && self.address.address.is_builtin_address(); (self.is_newly_created_contract && !builtin_address) || self.pending_db_clear + || self.storage_overrided } } diff --git a/crates/cfxcore/executor/src/state/overlay_account/storage.rs b/crates/cfxcore/executor/src/state/overlay_account/storage.rs index d80b647c67..fed4f9e250 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/storage.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/storage.rs @@ -198,7 +198,7 @@ impl OverlayAccount { ) -> DbResult { Ok(if let Some(value) = self.cached_entry_at(key) { value - } else if self.fresh_storage() || self.storage_overrided { + } else if self.fresh_storage() { StorageValue::default() } else { self.get_and_cache_storage(db, key)? @@ -242,6 +242,7 @@ impl OverlayAccount { // used for state override.diff pub fn update_storage_read_cache(&mut self, key: Vec, value: U256) { + assert!(self.storage_write_checkpoint.is_none()); let owner = if self.address.space == Space::Native { Some(self.address.address) } else { diff --git a/crates/cfxcore/executor/src/state/state_object/state_override.rs b/crates/cfxcore/executor/src/state/state_object/state_override.rs index 8fe4604f03..c12c6cd9b6 100644 --- a/crates/cfxcore/executor/src/state/state_object/state_override.rs +++ b/crates/cfxcore/executor/src/state/state_object/state_override.rs @@ -1,13 +1,8 @@ -use std::collections::HashMap; - use super::State; -use crate::state::{ - overlay_account::{AccountEntry, OverlayAccount, RequireFields}, - CleanupMode, -}; +use crate::state::overlay_account::AccountEntry; use cfx_rpc_eth_types::StateOverride; -use cfx_statedb::Result as DbResult; -use cfx_types::{AddressWithSpace, Space, H256, U256}; +use cfx_statedb::{Result as DbResult, StateDbExt}; +use cfx_types::{AddressWithSpace, Space}; /// Apply the state override to the state object, only used for rpc call eg /// eth_call, eth_estimateGas etc. @@ -23,69 +18,15 @@ impl State { space, }; - self.prefetch(&addr_with_space, RequireFields::Code)?; - - // apply the overrides - if let Some(balance) = account.balance { - let mut cleanup_mode = CleanupMode::NoEmpty; - let current_balance = self.balance(&addr_with_space)?; - if current_balance > U256::zero() { - self.sub_balance( - &addr_with_space, - ¤t_balance, - &mut cleanup_mode, - )?; - } - self.add_balance(&addr_with_space, &balance, cleanup_mode)?; - } - - if let Some(nonce) = account.nonce { - self.set_nonce(&addr_with_space, &U256::from(nonce))?; - } + let loaded_account = self.db.get_account(&addr_with_space)?; + let account_entry = AccountEntry::from_loaded_and_override( + &addr_with_space, + loaded_account, + account, + ); - if let Some(code) = account.code.clone() { - self.init_code(&addr_with_space, code, address.to_owned())?; - } - - match (account.state.clone(), account.state_diff.clone()) { - (Some(state), None) => { - self.override_state(&addr_with_space, &state, true)?; - } - (None, Some(diff)) => { - self.override_state(&addr_with_space, &diff, false)?; - } - (Some(_state), Some(_diff)) => unreachable!(), /* the rpc layer will check this, so it should not happen here */ - (None, None) => {} - } - - if account.move_precompile_to.is_some() { - // TODO: impl move precompile to logic - } + self.cache.write().insert(addr_with_space, account_entry); } Ok(()) } - - /// Override the storage read cache of an account. - /// If `complete_override` is true, the cache will be replaced by the given - /// state. Otherwise, the cache will be updated by the given state. - fn override_state( - &mut self, address: &AddressWithSpace, state: &HashMap, - complete_override: bool, - ) -> DbResult<()> { - let mut cache = self.cache.write(); - match cache.get_mut(address) { - None | Some(AccountEntry::DbAbsent) => { - let mut overlay_acc = OverlayAccount::default(); - overlay_acc - .override_storage_read_cache(state, complete_override); - cache.insert(*address, AccountEntry::new_dirty(overlay_acc)); - } - Some(acc) => { - let acc = acc.account_mut().unwrap(); - acc.override_storage_read_cache(state, complete_override); - } - } - - Ok(()) - } } From 306a18917185465f8e01616dc2747016543dca29 Mon Sep 17 00:00:00 2001 From: Pana Date: Wed, 8 Jan 2025 16:53:14 +0800 Subject: [PATCH 3/4] fix review comments --- .../state/overlay_account/account_entry.rs | 4 +-- .../src/state/overlay_account/factory.rs | 25 +++++++--------- .../src/state/overlay_account/storage.rs | 29 ++++++++----------- .../src/state/state_object/state_override.rs | 5 ++-- 4 files changed, 27 insertions(+), 36 deletions(-) diff --git a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs index b9a5fb33eb..f4621f1dc9 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs @@ -38,13 +38,13 @@ impl AccountEntry { } } - pub fn from_loaded_and_override( + pub fn from_loaded_with_override( address: &AddressWithSpace, account: Option, acc_overrides: &AccountOverride, ) -> Self { let acc = account.unwrap_or_else(|| Account::new_empty(address)); Cached( - OverlayAccount::from_loaded_and_override( + OverlayAccount::from_loaded_with_override( address, acc, acc_overrides, diff --git a/crates/cfxcore/executor/src/state/overlay_account/factory.rs b/crates/cfxcore/executor/src/state/overlay_account/factory.rs index b90a1999e0..c23822d91c 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/factory.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/factory.rs @@ -55,14 +55,14 @@ impl OverlayAccount { overlay_account } - pub fn from_loaded_and_override( + pub fn from_loaded_with_override( address: &AddressWithSpace, account: Account, acc_overrides: &AccountOverride, ) -> Self { let mut acc = Self::from_loaded(address, account); if let Some(balance) = acc_overrides.balance { - let curr_balance = acc.balance().clone(); + let curr_balance = *acc.balance(); if curr_balance > U256::zero() { acc.sub_balance(&curr_balance); } @@ -73,26 +73,21 @@ impl OverlayAccount { acc.set_nonce(&U256::from(nonce)); } - if let Some(code) = acc_overrides.code.clone() { - acc.init_code(code, address.address); + if let Some(code) = acc_overrides.code.as_ref() { + acc.init_code(code.clone(), address.address); } match ( - acc_overrides.state.clone(), - acc_overrides.state_diff.clone(), + acc_overrides.state.as_ref(), + acc_overrides.state_diff.as_ref(), ) { - (Some(state), None) => { - acc.override_storage_read_cache(&state, true); + (Some(state_override), None) => { + acc.override_storage_read_cache(state_override, true); } (None, Some(diff)) => { - acc.override_storage_read_cache(&diff, false); + acc.override_storage_read_cache(diff, false); } - (Some(_state), Some(_diff)) => unreachable!(), /* the rpc layer */ - // will check - // this, so it - // should not - // happen here - (None, None) => {} + (_, _) => {} } if acc_overrides.move_precompile_to.is_some() { diff --git a/crates/cfxcore/executor/src/state/overlay_account/storage.rs b/crates/cfxcore/executor/src/state/overlay_account/storage.rs index fed4f9e250..adf84ccefb 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/storage.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/storage.rs @@ -240,31 +240,26 @@ impl OverlayAccount { && self.address.address != SYSTEM_STORAGE_ADDRESS } - // used for state override.diff - pub fn update_storage_read_cache(&mut self, key: Vec, value: U256) { - assert!(self.storage_write_checkpoint.is_none()); - let owner = if self.address.space == Space::Native { - Some(self.address.address) - } else { - None - }; - let storage_value = StorageValue { owner, value }; - self.storage_read_cache - .write() - .insert(key.to_vec(), storage_value); - } - pub fn override_storage_read_cache( - &mut self, state: &HashMap, complete_override: bool, + &mut self, account_storage: &HashMap, + complete_override: bool, ) { if complete_override { self.storage_read_cache.write().clear(); self.storage_overrided = true; } - for (key, value) in state.iter() { + assert!(self.storage_write_checkpoint.is_none()); + for (key, value) in account_storage { let key = key.as_bytes().to_vec(); let value = U256::from_big_endian(value.as_bytes()); - self.update_storage_read_cache(key, value); + // self.update_storage_read_cache(key, value); + let owner = if self.address.space == Space::Native { + Some(self.address.address) + } else { + None + }; + let storage_value = StorageValue { owner, value }; + self.storage_read_cache.write().insert(key, storage_value); } } diff --git a/crates/cfxcore/executor/src/state/state_object/state_override.rs b/crates/cfxcore/executor/src/state/state_object/state_override.rs index c12c6cd9b6..99d9e718ab 100644 --- a/crates/cfxcore/executor/src/state/state_object/state_override.rs +++ b/crates/cfxcore/executor/src/state/state_object/state_override.rs @@ -12,6 +12,7 @@ impl State { ) -> DbResult<()> { assert!(self.checkpoints.read().is_empty()); + let mut cache = self.cache.write(); for (address, account) in state_override.iter() { let addr_with_space = AddressWithSpace { address: address.to_owned(), @@ -19,13 +20,13 @@ impl State { }; let loaded_account = self.db.get_account(&addr_with_space)?; - let account_entry = AccountEntry::from_loaded_and_override( + let account_entry = AccountEntry::from_loaded_with_override( &addr_with_space, loaded_account, account, ); - self.cache.write().insert(addr_with_space, account_entry); + cache.insert(addr_with_space, account_entry); } Ok(()) } From b6d0752ef43e3b83d87d33a59a9b407b48d32e33 Mon Sep 17 00:00:00 2001 From: Pana Date: Fri, 10 Jan 2025 16:31:47 +0800 Subject: [PATCH 4/4] optimize code --- .../state/overlay_account/account_entry.rs | 16 ---- .../src/state/overlay_account/factory.rs | 43 --------- .../executor/src/state/overlay_account/mod.rs | 2 + .../state/overlay_account/state_override.rs | 90 +++++++++++++++++++ .../src/state/overlay_account/storage.rs | 27 +----- .../executor/src/state/state_object/mod.rs | 11 +-- .../src/state/state_object/state_override.rs | 12 ++- 7 files changed, 105 insertions(+), 96 deletions(-) create mode 100644 crates/cfxcore/executor/src/state/overlay_account/state_override.rs diff --git a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs index f4621f1dc9..dade5e6516 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/account_entry.rs @@ -17,7 +17,6 @@ pub enum AccountEntry { } use cfx_parameters::genesis::GENESIS_ACCOUNT_ADDRESS; -use cfx_rpc_eth_types::AccountOverride; use cfx_types::AddressWithSpace; use primitives::Account; use AccountEntry::*; @@ -38,21 +37,6 @@ impl AccountEntry { } } - pub fn from_loaded_with_override( - address: &AddressWithSpace, account: Option, - acc_overrides: &AccountOverride, - ) -> Self { - let acc = account.unwrap_or_else(|| Account::new_empty(address)); - Cached( - OverlayAccount::from_loaded_with_override( - address, - acc, - acc_overrides, - ), - true, - ) - } - pub fn is_dirty(&self) -> bool { matches!(self, Cached(_, true)) } pub fn is_db_absent(&self) -> bool { matches!(self, DbAbsent) } diff --git a/crates/cfxcore/executor/src/state/overlay_account/factory.rs b/crates/cfxcore/executor/src/state/overlay_account/factory.rs index c23822d91c..72ec00e19c 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/factory.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/factory.rs @@ -1,6 +1,5 @@ use std::sync::Arc; -use cfx_rpc_eth_types::AccountOverride; use cfx_types::{Address, AddressSpaceUtil, AddressWithSpace, Space, U256}; use keccak_hash::KECCAK_EMPTY; use parking_lot::RwLock; @@ -55,48 +54,6 @@ impl OverlayAccount { overlay_account } - pub fn from_loaded_with_override( - address: &AddressWithSpace, account: Account, - acc_overrides: &AccountOverride, - ) -> Self { - let mut acc = Self::from_loaded(address, account); - - if let Some(balance) = acc_overrides.balance { - let curr_balance = *acc.balance(); - if curr_balance > U256::zero() { - acc.sub_balance(&curr_balance); - } - acc.add_balance(&balance); - } - - if let Some(nonce) = acc_overrides.nonce { - acc.set_nonce(&U256::from(nonce)); - } - - if let Some(code) = acc_overrides.code.as_ref() { - acc.init_code(code.clone(), address.address); - } - - match ( - acc_overrides.state.as_ref(), - acc_overrides.state_diff.as_ref(), - ) { - (Some(state_override), None) => { - acc.override_storage_read_cache(state_override, true); - } - (None, Some(diff)) => { - acc.override_storage_read_cache(diff, false); - } - (_, _) => {} - } - - if acc_overrides.move_precompile_to.is_some() { - // TODO: impl move precompile to logic - } - - acc - } - /// Create an OverlayAccount of basic account when the account doesn't exist /// before. pub fn new_basic(address: &AddressWithSpace, balance: U256) -> Self { diff --git a/crates/cfxcore/executor/src/state/overlay_account/mod.rs b/crates/cfxcore/executor/src/state/overlay_account/mod.rs index 550945fabf..efbb12d423 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/mod.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/mod.rs @@ -45,6 +45,8 @@ mod storage; mod checkpoints; +mod state_override; + #[cfg(test)] mod tests; diff --git a/crates/cfxcore/executor/src/state/overlay_account/state_override.rs b/crates/cfxcore/executor/src/state/overlay_account/state_override.rs new file mode 100644 index 0000000000..2977439c87 --- /dev/null +++ b/crates/cfxcore/executor/src/state/overlay_account/state_override.rs @@ -0,0 +1,90 @@ +use super::{AccountEntry, OverlayAccount}; +use cfx_rpc_eth_types::AccountOverride; +use cfx_types::{AddressWithSpace, Space, H256, U256}; +use primitives::{Account, StorageValue}; +use std::{collections::HashMap, sync::Arc}; + +impl AccountEntry { + pub fn from_loaded_with_override( + address: &AddressWithSpace, account: Option, + acc_overrides: &AccountOverride, + ) -> Self { + let acc = account.unwrap_or_else(|| Account::new_empty(address)); + AccountEntry::Cached( + OverlayAccount::from_loaded_with_override( + address, + acc, + acc_overrides, + ), + true, + ) + } +} + +impl OverlayAccount { + pub fn from_loaded_with_override( + address: &AddressWithSpace, account: Account, + acc_overrides: &AccountOverride, + ) -> Self { + let mut acc = Self::from_loaded(address, account); + + if let Some(balance) = acc_overrides.balance { + let curr_balance = *acc.balance(); + if curr_balance > U256::zero() { + acc.sub_balance(&curr_balance); + } + acc.add_balance(&balance); + } + + if let Some(nonce) = acc_overrides.nonce { + acc.set_nonce(&U256::from(nonce)); + } + + if let Some(code) = acc_overrides.code.as_ref() { + acc.init_code(code.clone(), address.address); + } + + match ( + acc_overrides.state.as_ref(), + acc_overrides.state_diff.as_ref(), + ) { + (Some(state_override), None) => { + acc.override_storage_read_cache(state_override, true); + } + (None, Some(diff)) => { + acc.override_storage_read_cache(diff, false); + } + (_, _) => {} + } + + if acc_overrides.move_precompile_to.is_some() { + // TODO: impl move precompile to logic + } + + acc + } + + fn override_storage_read_cache( + &mut self, account_storage: &HashMap, + complete_override: bool, + ) { + assert!(self.storage_write_checkpoint.is_none()); + + self.storage_overrided = complete_override; + + let read_cache = Arc::get_mut(&mut self.storage_read_cache) + .expect("override should happen when no checkpoint") + .get_mut(); + for (key, value) in account_storage { + let key = key.as_bytes().to_vec(); + let value = U256::from_big_endian(value.as_bytes()); + let owner = if self.address.space == Space::Native { + Some(self.address.address) + } else { + None + }; + let storage_value = StorageValue { owner, value }; + read_cache.insert(key, storage_value); + } + } +} diff --git a/crates/cfxcore/executor/src/state/overlay_account/storage.rs b/crates/cfxcore/executor/src/state/overlay_account/storage.rs index adf84ccefb..73d0bc8cc3 100644 --- a/crates/cfxcore/executor/src/state/overlay_account/storage.rs +++ b/crates/cfxcore/executor/src/state/overlay_account/storage.rs @@ -7,12 +7,12 @@ use cfx_parameters::{ staking::COLLATERAL_UNITS_PER_STORAGE_KEY, }; use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric}; -use cfx_types::{Address, Space, H256, U256}; +use cfx_types::{Address, Space, U256}; use primitives::{ SkipInputCheck, StorageKey, StorageKeyWithSpace, StorageValue, }; -use std::collections::{hash_map::Entry::*, HashMap}; +use std::collections::hash_map::Entry::*; use super::OverlayAccount; @@ -240,29 +240,6 @@ impl OverlayAccount { && self.address.address != SYSTEM_STORAGE_ADDRESS } - pub fn override_storage_read_cache( - &mut self, account_storage: &HashMap, - complete_override: bool, - ) { - if complete_override { - self.storage_read_cache.write().clear(); - self.storage_overrided = true; - } - assert!(self.storage_write_checkpoint.is_none()); - for (key, value) in account_storage { - let key = key.as_bytes().to_vec(); - let value = U256::from_big_endian(value.as_bytes()); - // self.update_storage_read_cache(key, value); - let owner = if self.address.space == Space::Native { - Some(self.address.address) - } else { - None - }; - let storage_value = StorageValue { owner, value }; - self.storage_read_cache.write().insert(key, storage_value); - } - } - pub fn change_storage_value( &mut self, db: &StateDbGeneric, key: &[u8], value: U256, ) -> DbResult<()> { diff --git a/crates/cfxcore/executor/src/state/state_object/mod.rs b/crates/cfxcore/executor/src/state/state_object/mod.rs index be05034840..f0ec51da1f 100644 --- a/crates/cfxcore/executor/src/state/state_object/mod.rs +++ b/crates/cfxcore/executor/src/state/state_object/mod.rs @@ -66,9 +66,8 @@ use super::{ overlay_account::{AccountEntry, OverlayAccount, RequireFields}, }; use crate::substate::Substate; -use cfx_rpc_eth_types::StateOverride; use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric as StateDb}; -use cfx_types::{AddressWithSpace, Space}; +use cfx_types::AddressWithSpace; use parking_lot::RwLock; use std::collections::{BTreeSet, HashMap}; @@ -112,14 +111,6 @@ impl State { }) } - pub fn new_with_override( - db: StateDb, state_override: &StateOverride, space: Space, - ) -> DbResult { - let mut state = Self::new(db)?; - state.apply_override(state_override, space)?; - Ok(state) - } - pub fn prefetch_accounts( &self, addresses: BTreeSet, pool: &rayon::ThreadPool, ) -> DbResult<()> { diff --git a/crates/cfxcore/executor/src/state/state_object/state_override.rs b/crates/cfxcore/executor/src/state/state_object/state_override.rs index 99d9e718ab..653a1a9ab7 100644 --- a/crates/cfxcore/executor/src/state/state_object/state_override.rs +++ b/crates/cfxcore/executor/src/state/state_object/state_override.rs @@ -1,13 +1,21 @@ use super::State; use crate::state::overlay_account::AccountEntry; use cfx_rpc_eth_types::StateOverride; -use cfx_statedb::{Result as DbResult, StateDbExt}; +use cfx_statedb::{Result as DbResult, StateDbExt, StateDbGeneric as StateDb}; use cfx_types::{AddressWithSpace, Space}; /// Apply the state override to the state object, only used for rpc call eg /// eth_call, eth_estimateGas etc. impl State { - pub(super) fn apply_override( + pub fn new_with_override( + db: StateDb, state_override: &StateOverride, space: Space, + ) -> DbResult { + let mut state = Self::new(db)?; + state.apply_override(state_override, space)?; + Ok(state) + } + + fn apply_override( &mut self, state_override: &StateOverride, space: Space, ) -> DbResult<()> { assert!(self.checkpoints.read().is_empty());