Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Impl eth_call state override feature #3027

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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};

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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();

Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -1706,6 +1728,19 @@ impl ConsensusExecutionHandler {
fn get_state_by_epoch_id_and_space(
&self, epoch_id: &H256, epoch_height: u64, state_space: Option<Space>,
) -> DbResult<State> {
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<Space>,
) -> DbResult<StateDb> {
// Keep the lock until we get the desired State, otherwise the State may
// expire.
let state_availability_boundary =
Expand Down Expand Up @@ -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)
}
}

Expand Down
12 changes: 9 additions & 3 deletions crates/cfxcore/core/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?;
Expand All @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion crates/cfxcore/execute-helper/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions crates/cfxcore/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]}
Expand Down
4 changes: 3 additions & 1 deletion crates/cfxcore/executor/src/executive/fresh_executive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ impl<'a, O: ExecutiveObserver> FreshExecutive<'a, O> {
context: ExecutiveContext<'a>, tx: &'a SignedTransaction,
options: TransactOptions<O>,
) -> Self {
let TransactOptions { observer, settings } = options;
let TransactOptions {
observer, settings, ..
} = options;
let base_gas = gas_required_for(
tx.action() == &Action::Create,
&tx.data(),
Expand Down
35 changes: 33 additions & 2 deletions crates/cfxcore/executor/src/executive/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -61,6 +62,36 @@ impl<'a> ExecutiveContext<'a> {
Err(execution_outcome) => execution_outcome,
})
}

pub fn apply_env_overrides(
env: &mut Env, block_override: Box<BlockOverrides>,
) {
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(
Expand Down
3 changes: 3 additions & 0 deletions crates/cfxcore/executor/src/state/overlay_account/factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ impl Default for OverlayAccount {
code: None,
is_newly_created_contract: false,
pending_db_clear: false,
storage_overrided: false,
}
}
}
Expand Down Expand Up @@ -157,6 +158,7 @@ impl OverlayAccount {
WriteCheckpointLayer::new_empty(checkpoint_id),
),
storage_layout_change: self.storage_layout_change.clone(),
storage_overrided: self.storage_overrided,
}
}

Expand Down Expand Up @@ -188,6 +190,7 @@ impl OverlayAccount {
)),
transient_storage_checkpoint: None,
storage_layout_change: self.storage_layout_change.clone(),
storage_overrided: self.storage_overrided,
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/cfxcore/executor/src/state/overlay_account/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
37 changes: 32 additions & 5 deletions crates/cfxcore/executor/src/state/overlay_account/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -196,9 +196,9 @@ impl OverlayAccount {
pub fn storage_entry_at(
&self, db: &StateDbGeneric, key: &[u8],
) -> DbResult<StorageValue> {
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)?
Expand Down Expand Up @@ -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<u8>, 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<H256, H256>, 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<()> {
Expand Down
13 changes: 12 additions & 1 deletion crates/cfxcore/executor/src/state/state_object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ mod storage_entry;

mod reward;

mod state_override;

#[cfg(test)]
mod tests;

Expand All @@ -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};

Expand Down Expand Up @@ -109,6 +112,14 @@ impl State {
})
}

pub fn new_with_override(
db: StateDb, state_override: &StateOverride, space: Space,
) -> DbResult<Self> {
let mut state = Self::new(db)?;
state.apply_override(state_override, space)?;
Ok(state)
}

pub fn prefetch_accounts(
&self, addresses: BTreeSet<AddressWithSpace>, pool: &rayon::ThreadPool,
) -> DbResult<()> {
Expand Down
Loading