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 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
16 changes: 16 additions & 0 deletions crates/cfxcore/executor/src/state/overlay_account/account_entry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;
Expand All @@ -37,6 +38,21 @@ impl AccountEntry {
}
}

pub fn from_loaded_and_override(
address: &AddressWithSpace, account: Option<Account>,
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) }
Expand Down
51 changes: 51 additions & 0 deletions crates/cfxcore/executor/src/state/overlay_account/factory.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -30,6 +31,7 @@ impl Default for OverlayAccount {
code: None,
is_newly_created_contract: false,
pending_db_clear: false,
storage_overrided: false,
}
}
}
Expand All @@ -53,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 {
Expand Down Expand Up @@ -157,6 +206,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 +238,7 @@ impl OverlayAccount {
)),
transient_storage_checkpoint: None,
storage_layout_change: self.storage_layout_change.clone(),
storage_overrided: self.storage_overrided,
}
}
}
Expand Down
7 changes: 7 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 Expand Up @@ -183,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
}
}

Expand Down
Loading