diff --git a/Cargo.lock b/Cargo.lock index 79e5c93d8d..d968c3d9f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1287,6 +1287,7 @@ dependencies = [ "cfx-parity-trace-types", "cfx-statedb", "cfx-types", + "cfx-vm-interpreter", "cfx-vm-tracer-derive", "cfx-vm-types", "geth-tracer", @@ -1762,6 +1763,7 @@ dependencies = [ "malloc_size_of", "memory-cache", "parking_lot 0.12.1", + "primitives", "rustc-hex", ] diff --git a/crates/cfx_types/src/contract_address.rs b/crates/cfx_types/src/contract_address.rs index cdba545803..98a98f2a44 100644 --- a/crates/cfx_types/src/contract_address.rs +++ b/crates/cfx_types/src/contract_address.rs @@ -1,4 +1,6 @@ -use super::{Address, H256, U256}; +use super::{ + Address, AddressSpaceUtil, AddressUtil, AddressWithSpace, Space, H256, U256, +}; use keccak_hash::keccak; use rlp::RlpStream; @@ -86,3 +88,20 @@ pub fn cal_contract_address( }; return (address, code_hash); } + +pub fn cal_contract_address_with_space( + address_scheme: CreateContractAddressType, block_number: u64, + sender: &AddressWithSpace, nonce: &U256, code: &[u8], +) -> (AddressWithSpace, H256) { + let (mut address, code_hash) = cal_contract_address( + address_scheme, + block_number, + &sender.address, + nonce, + code, + ); + if sender.space == Space::Native { + address.set_contract_type_bits(); + } + (address.with_space(sender.space), code_hash) +} diff --git a/crates/cfx_types/src/lib.rs b/crates/cfx_types/src/lib.rs index 3324fbf8bf..62b0fe5521 100644 --- a/crates/cfx_types/src/lib.rs +++ b/crates/cfx_types/src/lib.rs @@ -20,12 +20,16 @@ mod utils; pub use utils::*; pub mod address_util; +pub use address_util::AddressUtil; pub mod space_util; pub use space_util::AddressSpaceUtil; pub mod contract_address; -pub use contract_address::{cal_contract_address, CreateContractAddressType}; +pub use contract_address::{ + cal_contract_address, cal_contract_address_with_space, + CreateContractAddressType, +}; /// The KECCAK hash of an empty bloom filter (0x00 * 256) pub const KECCAK_EMPTY_BLOOM: H256 = H256([ diff --git a/crates/cfx_types/src/utils.rs b/crates/cfx_types/src/utils.rs index 6391b144c3..3aeac70f46 100644 --- a/crates/cfx_types/src/utils.rs +++ b/crates/cfx_types/src/utils.rs @@ -42,3 +42,12 @@ pub fn u256_to_h256_be(value: U256) -> H256 { pub fn h256_to_u256_be(value: H256) -> U256 { U256::from_big_endian(value.as_bytes()) } + +/// Creates an Ethereum address from an EVM word's upper 20 bytes +pub fn u256_to_address_be(value: U256) -> Address { + let mut buf = [0u8; 32]; + value.to_big_endian(&mut buf); + let mut addr_bytes: [u8; 20] = [0u8; 20]; + addr_bytes.copy_from_slice(&buf[12..]); + Address::from(addr_bytes) +} diff --git a/crates/cfxcore/core/src/genesis_block.rs b/crates/cfxcore/core/src/genesis_block.rs index 7059c05022..79ef9e1657 100644 --- a/crates/cfxcore/core/src/genesis_block.rs +++ b/crates/cfxcore/core/src/genesis_block.rs @@ -27,8 +27,9 @@ use cfx_parameters::{ use cfx_statedb::StateDb; use cfx_storage::{StorageManager, StorageManagerTrait}; use cfx_types::{ - address_util::AddressUtil, Address, AddressSpaceUtil, AddressWithSpace, - Space, H256, U256, + address_util::AddressUtil, cal_contract_address_with_space, Address, + AddressSpaceUtil, AddressWithSpace, CreateContractAddressType, Space, H256, + U256, }; use diem_crypto::{ bls::BLSPrivateKey, ec_vrf::EcVrfPublicKey, PrivateKey, ValidCryptoMaterial, @@ -41,13 +42,11 @@ use secret_store::SecretStore; use crate::verification::{compute_receipts_root, compute_transaction_root}; use cfx_executor::{ - executive::{ - contract_address, ExecutionOutcome, ExecutiveContext, TransactOptions, - }, + executive::{ExecutionOutcome, ExecutiveContext, TransactOptions}, machine::Machine, state::State, }; -use cfx_vm_types::{CreateContractAddress, Env}; +use cfx_vm_types::Env; use diem_types::account_address::AccountAddress; use primitives::transaction::native_transaction::NativeTransaction; @@ -323,8 +322,8 @@ pub fn genesis_block( machine.clone(), ); - let (contract_address, _) = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let (contract_address, _) = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, 0, &genesis_account_address, &(i - 1).into(), diff --git a/crates/client/src/rpc/impls/cfx/cfx_handler.rs b/crates/client/src/rpc/impls/cfx/cfx_handler.rs index 4a3f6a5fe6..9f6f5f7d8a 100644 --- a/crates/client/src/rpc/impls/cfx/cfx_handler.rs +++ b/crates/client/src/rpc/impls/cfx/cfx_handler.rs @@ -1468,6 +1468,7 @@ impl RpcImpl { has_gas_price: request.has_gas_price(), has_nonce: request.nonce.is_some(), has_storage_limit: request.storage_limit.is_some(), + collect_access_list: false, }; let epoch_height = consensus_graph diff --git a/crates/client/src/rpc/types/cfx/receipt.rs b/crates/client/src/rpc/types/cfx/receipt.rs index b480a37be8..2789b8f3b3 100644 --- a/crates/client/src/rpc/types/cfx/receipt.rs +++ b/crates/client/src/rpc/types/cfx/receipt.rs @@ -5,10 +5,10 @@ use crate::rpc::types::{Log, RpcAddress}; use cfx_addr::Network; use cfx_types::{ - address_util::AddressUtil, Bloom, Space, SpaceMap, H256, U256, U64, + address_util::AddressUtil, cal_contract_address, Bloom, + CreateContractAddressType, Space, SpaceMap, H256, U256, U64, }; use cfx_util_macros::bail; -use cfx_vm_types::{contract_address, CreateContractAddress}; use primitives::{ receipt::{ Receipt as PrimitiveReceipt, StorageChange as PrimitiveStorageChange, @@ -120,8 +120,8 @@ impl Receipt { if Action::Create == unsigned.action() && outcome_status == TransactionStatus::Success { - let (mut created_address, _) = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let (mut created_address, _) = cal_contract_address( + CreateContractAddressType::FromSenderNonceAndCodeHash, block_number.into(), &transaction.sender, unsigned.nonce(), @@ -142,8 +142,8 @@ impl Receipt { if Action::Create == unsigned.action() && outcome_status == TransactionStatus::Success { - let (created_address, _) = contract_address( - CreateContractAddress::FromSenderNonce, + let (created_address, _) = cal_contract_address( + CreateContractAddressType::FromSenderNonce, 0, &transaction.sender, unsigned.nonce(), diff --git a/crates/execution/execute-helper/Cargo.toml b/crates/execution/execute-helper/Cargo.toml index 1df0383681..dccb9e6d99 100644 --- a/crates/execution/execute-helper/Cargo.toml +++ b/crates/execution/execute-helper/Cargo.toml @@ -12,6 +12,7 @@ cfx-statedb = { workspace = true } cfx-vm-tracer-derive = { workspace = true } cfx-types = { workspace = true } cfx-vm-types = { workspace = true } +cfx-vm-interpreter = { workspace = true } cfx-executor = { workspace = true } log = { workspace = true } primitives = { workspace = true } diff --git a/crates/execution/execute-helper/src/estimation.rs b/crates/execution/execute-helper/src/estimation.rs index d52bfa1e2b..453fd1ea57 100644 --- a/crates/execution/execute-helper/src/estimation.rs +++ b/crates/execution/execute-helper/src/estimation.rs @@ -9,7 +9,8 @@ use cfx_executor::{ use solidity_abi::string_revert_reason_decode; use super::observer::{ - exec_tracer::ErrorUnwind, gasman::GasLimitEstimation, Observer, + access_list::AccessListKey, exec_tracer::ErrorUnwind, + gasman::GasLimitEstimationKey, Observer, }; use cfx_parameters::{consensus::ONE_CFX_IN_DRIP, staking::*}; use cfx_statedb::Result as DbResult; @@ -17,9 +18,12 @@ use cfx_types::{ address_util::AddressUtil, Address, AddressSpaceUtil, Space, U256, }; use cfx_vm_types::{self as vm, Env, Spec}; -use primitives::{transaction::Action, SignedTransaction, Transaction}; +use primitives::{ + transaction::Action, AccessList, SignedTransaction, Transaction, +}; use std::{ cmp::{max, min}, + collections::HashSet, fmt::Display, ops::{Mul, Shl}, }; @@ -33,6 +37,7 @@ enum SponsoredType { pub struct EstimateExt { pub estimated_gas_limit: U256, pub estimated_storage_limit: u64, + pub access_list: AccessList, // default empty } pub struct EstimationContext<'a> { @@ -140,6 +145,12 @@ impl<'a> EstimationContext<'a> { self.process_estimate_request(&mut tx, &request)?; + let access_list = if request.collect_access_list { + self.collect_access_list(tx.clone(), request)? + } else { + AccessList::new() + }; + let (executed, overwrite_storage_limit) = match self .two_pass_estimation(&tx, request)? { @@ -156,6 +167,7 @@ impl<'a> EstimationContext<'a> { executed, &tx, ), estimated_storage_limit: storage_limit(executed), + access_list, } } ExecutionOutcome::Finished(_) => unreachable!(), @@ -169,9 +181,62 @@ impl<'a> EstimationContext<'a> { executed, overwrite_storage_limit, &request, + access_list, ) } + pub fn collect_access_list( + &mut self, tx: SignedTransaction, request: EstimateRequest, + ) -> DbResult { + // prepare the excludes: add from, to, precompiles, 7702 authorities to + // excludes + let mut excludes = HashSet::new(); + excludes.insert(tx.sender().address); + let to = match tx.transaction.action() { + Action::Call(to) => to, + Action::Create => { + tx.cal_created_address().expect("should success").address + } + }; + excludes.insert(to); + + if let Some(auth_list) = tx.authorization_list() { + excludes + .extend(auth_list.iter().filter_map(|auth| auth.authority())); + } + + let builtins = match tx.space() { + Space::Native => &self.machine.builtins(), + Space::Ethereum => &self.machine.builtins_evm(), + }; + excludes.extend(builtins.iter().map(|(addr, _)| *addr)); + + let access_list = tx + .access_list() + .map(|val| val.to_vec()) + .unwrap_or(AccessList::new()); + + // execute the transaction to collect access list + let res = self.as_executive().transact( + &tx, + request.access_list_options(access_list, excludes), + )?; + let executed = match res { + ExecutionOutcome::Finished(executed) => executed, + ExecutionOutcome::ExecutionErrorBumpNonce(_exec_err, executed) => { + executed + } + ExecutionOutcome::NotExecutedDrop(_) + | ExecutionOutcome::NotExecutedToReconsiderPacking(_) => { + return Ok(AccessList::new()); + } + }; + let access_list = executed.ext_result.get::().expect( + "AccessListKey should be set by AccessListInspector observer", + ); + Ok(access_list.to_vec()) + } + fn check_cip130( &self, tx: &SignedTransaction, request: &EstimateRequest, ) -> Option<(ExecutionOutcome, EstimateExt)> { @@ -195,12 +260,11 @@ impl<'a> EstimationContext<'a> { // storage limit paid by the sponsor are different values. So // this function will // - // 1. First Pass: Assuming the sponsor pays for storage collateral, - // check if the transaction will fail for - // NotEnoughBalanceForStorage. + // 1. First Pass: Assuming the sender pays for storage collateral, + // check if the transaction will finished // // 2. Second Pass: If it does, executes the transaction again assuming - // the user pays for the storage collateral. The resultant + // the sponsor pays for the storage collateral. The resultant // storage limit must be larger than the maximum storage limit // can be afford by the sponsor, to guarantee the user pays for // the storage limit. @@ -294,6 +358,7 @@ impl<'a> EstimationContext<'a> { fn enact_executed_by_estimation_request( &self, tx: SignedTransaction, mut executed: Executed, overwrite_storage_limit: Option, request: &EstimateRequest, + access_list: AccessList, ) -> DbResult<(ExecutionOutcome, EstimateExt)> { let estimated_storage_limit = overwrite_storage_limit.unwrap_or(storage_limit(&executed)); @@ -301,6 +366,7 @@ impl<'a> EstimationContext<'a> { let estimation = EstimateExt { estimated_storage_limit, estimated_gas_limit, + access_list, }; let gas_sponsored_contract_if_eligible_sender = self @@ -412,7 +478,7 @@ fn estimated_gas_limit(executed: &Executed, tx: &SignedTransaction) -> U256 { .map(|&x| if x == 0 { 10 } else { 40 }) .sum::(); let estimated = - executed.ext_result.get::().unwrap() * 7 / 6 + executed.ext_result.get::().unwrap() * 7 / 6 + executed.base_gas; U256::max( eip7623_gas_limit.into(), @@ -464,6 +530,7 @@ pub struct EstimateRequest { pub has_gas_price: bool, pub has_nonce: bool, pub has_storage_limit: bool, + pub collect_access_list: bool, } impl EstimateRequest { @@ -498,4 +565,13 @@ impl EstimateRequest { settings: self.transact_settings(ChargeCollateral::EstimateSponsor), } } + + pub fn access_list_options( + self, access_list: AccessList, excludes: HashSet
, + ) -> TransactOptions { + TransactOptions { + observer: Observer::access_list_inspector(access_list, excludes), + settings: self.transact_settings(ChargeCollateral::EstimateSender), + } + } } diff --git a/crates/execution/execute-helper/src/observer/access_list.rs b/crates/execution/execute-helper/src/observer/access_list.rs new file mode 100644 index 0000000000..37adabd9bd --- /dev/null +++ b/crates/execution/execute-helper/src/observer/access_list.rs @@ -0,0 +1,147 @@ +use cfx_executor::observer::{ + CallTracer, CheckpointTracer, DrainTrace, InternalTransferTracer, + OpcodeTracer, SetAuthTracer, StorageTracer, +}; +use cfx_types::{u256_to_address_be, u256_to_h256_be, Address, H256}; +use cfx_vm_interpreter::instructions::Instruction; +use cfx_vm_types::InterpreterInfo; +use primitives::{AccessList, AccessListItem}; +use std::collections::{BTreeSet, HashMap, HashSet}; +use typemap::ShareDebugMap; + +/// An [Inspector] that collects touched accounts and storage slots. +/// +/// This can be used to construct an [AccessList] for a transaction via +/// `eth_createAccessList` +#[derive(Debug, Default)] +pub struct AccessListInspector { + /// All addresses that should be excluded from the final accesslist + excluded: HashSet
, + /// All addresses and touched slots + touched_slots: HashMap>, +} + +impl From<(AccessList, HashSet
)> for AccessListInspector { + fn from(data: (AccessList, HashSet
)) -> Self { + Self::new(data.0, data.1) + } +} + +impl AccessListInspector { + /// Creates a new [AccessListInspector] with the given excluded addresses. + pub fn new(access_list: AccessList, excluded: HashSet
) -> Self { + Self { + excluded, + touched_slots: access_list + .into_iter() + .map(|v| (v.address, v.storage_keys.into_iter().collect())) + .collect(), + } + } + + /// Returns the excluded addresses. + pub fn excluded(&self) -> &HashSet
{ &self.excluded } + + /// Returns a reference to the map of addresses and their corresponding + /// touched storage slots. + pub fn touched_slots(&self) -> &HashMap> { + &self.touched_slots + } + + /// Consumes the inspector and returns the map of addresses and their + /// corresponding touched storage slots. + pub fn into_touched_slots(self) -> HashMap> { + self.touched_slots + } + + /// Returns list of addresses and storage keys used by the transaction. It + /// gives you the list of addresses and storage keys that were touched + /// during execution. + pub fn into_access_list(self) -> AccessList { + let items = self.touched_slots.into_iter().map(|(address, slots)| { + AccessListItem { + address, + storage_keys: slots.into_iter().collect(), + } + }); + items.collect() + } + + /// Returns list of addresses and storage keys used by the transaction. It + /// gives you the list of addresses and storage keys that were touched + /// during execution. + pub fn access_list(&self) -> AccessList { + let items = + self.touched_slots + .iter() + .map(|(address, slots)| AccessListItem { + address: *address, + storage_keys: slots.iter().copied().collect(), + }); + items.collect() + } + + pub fn collcect_excluded_addresses(&mut self, item: Address) { + self.excluded.insert(item); + } +} + +impl DrainTrace for AccessListInspector { + fn drain_trace(self, map: &mut ShareDebugMap) { + map.insert::(self.into_access_list()); + } +} + +pub struct AccessListKey; + +impl typemap::Key for AccessListKey { + type Value = AccessList; +} + +impl OpcodeTracer for AccessListInspector { + fn step(&mut self, interp: &dyn InterpreterInfo) { + let ins = Instruction::from_u8(interp.current_opcode()) + .expect("valid opcode"); + match ins { + Instruction::SLOAD | Instruction::SSTORE => { + if let Some(slot) = interp.stack().last() { + let cur_contract = interp.contract_address(); + self.touched_slots + .entry(cur_contract) + .or_default() + .insert(u256_to_h256_be(*slot)); + } + } + Instruction::EXTCODECOPY + | Instruction::EXTCODEHASH + | Instruction::EXTCODESIZE + | Instruction::BALANCE + | Instruction::SUICIDE => { + if let Some(slot) = interp.stack().last() { + let addr = u256_to_address_be(*slot); + if !self.excluded.contains(&addr) { + self.touched_slots.entry(addr).or_default(); + } + } + } + Instruction::DELEGATECALL + | Instruction::CALL + | Instruction::STATICCALL + | Instruction::CALLCODE => { + if let Some(slot) = interp.stack().last() { + let addr = u256_to_address_be(*slot); + if !self.excluded.contains(&addr) { + self.touched_slots.entry(addr).or_default(); + } + } + } + _ => (), + } + } +} + +impl CallTracer for AccessListInspector {} +impl CheckpointTracer for AccessListInspector {} +impl InternalTransferTracer for AccessListInspector {} +impl StorageTracer for AccessListInspector {} +impl SetAuthTracer for AccessListInspector {} diff --git a/crates/execution/execute-helper/src/observer/gasman.rs b/crates/execution/execute-helper/src/observer/gasman.rs index 7facbb8824..d60834c1f0 100644 --- a/crates/execution/execute-helper/src/observer/gasman.rs +++ b/crates/execution/execute-helper/src/observer/gasman.rs @@ -54,13 +54,13 @@ pub struct GasMan { impl DrainTrace for GasMan { fn drain_trace(self, map: &mut ShareDebugMap) { - map.insert::(self.gas_required()); + map.insert::(self.gas_required()); } } -pub struct GasLimitEstimation; +pub struct GasLimitEstimationKey; -impl typemap::Key for GasLimitEstimation { +impl typemap::Key for GasLimitEstimationKey { type Value = U256; } diff --git a/crates/execution/execute-helper/src/observer/mod.rs b/crates/execution/execute-helper/src/observer/mod.rs index 253c94b3f2..fb78110319 100644 --- a/crates/execution/execute-helper/src/observer/mod.rs +++ b/crates/execution/execute-helper/src/observer/mod.rs @@ -1,7 +1,9 @@ +pub mod access_list; pub mod exec_tracer; pub mod gasman; mod utils; +use access_list::AccessListInspector; use exec_tracer::ExecTracer; use gasman::GasMan; @@ -13,13 +15,17 @@ use cfx_vm_tracer_derive::{AsTracer, DrainTrace}; use std::sync::Arc; use alloy_rpc_types_trace::geth::GethDebugTracingOptions; +use cfx_types::Address; use geth_tracer::{GethTracer, TxExecContext}; +use primitives::AccessList; +use std::collections::HashSet; #[derive(AsTracer, DrainTrace)] pub struct Observer { pub tracer: Option, // parity tracer pub gas_man: Option, pub geth_tracer: Option, + pub access_list_inspector: Option, } impl Observer { @@ -28,6 +34,7 @@ impl Observer { tracer: Some(ExecTracer::default()), gas_man: None, geth_tracer: None, + access_list_inspector: None, } } @@ -36,6 +43,7 @@ impl Observer { tracer: None, gas_man: None, geth_tracer: None, + access_list_inspector: None, } } @@ -44,6 +52,7 @@ impl Observer { tracer: Some(ExecTracer::default()), gas_man: Some(GasMan::default()), geth_tracer: None, + access_list_inspector: None, } } @@ -55,6 +64,21 @@ impl Observer { tracer: None, gas_man: None, geth_tracer: Some(GethTracer::new(tx_exec_context, machine, opts)), + access_list_inspector: None, + } + } + + pub fn access_list_inspector( + access_list: AccessList, excluded: HashSet
, + ) -> Self { + Observer { + tracer: None, + gas_man: None, + geth_tracer: None, + access_list_inspector: Some(AccessListInspector::new( + access_list, + excluded, + )), } } } diff --git a/crates/execution/executor/src/context.rs b/crates/execution/executor/src/context.rs index b72700d47f..0321a0b734 100644 --- a/crates/execution/executor/src/context.rs +++ b/crates/execution/executor/src/context.rs @@ -4,7 +4,6 @@ // Transaction execution environment. use crate::{ - executive::contract_address, executive_observer::TracerTrait, internal_contract::{ block_hash_slot, epoch_hash_slot, suicide as suicide_impl, @@ -21,13 +20,14 @@ use cfx_parameters::staking::{ code_collateral_units, DRIPS_PER_STORAGE_COLLATERAL_UNIT, }; use cfx_types::{ - Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H256, + cal_contract_address_with_space, Address, AddressSpaceUtil, + AddressWithSpace, BigEndianHash, CreateContractAddressType, Space, H256, U256, }; use cfx_vm_types::{ self as vm, ActionParams, ActionValue, CallType, Context as ContextTrait, - ContractCreateResult, CreateContractAddress, CreateType, Env, Error, - MessageCallResult, ReturnData, Spec, TrapKind, + ContractCreateResult, CreateType, Env, Error, MessageCallResult, + ReturnData, Spec, TrapKind, }; use std::sync::Arc; use vm::BlockHashSource; @@ -264,10 +264,9 @@ impl<'a> ContextTrait for Context<'a> { fn create( &mut self, gas: &U256, value: &U256, code: &[u8], - address_scheme: CreateContractAddress, - ) -> cfx_statedb::Result< - ::std::result::Result, - > { + address_scheme: CreateContractAddressType, + ) -> cfx_statedb::Result> + { let caller = AddressWithSpace { address: self.origin.address, space: self.space, @@ -275,7 +274,7 @@ impl<'a> ContextTrait for Context<'a> { let create_type = CreateType::from_address_scheme(&address_scheme); // create new contract address - let (address_with_space, code_hash) = self::contract_address( + let (address_with_space, code_hash) = cal_contract_address_with_space( address_scheme, self.env.number.into(), &caller, @@ -339,7 +338,7 @@ impl<'a> ContextTrait for Context<'a> { &mut self, gas: &U256, sender_address: &Address, receive_address: &Address, value: Option, data: &[u8], code_address: &Address, call_type: CallType, - ) -> cfx_statedb::Result<::std::result::Result> + ) -> cfx_statedb::Result> { trace!(target: "context", "call"); diff --git a/crates/execution/executor/src/executive/fresh_executive.rs b/crates/execution/executor/src/executive/fresh_executive.rs index 57f1df39c4..943e7014e5 100644 --- a/crates/execution/executor/src/executive/fresh_executive.rs +++ b/crates/execution/executor/src/executive/fresh_executive.rs @@ -12,8 +12,9 @@ use cfx_parameters::staking::DRIPS_PER_STORAGE_COLLATERAL_UNIT; use cfx_statedb::Result as DbResult; use cfx_types::{Address, AddressSpaceUtil, Space, U256, U512}; -use cfx_vm_types::extract_7702_payload; -use primitives::{transaction::Action, SignedTransaction, Transaction}; +use primitives::{ + extract_7702_payload, transaction::Action, SignedTransaction, Transaction, +}; macro_rules! early_return_on_err { ($e:expr) => { diff --git a/crates/execution/executor/src/executive/mod.rs b/crates/execution/executor/src/executive/mod.rs index 3e48f75383..a58772f1cc 100644 --- a/crates/execution/executor/src/executive/mod.rs +++ b/crates/execution/executor/src/executive/mod.rs @@ -11,11 +11,8 @@ 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, - SpaceMap, H256, U256, -}; -use cfx_vm_types::{ConsensusGasSpec, CreateContractAddress, Env, Spec}; +use cfx_types::{SpaceMap, U256}; +use cfx_vm_types::{ConsensusGasSpec, Env, Spec}; use primitives::{AccessList, SignedTransaction}; use fresh_executive::FreshExecutive; @@ -161,23 +158,6 @@ pub fn eip7623_required_gas(data: &[u8], spec: &ConsensusGasSpec) -> u64 { spec.tx_gas as u64 + data.iter().map(byte_floor_gas).sum::() } -pub fn contract_address( - address_scheme: CreateContractAddress, block_number: u64, - sender: &AddressWithSpace, nonce: &U256, code: &[u8], -) -> (AddressWithSpace, H256) { - let (mut address, code_hash) = cfx_vm_types::contract_address( - address_scheme, - block_number, - &sender.address, - nonce, - code, - ); - if sender.space == Space::Native { - address.set_contract_type_bits(); - } - (address.with_space(sender.space), code_hash) -} - #[cfg(test)] pub mod test_util { use crate::{ diff --git a/crates/execution/executor/src/executive/pre_checked_executive.rs b/crates/execution/executor/src/executive/pre_checked_executive.rs index 7e4df15638..60b1eee6fe 100644 --- a/crates/execution/executor/src/executive/pre_checked_executive.rs +++ b/crates/execution/executor/src/executive/pre_checked_executive.rs @@ -1,5 +1,4 @@ use super::{ - contract_address, executed::make_ext_result, fresh_executive::CostInfo, transact_options::{ChargeCollateral, TransactSettings}, @@ -18,23 +17,20 @@ use crate::{ }; use cfx_parameters::staking::code_collateral_units; use cfx_vm_types::{ - self as vm, ActionParams, ActionValue, CallType, CreateContractAddress, - CreateType, + self as vm, ActionParams, ActionValue, CallType, CreateType, }; use cfx_parity_trace_types::{SetAuth, SetAuthOutcome}; use cfx_statedb::Result as DbResult; use cfx_types::{ - Address, AddressSpaceUtil, BigEndianHash, Space, H256, U256, U512, + cal_contract_address_with_space, Address, AddressSpaceUtil, + CreateContractAddressType, Space, U256, U512, }; -use cfxkey::{public_to_address, Signature}; -use keccak_hash::keccak; use primitives::{ transaction::Action, AuthorizationListItem, SignedTransaction, + CODE_PREFIX_7702, }; -use rlp::RlpStream; use std::{convert::TryInto, sync::Arc}; -use vm::CODE_PREFIX_7702; pub(super) struct PreCheckedExecutive<'a, O: ExecutiveObserver> { pub context: ExecutiveContext<'a>, @@ -177,11 +173,13 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { Action::Create => { let address_scheme = match tx.space() { Space::Native => { - CreateContractAddress::FromSenderNonceAndCodeHash + CreateContractAddressType::FromSenderNonceAndCodeHash + } + Space::Ethereum => { + CreateContractAddressType::FromSenderNonce } - Space::Ethereum => CreateContractAddress::FromSenderNonce, }; - let (new_address, code_hash) = contract_address( + let (new_address, code_hash) = cal_contract_address_with_space( address_scheme, env.number.into(), &sender, @@ -689,8 +687,6 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { fn process_cip7702_authorization(&mut self) -> DbResult { - const MAGIC: u8 = 0x05; - let Some(authorization_list) = self.tx.authorization_list() else { return Ok(0); }; @@ -701,15 +697,15 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { self.context.env.chain_id[&Space::Ethereum] as u64; let state = &mut self.context.state; - for AuthorizationListItem { - chain_id, - address, - nonce, - y_parity, - r, - s, - } in authorization_list.iter() - { + for auth_item in authorization_list.iter() { + let AuthorizationListItem { + chain_id, + address, + nonce, + y_parity: _, + r: _, + s: _, + } = auth_item; let mut set_auth_action = SetAuth { space: self.tx.space(), chain_id: *chain_id, @@ -719,53 +715,37 @@ impl<'a, O: ExecutiveObserver> PreCheckedExecutive<'a, O> { outcome: SetAuthOutcome::Success, }; // 1. Verify the chain id is either 0 or the chain's current ID. - if *chain_id != U256::zero() - && *chain_id != U256::from(current_chain_id) - { + if !auth_item.is_chain_id_valid(current_chain_id) { set_auth_action.outcome = SetAuthOutcome::InvalidChainId; self.observer.as_tracer().record_set_auth(set_auth_action); continue; } // 2. Verify the nonce is less than 2**64 - 1 - if *nonce == u64::MAX { + if !auth_item.is_nonce_valid() { set_auth_action.outcome = SetAuthOutcome::NonceOverflow; self.observer.as_tracer().record_set_auth(set_auth_action); continue; } - let valid_signature = { - let r: H256 = BigEndianHash::from_uint(r); - let s: H256 = BigEndianHash::from_uint(s); - let signature = Signature::from_rsv(&r, &s, *y_parity); - if !signature.is_low_s() || !signature.is_valid() { + let _validator_signature = match auth_item.signature() { + Some(sig) => sig, + None => { set_auth_action.outcome = SetAuthOutcome::InvalidSignature; self.observer.as_tracer().record_set_auth(set_auth_action); continue; } - signature }; - // 3. authority = ecrecover(keccak(MAGIC || rlp([chain_id, address, - // nonce])), y_parity, r, s) - let authorization_hash = { - let mut rlp = RlpStream::new_list(3); - rlp.append(chain_id).append(address).append(nonce); - - let mut hash_input = vec![MAGIC]; - hash_input.extend_from_slice(rlp.as_raw()); - - keccak(hash_input) - }; - let authority = if let Ok(public) = - cfxkey::recover(&valid_signature, &authorization_hash) - { - public_to_address(&public, /* type_nibble */ false) - .with_evm_space() - } else { - set_auth_action.outcome = SetAuthOutcome::InvalidSignature; - self.observer.as_tracer().record_set_auth(set_auth_action); - continue; + // 3. authority = ecrecover(keccak(AUTH_MAGIC || rlp([chain_id, + // address, nonce])), y_parity, r, s) + let authority = match auth_item.authority() { + Some(addr) => addr.with_evm_space(), + None => { + set_auth_action.outcome = SetAuthOutcome::InvalidSignature; + self.observer.as_tracer().record_set_auth(set_auth_action); + continue; + } }; set_auth_action.author = Some(authority.address); diff --git a/crates/execution/executor/src/executive/tests.rs b/crates/execution/executor/src/executive/tests.rs index a21e858f8e..82fc228f61 100644 --- a/crates/execution/executor/src/executive/tests.rs +++ b/crates/execution/executor/src/executive/tests.rs @@ -15,13 +15,13 @@ use cfx_parameters::{ staking::*, }; use cfx_types::{ - address_util::AddressUtil, Address, AddressSpaceUtil, BigEndianHash, U256, + address_util::AddressUtil, cal_contract_address_with_space, Address, + AddressSpaceUtil, BigEndianHash, CreateContractAddressType, Space, U256, U512, }; use cfx_vm_interpreter::{FinalizationResult, GasPriceTier}; use cfx_vm_types::{ - self as vm, ActionParams, ActionValue, CallType, CreateContractAddress, - CreateType, Env, + self as vm, ActionParams, ActionValue, CallType, CreateType, Env, }; use cfxkey::{Generator, Random}; use primitives::{ @@ -59,8 +59,8 @@ fn test_contract_address() { Address::from_str("87ed868bd4e05f0be585961a5293a68cfb6ce60e").unwrap(); assert_eq!( expected_address, - contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &address.with_native_space(), &U256::from(88), @@ -76,8 +76,8 @@ fn test_sender_balance() { let sender = Address::from_str("1f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender_with_space = sender.with_native_space(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -183,8 +183,8 @@ fn test_create_contract_out_of_depth() { let sender = Address::from_str("1d1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender_with_space = sender.with_native_space(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -238,8 +238,8 @@ fn test_suicide_when_creation() { let sender_addr = Address::from_str("1d1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender_with_space = sender_addr.with_native_space(); - let contract_addr = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let contract_addr = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -320,8 +320,8 @@ fn test_call_to_create() { let sender = Address::from_str("1d1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender_with_space = sender.with_native_space(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -468,8 +468,8 @@ fn test_keccak() { let sender = Address::from_str("1f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let sender_with_space = sender.with_native_space(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -935,8 +935,8 @@ fn test_commission_privilege_all_whitelisted_across_epochs() { let sender = Random.generate().unwrap().address(); let sender_with_space = sender.with_native_space(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -1122,8 +1122,8 @@ fn test_commission_privilege() { let caller1 = Random.generate().unwrap(); let caller2 = Random.generate().unwrap(); let caller3 = Random.generate().unwrap(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -1521,8 +1521,8 @@ fn test_storage_commission_privilege() { let caller1 = Random.generate().unwrap(); let caller2 = Random.generate().unwrap(); let caller3 = Random.generate().unwrap(); - let address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let address = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), @@ -2130,8 +2130,8 @@ fn test_push0() { let sender_addr = Address::from_str("1d1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender_with_space = sender_addr.with_native_space(); - let contract_addr = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let contract_addr = cal_contract_address_with_space( + CreateContractAddressType::FromSenderNonceAndCodeHash, /* block_number = */ 0, &sender_with_space, &U256::zero(), diff --git a/crates/execution/executor/src/executive/transact_options.rs b/crates/execution/executor/src/executive/transact_options.rs index d6456cd51e..60b8145537 100644 --- a/crates/execution/executor/src/executive/transact_options.rs +++ b/crates/execution/executor/src/executive/transact_options.rs @@ -26,8 +26,13 @@ pub struct TransactSettings { #[derive(Debug, Clone, Copy)] pub enum ChargeCollateral { + /// Charge normal collateral. Normal, + /// Estimate collateral which would be charged to the sender. + /// This mode does not actually charge the sender. EstimateSender, + /// Estimate collateral which would be charged to the sponsor. + /// This mode does not actually charge the sponsor. EstimateSponsor, } diff --git a/crates/execution/executor/src/internal_contract/impls/cross_space.rs b/crates/execution/executor/src/internal_contract/impls/cross_space.rs index 694ea7dbd3..fa50004731 100644 --- a/crates/execution/executor/src/internal_contract/impls/cross_space.rs +++ b/crates/execution/executor/src/internal_contract/impls/cross_space.rs @@ -1,5 +1,5 @@ use crate::{ - executive::{contract_address, gas_required_for}, + executive::gas_required_for, executive_observer::AddressPocket, internal_bail, stack::{ @@ -11,12 +11,13 @@ use crate::{ use cfx_parameters::block::CROSS_SPACE_GAS_RATIO; use cfx_statedb::Result as DbResult; use cfx_types::{ - address_util::AddressUtil, Address, AddressSpaceUtil, Space, H256, U256, + address_util::AddressUtil, cal_contract_address_with_space, Address, + AddressSpaceUtil, CreateContractAddressType, Space, H256, U256, }; use cfx_vm_interpreter::Finalize; use cfx_vm_types::{ - self as vm, ActionParams, ActionValue, CallType, Context as _, - CreateContractAddress, CreateType, GasLeft, ParamsType, Spec, + self as vm, ActionParams, ActionValue, CallType, Context as _, CreateType, + GasLeft, ParamsType, Spec, }; use solidity_abi::ABIEncodable; use std::{marker::PhantomData, sync::Arc}; @@ -347,13 +348,16 @@ pub fn create_to_evmcore( ); let (address_scheme, create_type) = match salt { - None => (CreateContractAddress::FromSenderNonce, CreateType::CREATE), + None => ( + CreateContractAddressType::FromSenderNonce, + CreateType::CREATE, + ), Some(salt) => ( - CreateContractAddress::FromSenderSaltAndCodeHash(salt), + CreateContractAddressType::FromSenderSaltAndCodeHash(salt), CreateType::CREATE2, ), }; - let (address_with_space, code_hash) = contract_address( + let (address_with_space, code_hash) = cal_contract_address_with_space( address_scheme, context.env.number.into(), &mapped_sender, diff --git a/crates/execution/executor/src/machine/mod.rs b/crates/execution/executor/src/machine/mod.rs index f16a562be0..380e380940 100644 --- a/crates/execution/executor/src/machine/mod.rs +++ b/crates/execution/executor/src/machine/mod.rs @@ -37,7 +37,7 @@ impl Machine { params, vm_factory, builtins: Arc::new(BTreeMap::new()), - builtins_evm: Arc::new(Default::default()), + builtins_evm: Arc::new(BTreeMap::new()), internal_contracts: Arc::new(InternalContractMap::default()), #[cfg(test)] max_depth: None, diff --git a/crates/execution/executor/src/observer/call_tracer.rs b/crates/execution/executor/src/observer/call_tracer.rs index b9907e0cee..097c519cd2 100644 --- a/crates/execution/executor/src/observer/call_tracer.rs +++ b/crates/execution/executor/src/observer/call_tracer.rs @@ -4,7 +4,7 @@ use cfx_vm_types::ActionParams; use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] #[allow(unused_variables)] pub trait CallTracer { diff --git a/crates/execution/executor/src/observer/checkpoint_tracer.rs b/crates/execution/executor/src/observer/checkpoint_tracer.rs index 6ee2474f91..b247b7e571 100644 --- a/crates/execution/executor/src/observer/checkpoint_tracer.rs +++ b/crates/execution/executor/src/observer/checkpoint_tracer.rs @@ -1,7 +1,7 @@ use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] pub trait CheckpointTracer { fn trace_checkpoint(&mut self) {} diff --git a/crates/execution/executor/src/observer/internal_transfer_tracer.rs b/crates/execution/executor/src/observer/internal_transfer_tracer.rs index abc827d412..dfcb7dc0bc 100644 --- a/crates/execution/executor/src/observer/internal_transfer_tracer.rs +++ b/crates/execution/executor/src/observer/internal_transfer_tracer.rs @@ -6,7 +6,7 @@ use self::AddressPocket::*; use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] #[allow(unused_variables)] /// This trait is used by executive to build traces. diff --git a/crates/execution/executor/src/observer/opcode_tracer.rs b/crates/execution/executor/src/observer/opcode_tracer.rs index f5f24fb0f8..f3bc10470f 100644 --- a/crates/execution/executor/src/observer/opcode_tracer.rs +++ b/crates/execution/executor/src/observer/opcode_tracer.rs @@ -4,7 +4,7 @@ use cfx_vm_types::InterpreterInfo; use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] pub trait OpcodeTracer { fn do_trace_opcode(&self, _enabled: &mut bool) {} diff --git a/crates/execution/executor/src/observer/set_auth_tracer.rs b/crates/execution/executor/src/observer/set_auth_tracer.rs index 7cd829135b..b93364630b 100644 --- a/crates/execution/executor/src/observer/set_auth_tracer.rs +++ b/crates/execution/executor/src/observer/set_auth_tracer.rs @@ -2,7 +2,7 @@ use cfx_parity_trace_types::SetAuth; use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] #[allow(unused_variables)] pub trait SetAuthTracer { diff --git a/crates/execution/executor/src/observer/storage_tracer.rs b/crates/execution/executor/src/observer/storage_tracer.rs index 50e595bff4..bbcfc014b4 100644 --- a/crates/execution/executor/src/observer/storage_tracer.rs +++ b/crates/execution/executor/src/observer/storage_tracer.rs @@ -1,6 +1,6 @@ use impl_tools::autoimpl; use impl_trait_for_tuples::impl_for_tuples; -#[impl_for_tuples(3)] +#[impl_for_tuples(4)] #[autoimpl(for &mut T)] pub trait StorageTracer {} diff --git a/crates/execution/executor/src/state/overlay_account/basic.rs b/crates/execution/executor/src/state/overlay_account/basic.rs index e8bdb5bd3f..7489f1ca52 100644 --- a/crates/execution/executor/src/state/overlay_account/basic.rs +++ b/crates/execution/executor/src/state/overlay_account/basic.rs @@ -1,8 +1,9 @@ use cfx_bytes::Bytes; use cfx_types::{Address, AddressWithSpace, H256, U256}; -use cfx_vm_types::CODE_PREFIX_7702; use keccak_hash::{keccak, KECCAK_EMPTY}; -use primitives::{storage::STORAGE_LAYOUT_REGULAR_V0, CodeInfo}; +use primitives::{ + storage::STORAGE_LAYOUT_REGULAR_V0, CodeInfo, CODE_PREFIX_7702, +}; use std::sync::Arc; use super::OverlayAccount; diff --git a/crates/execution/executor/src/state/state_object/basic_fields.rs b/crates/execution/executor/src/state/state_object/basic_fields.rs index f66b0bc180..8f25b72cf2 100644 --- a/crates/execution/executor/src/state/state_object/basic_fields.rs +++ b/crates/execution/executor/src/state/state_object/basic_fields.rs @@ -6,8 +6,8 @@ use cfx_types::{ address_util::AddressUtil, Address, AddressSpaceUtil, AddressWithSpace, Space, H256, U256, }; -use cfx_vm_types::extract_7702_payload; use keccak_hash::KECCAK_EMPTY; +use primitives::extract_7702_payload; #[cfg(test)] use primitives::StorageLayout; use std::sync::Arc; diff --git a/crates/execution/vm-interpreter/Cargo.toml b/crates/execution/vm-interpreter/Cargo.toml index 287bba4421..d6e509ce4a 100644 --- a/crates/execution/vm-interpreter/Cargo.toml +++ b/crates/execution/vm-interpreter/Cargo.toml @@ -18,6 +18,7 @@ malloc_size_of = { workspace = true } memory-cache = { workspace = true } parking_lot = { workspace = true } rustc-hex = { workspace = true } +primitives = { workspace = true } [dev-dependencies] cfx-vm-types = { workspace = true, features = ["testonly_code"] } diff --git a/crates/execution/vm-interpreter/src/interpreter/gasometer.rs b/crates/execution/vm-interpreter/src/interpreter/gasometer.rs index a43dceeffe..fd502c2480 100644 --- a/crates/execution/vm-interpreter/src/interpreter/gasometer.rs +++ b/crates/execution/vm-interpreter/src/interpreter/gasometer.rs @@ -18,10 +18,11 @@ // Conflux is free software and distributed under GNU General Public License. // See http://www.gnu.org/licenses/ -use cfx_types::{Address, Space, H256, U256}; +use cfx_types::{Space, H256, U256}; use cfx_vm_types::{self as vm, Spec}; -use std::{cmp, sync::Arc}; -use vm::{BlockHashSource, CODE_PREFIX_7702}; +use primitives::extract_7702_payload; +use std::cmp; +use vm::BlockHashSource; use super::{ instructions::{self, Instruction, InstructionInfo}, @@ -625,7 +626,9 @@ fn calc_call_gas( return Ok(call_gas); } - let Some(delegated_address) = delegated_address(context.extcode(&address)?) + let maybe_code = context.extcode(&address)?; + let Some(delegated_address) = + maybe_code.map(|code| extract_7702_payload(&code)).flatten() else { return Ok(call_gas); }; @@ -638,21 +641,6 @@ fn calc_call_gas( }) } -fn delegated_address(extcode: Option>>) -> Option
{ - let code = extcode?; - if !code.starts_with(CODE_PREFIX_7702) { - return None; - } - - let (_prefix, payload) = code.split_at(CODE_PREFIX_7702.len()); - - if payload.len() == Address::len_bytes() { - Some(Address::from_slice(payload)) - } else { - None - } -} - #[test] fn test_mem_gas_cost() { // given diff --git a/crates/execution/vm-interpreter/src/interpreter/mod.rs b/crates/execution/vm-interpreter/src/interpreter/mod.rs index e1f51b711a..d6bae47e8a 100644 --- a/crates/execution/vm-interpreter/src/interpreter/mod.rs +++ b/crates/execution/vm-interpreter/src/interpreter/mod.rs @@ -43,11 +43,13 @@ use super::{ }; use bit_set::BitSet; use cfx_bytes::Bytes; -use cfx_types::{Address, BigEndianHash, Space, H256, U256, U512}; +use cfx_types::{ + Address, BigEndianHash, CreateContractAddressType, Space, H256, U256, U512, +}; use cfx_vm_types::{ self as vm, ActionParams, ActionValue, CallType, ContractCreateResult, - CreateContractAddress, GasLeft, InstructionResult, InterpreterInfo, - MessageCallResult, ParamsType, ReturnData, Spec, TrapError, TrapKind, + GasLeft, InstructionResult, InterpreterInfo, MessageCallResult, ParamsType, + ReturnData, Spec, TrapError, TrapKind, }; use keccak_hash::keccak; use std::{cmp, convert::TryFrom, marker::PhantomData, mem, sync::Arc}; @@ -748,11 +750,11 @@ impl Interpreter { return Err(vm::Error::CreateInitCodeSizeLimit); } let address_scheme = match instruction { - instructions::CREATE if context.space() == Space::Native => CreateContractAddress::FromSenderNonceAndCodeHash, - instructions::CREATE if context.space() == Space::Ethereum => CreateContractAddress::FromSenderNonce, + instructions::CREATE if context.space() == Space::Native => CreateContractAddressType::FromSenderNonceAndCodeHash, + instructions::CREATE if context.space() == Space::Ethereum => CreateContractAddressType::FromSenderNonce, instructions::CREATE2 => { let h: H256 = BigEndianHash::from_uint(&self.stack.pop_back()); - CreateContractAddress::FromSenderSaltAndCodeHash(h) + CreateContractAddressType::FromSenderSaltAndCodeHash(h) }, _ => unreachable!("instruction can only be CREATE/CREATE2 checked above; qed"), }; diff --git a/crates/execution/vm-types/src/call_create_type.rs b/crates/execution/vm-types/src/call_create_type.rs index c05c0e8b04..a76e1a02d7 100644 --- a/crates/execution/vm-types/src/call_create_type.rs +++ b/crates/execution/vm-types/src/call_create_type.rs @@ -20,7 +20,7 @@ //! EVM call types. -use super::CreateContractAddress; +use cfx_types::CreateContractAddressType; use rlp::{Decodable, DecoderError, Encodable, Rlp, RlpStream}; use serde::Serialize; @@ -85,16 +85,18 @@ pub enum CreateType { } impl CreateType { - pub fn from_address_scheme(address: &CreateContractAddress) -> CreateType { + pub fn from_address_scheme( + address: &CreateContractAddressType, + ) -> CreateType { match address { - CreateContractAddress::FromSenderNonce => CreateType::CREATE, - CreateContractAddress::FromSenderNonceAndCodeHash => { + CreateContractAddressType::FromSenderNonce => CreateType::CREATE, + CreateContractAddressType::FromSenderNonceAndCodeHash => { CreateType::CREATE } - CreateContractAddress::FromBlockNumberSenderNonceAndCodeHash => { + CreateContractAddressType::FromBlockNumberSenderNonceAndCodeHash => { unreachable!("Inactivate address scheme") } - CreateContractAddress::FromSenderSaltAndCodeHash(_) => { + CreateContractAddressType::FromSenderSaltAndCodeHash(_) => { CreateType::CREATE2 } } diff --git a/crates/execution/vm-types/src/context.rs b/crates/execution/vm-types/src/context.rs index 2190174350..3ec48dba5c 100644 --- a/crates/execution/vm-types/src/context.rs +++ b/crates/execution/vm-types/src/context.rs @@ -30,11 +30,7 @@ use super::{ }; use cfx_bytes::Bytes; use cfx_db_errors::statedb::Result as DbResult; -pub use cfx_types::{ - cal_contract_address as contract_address, - CreateContractAddressType as CreateContractAddress, -}; -use cfx_types::{Address, Space, H256, U256}; +use cfx_types::{Address, CreateContractAddressType, Space, H256, U256}; use std::sync::Arc; #[derive(Debug)] @@ -113,7 +109,7 @@ pub trait Context { /// succesfull. fn create( &mut self, gas: &U256, value: &U256, code: &[u8], - address: CreateContractAddress, + address: CreateContractAddressType, ) -> DbResult<::std::result::Result>; /// Message call. diff --git a/crates/execution/vm-types/src/lib.rs b/crates/execution/vm-types/src/lib.rs index 3ea8634644..339552d63c 100644 --- a/crates/execution/vm-types/src/lib.rs +++ b/crates/execution/vm-types/src/lib.rs @@ -19,8 +19,7 @@ pub use self::{ action_params::{ActionParams, ActionValue, ParamsType}, call_create_type::{CallType, CreateType}, context::{ - contract_address, BlockHashSource, Context, ContractCreateResult, - CreateContractAddress, MessageCallResult, + BlockHashSource, Context, ContractCreateResult, MessageCallResult, }, env::Env, error::{ @@ -30,10 +29,7 @@ pub use self::{ instruction_result::InstructionResult, interpreter_info::InterpreterInfo, return_data::{GasLeft, ReturnData}, - spec::{ - extract_7702_payload, CIP645Spec, ConsensusGasSpec, Spec, - CODE_PREFIX_7702, - }, + spec::{CIP645Spec, ConsensusGasSpec, Spec}, }; /// Virtual Machine interface diff --git a/crates/execution/vm-types/src/spec.rs b/crates/execution/vm-types/src/spec.rs index 64593d8c2c..8550d61c82 100644 --- a/crates/execution/vm-types/src/spec.rs +++ b/crates/execution/vm-types/src/spec.rs @@ -22,8 +22,6 @@ use cfx_types::{address_util::AddressUtil, Address}; use primitives::{block::BlockHeight, BlockNumber}; -pub const CODE_PREFIX_7702: &'static [u8] = b"\xef\x01\x00"; - /// Definition of the cost spec and other parameterisations for the VM. #[derive(Debug, Clone)] pub struct Spec { @@ -506,16 +504,3 @@ impl ConsensusGasSpec { impl Default for Spec { fn default() -> Self { Spec::new_spec_for_test() } } - -pub fn extract_7702_payload(code: &[u8]) -> Option
{ - if code.starts_with(CODE_PREFIX_7702) { - let (_prefix, payload) = code.split_at(CODE_PREFIX_7702.len()); - if payload.len() == Address::len_bytes() { - Some(Address::from_slice(payload)) - } else { - None - } - } else { - None - } -} diff --git a/crates/execution/vm-types/src/tests.rs b/crates/execution/vm-types/src/tests.rs index d0bbd9a365..6a7a930a9a 100644 --- a/crates/execution/vm-types/src/tests.rs +++ b/crates/execution/vm-types/src/tests.rs @@ -21,13 +21,15 @@ use crate::BlockHashSource; use super::{ - error::TrapKind, CallType, Context, ContractCreateResult, - CreateContractAddress, Env, Error, GasLeft, MessageCallResult, Result, - ReturnData, Spec, + error::TrapKind, CallType, Context, ContractCreateResult, Env, Error, + GasLeft, MessageCallResult, Result, ReturnData, Spec, }; use cfx_bytes::Bytes; use cfx_db_errors::statedb::Result as DbResult; -use cfx_types::{address_util::AddressUtil, Address, Space, H256, U256}; +use cfx_types::{ + address_util::AddressUtil, Address, CreateContractAddressType, Space, H256, + U256, +}; use keccak_hash::keccak; use std::{ collections::{HashMap, HashSet}, @@ -48,7 +50,7 @@ pub enum MockCallType { #[derive(PartialEq, Eq, Hash, Debug)] pub struct MockCall { pub call_type: MockCallType, - pub create_scheme: Option, + pub create_scheme: Option, pub gas: U256, pub sender_address: Option
, pub receive_address: Option
, @@ -155,7 +157,7 @@ impl Context for MockContext { fn create( &mut self, gas: &U256, value: &U256, code: &[u8], - address: CreateContractAddress, + address: CreateContractAddressType, ) -> DbResult<::std::result::Result> { self.calls.insert(MockCall { call_type: MockCallType::Create, diff --git a/crates/primitives/src/lib.rs b/crates/primitives/src/lib.rs index 50fe552b61..9bc26b360c 100644 --- a/crates/primitives/src/lib.rs +++ b/crates/primitives/src/lib.rs @@ -42,10 +42,11 @@ pub use crate::{ }, storage_key::*, transaction::{ - AccessList, AccessListItem, Action, AuthorizationList, - AuthorizationListItem, SignedTransaction, Transaction, - TransactionWithSignature, TransactionWithSignatureSerializePart, - TxPropagateId, + extract_7702_payload, AccessList, AccessListItem, Action, + AuthorizationList, AuthorizationListItem, SignedTransaction, + Transaction, TransactionWithSignature, + TransactionWithSignatureSerializePart, TxPropagateId, AUTH_MAGIC, + CODE_PREFIX_7702, }, transaction_index::TransactionIndex, zero::Zero, diff --git a/crates/primitives/src/transaction/authorization.rs b/crates/primitives/src/transaction/authorization.rs new file mode 100644 index 0000000000..93d695f3fb --- /dev/null +++ b/crates/primitives/src/transaction/authorization.rs @@ -0,0 +1,89 @@ +use cfx_types::{Address, BigEndianHash, H256, U256}; +use cfxkey::{public_to_address, Signature}; +use keccak_hash::keccak; +use rlp::RlpStream; +use rlp_derive::{RlpDecodable, RlpEncodable}; +use serde::{Deserialize, Serialize}; + +pub const AUTH_MAGIC: u8 = 0x05; + +pub const CODE_PREFIX_7702: &'static [u8] = b"\xef\x01\x00"; + +#[derive( + Debug, + Clone, + PartialEq, + Eq, + Serialize, + Deserialize, + RlpEncodable, + RlpDecodable, +)] +#[serde(rename_all = "camelCase")] +pub struct AuthorizationListItem { + pub chain_id: U256, + pub address: Address, + pub nonce: u64, + pub y_parity: u8, + pub r: U256, + pub s: U256, +} + +pub type AuthorizationList = Vec; + +impl AuthorizationListItem { + pub fn is_valid_y_parity(&self) -> bool { + self.y_parity == 0 || self.y_parity == 1 + } + + pub fn is_chain_id_valid(&self, chain_id: u64) -> bool { + self.chain_id == U256::from(chain_id) || self.chain_id.is_zero() + } + + pub fn is_nonce_valid(&self) -> bool { self.nonce < u64::MAX } + + pub fn hash(&self) -> H256 { + let mut rlp = RlpStream::new_list(3); + rlp.append(&self.chain_id) + .append(&self.address) + .append(&self.nonce); + + let mut hash_input = vec![AUTH_MAGIC]; + hash_input.extend_from_slice(rlp.as_raw()); + + keccak(hash_input) + } + + pub fn signature(&self) -> Option { + let r: H256 = BigEndianHash::from_uint(&self.r); + let s: H256 = BigEndianHash::from_uint(&self.s); + let signature = Signature::from_rsv(&r, &s, self.y_parity); + if !signature.is_low_s() || !signature.is_valid() { + return None; + } + Some(signature) + } + + pub fn authority(&self) -> Option
{ + let signature = self.signature()?; + let hash = self.hash(); + if let Ok(public) = cfxkey::recover(&signature, &hash) { + Some(public_to_address(&public, false)) + } else { + None + } + } +} + +pub fn extract_7702_payload(code: &[u8]) -> Option
{ + if code.starts_with(CODE_PREFIX_7702) { + let (_prefix, payload) = code.split_at(CODE_PREFIX_7702.len()); + if payload.len() == Address::len_bytes() { + Some(Address::from_slice(payload)) + } else { + None + } + } else { + None + } +} diff --git a/crates/primitives/src/transaction/mod.rs b/crates/primitives/src/transaction/mod.rs index 10f616e18a..ed0936940d 100644 --- a/crates/primitives/src/transaction/mod.rs +++ b/crates/primitives/src/transaction/mod.rs @@ -4,9 +4,14 @@ // TODO(7702): refactor this file +mod authorization; pub mod eth_transaction; pub mod native_transaction; +pub use authorization::{ + extract_7702_payload, AuthorizationList, AuthorizationListItem, AUTH_MAGIC, + CODE_PREFIX_7702, +}; pub use eth_transaction::{ Eip1559Transaction, Eip155Transaction, Eip2930Transaction, Eip7702Transaction, EthereumTransaction, @@ -26,7 +31,8 @@ use crate::{ }, }; use cfx_types::{ - Address, AddressSpaceUtil, AddressWithSpace, BigEndianHash, Space, H160, + cal_contract_address_with_space, Address, AddressSpaceUtil, + AddressWithSpace, BigEndianHash, CreateContractAddressType, Space, H160, H256, U256, }; use eth_transaction::eip155_signature; @@ -273,28 +279,6 @@ pub struct AccessListItem { pub type AccessList = Vec; -#[derive( - Debug, - Clone, - PartialEq, - Eq, - Serialize, - Deserialize, - RlpEncodable, - RlpDecodable, -)] -#[serde(rename_all = "camelCase")] -pub struct AuthorizationListItem { - pub chain_id: U256, - pub address: Address, - pub nonce: u64, - pub y_parity: u8, - pub r: U256, - pub s: U256, -} - -pub type AuthorizationList = Vec; - #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub enum Transaction { Native(TypedNativeTransaction), @@ -1146,6 +1130,33 @@ impl SignedTransaction { Ok(true) } } + + // Calculates the created contract address if the transaction is a contract + // creation. + pub fn cal_created_address(&self) -> Option { + if let Action::Create = self.action() { + let from = self.sender(); + let nonce = self.nonce(); + let space = self.space(); + let create_type = match space { + Space::Native => { + CreateContractAddressType::FromSenderNonceAndCodeHash + } + Space::Ethereum => CreateContractAddressType::FromSenderNonce, + }; + let code = self.data().as_slice(); + let (created_address, _) = cal_contract_address_with_space( + create_type, + 0, + &from, + &nonce, + code, + ); + Some(created_address) + } else { + None + } + } } impl MallocSizeOf for SignedTransaction { diff --git a/crates/rpc/rpc-eth-api/src/eth.rs b/crates/rpc/rpc-eth-api/src/eth.rs index f9f7db2511..18a07c7553 100644 --- a/crates/rpc/rpc-eth-api/src/eth.rs +++ b/crates/rpc/rpc-eth-api/src/eth.rs @@ -231,6 +231,7 @@ pub trait EthApi { #[method(name = "createAccessList")] async fn create_access_list( &self, request: TransactionRequest, block_number: Option, + state_override: Option, ) -> RpcResult; /// Generates and returns an estimate of how much gas is necessary to allow diff --git a/crates/rpc/rpc-eth-impl/src/eth.rs b/crates/rpc/rpc-eth-impl/src/eth.rs index 09859e63a2..7ccba890ee 100644 --- a/crates/rpc/rpc-eth-impl/src/eth.rs +++ b/crates/rpc/rpc-eth-impl/src/eth.rs @@ -1,6 +1,6 @@ use crate::helpers::{FeeHistoryCache, MAX_FEE_HISTORY_CACHE_BLOCK_COUNT}; use async_trait::async_trait; -use cfx_execute_helper::estimation::EstimateRequest; +use cfx_execute_helper::estimation::{EstimateExt, EstimateRequest}; use cfx_executor::executive::{ Executed, ExecutionError, ExecutionOutcome, TxDropError, }; @@ -140,7 +140,8 @@ impl EthApi { block_number_or_hash: Option, state_overrides: Option, block_overrides: Option>, - ) -> CoreResult<(Executed, U256)> { + collect_access_list: bool, + ) -> CoreResult<(Executed, EstimateExt)> { let consensus_graph = self.consensus_graph(); if request.gas_price.is_some() @@ -198,6 +199,7 @@ impl EthApi { has_gas_price: request.has_gas_price(), has_nonce: request.nonce.is_some(), has_storage_limit: false, + collect_access_list, }; let chain_id = self.consensus.best_chain_id(); @@ -274,7 +276,7 @@ impl EthApi { ExecutionOutcome::Finished(executed) => executed, }; - Ok((executed, estimation.estimated_gas_limit)) + Ok((executed, estimation)) } pub fn send_transaction_with_signature( @@ -1332,6 +1334,7 @@ impl EthApiServer for EthApi { block_number, state_overrides, block_overrides, + false, )?; Ok(execution.output.into()) @@ -1367,10 +1370,23 @@ impl EthApiServer for EthApi { /// gas usage compared to a transaction without an access list. async fn create_access_list( &self, request: TransactionRequest, block_number: Option, + state_overrides: Option, ) -> RpcResult { - let _ = block_number; - let _ = request; - Err(jsonrpsee_internal_error("Not implemented")) + let (_, estimate_res) = self.exec_transaction( + request, + block_number, + state_overrides, + None, + false, + )?; + Ok(AccessListResult { + access_list: estimate_res.access_list, + // TODO: return the gas used after applying access list + gas_used: estimate_res.estimated_gas_limit, + // if tx execution failed, this rpc call is failed and error is in + // the rpc error + error: None, + }) } /// Generates and returns an estimate of how much gas is necessary to allow @@ -1379,14 +1395,15 @@ impl EthApiServer for EthApi { &self, request: TransactionRequest, block_number: Option, state_overrides: Option, ) -> RpcResult { - let (_, estimated_gas) = self.exec_transaction( + let (_, estimate_res) = self.exec_transaction( request, block_number, state_overrides, None, + false, )?; - Ok(estimated_gas) + Ok(estimate_res.estimated_gas_limit) } /// Returns the current price per gas in wei. diff --git a/crates/rpc/rpc-eth-types/src/transaction.rs b/crates/rpc/rpc-eth-types/src/transaction.rs index 88d6311d78..aa49c2f867 100644 --- a/crates/rpc/rpc-eth-types/src/transaction.rs +++ b/crates/rpc/rpc-eth-types/src/transaction.rs @@ -19,10 +19,7 @@ // along with OpenEthereum. If not, see . use crate::{Bytes, SignedAuthorization}; -use cfx_types::{ - cal_contract_address, CreateContractAddressType, H160, H256, H512, U256, - U64, -}; +use cfx_types::{H160, H256, H512, U256, U64}; use primitives::{ transaction::eth_transaction::eip155_signature, AccessList, Action, SignedTransaction, @@ -174,18 +171,6 @@ impl Transaction { } pub fn deployed_contract_address(t: &SignedTransaction) -> Option { - match t.action() { - Action::Create => { - let (new_contract_address, _) = cal_contract_address( - CreateContractAddressType::FromSenderNonce, - 0, - &t.sender().address, - t.nonce(), - t.data(), - ); - Some(new_contract_address) - } - Action::Call(_) => None, - } + t.cal_created_address().map(|t| t.address) } } diff --git a/crates/transactiongen/src/lib.rs b/crates/transactiongen/src/lib.rs index c97eb9be1e..7078de9672 100644 --- a/crates/transactiongen/src/lib.rs +++ b/crates/transactiongen/src/lib.rs @@ -8,10 +8,9 @@ use log::{debug, info, trace}; use crate::bytes::Bytes; use cfx_types::{ - address_util::AddressUtil, Address, AddressSpaceUtil, BigEndianHash, H256, - H512, U256, U512, + address_util::AddressUtil, cal_contract_address, Address, AddressSpaceUtil, + BigEndianHash, CreateContractAddressType, H256, H512, U256, U512, }; -use cfx_vm_types::{contract_address, CreateContractAddress}; use cfxcore::{ SharedConsensusGraph, SharedSynchronizationService, SharedTransactionPool, }; @@ -301,8 +300,8 @@ impl DirectTransactionGenerator { accounts.insert(start_address.clone(), info); let address_by_index = vec![start_address.clone()]; - let mut erc20_address = contract_address( - CreateContractAddress::FromSenderNonceAndCodeHash, + let mut erc20_address = cal_contract_address( + CreateContractAddressType::FromSenderNonceAndCodeHash, // A fake block_number. There field is unnecessary in Ethereum // replay test. 0, diff --git a/tools/consensus_bench/Cargo.lock b/tools/consensus_bench/Cargo.lock index c0a9839f38..f2bbb7ef20 100644 --- a/tools/consensus_bench/Cargo.lock +++ b/tools/consensus_bench/Cargo.lock @@ -999,6 +999,7 @@ dependencies = [ "cfx-parity-trace-types", "cfx-statedb", "cfx-types", + "cfx-vm-interpreter", "cfx-vm-tracer-derive", "cfx-vm-types", "geth-tracer", @@ -1308,6 +1309,7 @@ dependencies = [ "malloc_size_of", "memory-cache", "parking_lot 0.12.3", + "primitives", "rustc-hex", ] diff --git a/tools/evm-spec-tester/Cargo.lock b/tools/evm-spec-tester/Cargo.lock index f4e0feabb7..dab77a2c90 100644 --- a/tools/evm-spec-tester/Cargo.lock +++ b/tools/evm-spec-tester/Cargo.lock @@ -1057,6 +1057,7 @@ dependencies = [ "cfx-parity-trace-types", "cfx-statedb", "cfx-types", + "cfx-vm-interpreter", "cfx-vm-tracer-derive", "cfx-vm-types", "geth-tracer", @@ -1493,6 +1494,7 @@ dependencies = [ "malloc_size_of", "memory-cache", "parking_lot 0.12.1", + "primitives", "rustc-hex", ]