EIP2929 with journaling + Yolov3 (#79)

This commit is contained in:
adria0.eth
2020-11-04 19:11:05 +01:00
committed by GitHub
parent 50a4d5fa57
commit 6078eeaed7
29 changed files with 921 additions and 199 deletions

View File

@@ -108,6 +108,7 @@ impl<'a> EvmTestClient<'a> {
Some(ethereum::new_byzantium_to_constantinoplefixat5_test())
}
ForkSpec::Berlin => Some(ethereum::new_berlin_test()),
ForkSpec::Yolo3 => Some(ethereum::new_yolo3_test()),
ForkSpec::FrontierToHomesteadAt5
| ForkSpec::HomesteadToDaoAt5
| ForkSpec::HomesteadToEIP150At5

View File

@@ -252,6 +252,11 @@ pub fn new_berlin_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/berlin_test.json"))
}
/// Create a new YOLO spec
pub fn new_yolo3_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/yolo3_test.json"))
}
/// Create a new Musicoin-MCIP3-era spec.
pub fn new_mcip3_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/mcip3_test.json"))

View File

@@ -16,7 +16,6 @@
//! Transaction Execution environment.
use bytes::{Bytes, BytesRef};
use crossbeam_utils::thread;
use ethereum_types::{Address, H256, U256, U512};
use evm::{CallType, FinalizationResult, Finalize};
use executed::ExecutionError;
@@ -31,26 +30,10 @@ use trace::{self, Tracer, VMTracer};
use transaction_ext::Transaction;
use types::transaction::{Action, SignedTransaction};
use vm::{
self, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo, ResumeCall,
ResumeCreate, ReturnData, Schedule, TrapError,
self, AccessList, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo,
ResumeCall, ResumeCreate, ReturnData, Schedule, TrapError,
};
#[cfg(debug_assertions)]
/// Roughly estimate what stack size each level of evm depth will use. (Debug build)
const STACK_SIZE_PER_DEPTH: usize = 128 * 1024;
#[cfg(not(debug_assertions))]
/// Roughly estimate what stack size each level of evm depth will use.
const STACK_SIZE_PER_DEPTH: usize = 24 * 1024;
#[cfg(debug_assertions)]
/// Entry stack overhead prior to execution. (Debug build)
const STACK_SIZE_ENTRY_OVERHEAD: usize = 100 * 1024;
#[cfg(not(debug_assertions))]
/// Entry stack overhead prior to execution.
const STACK_SIZE_ENTRY_OVERHEAD: usize = 20 * 1024;
#[cfg(any(test, feature = "test-helpers"))]
/// Precompile that can never be prunned from state trie (0x3, only in tests)
const UNPRUNABLE_PRECOMPILE_ADDRESS: Option<Address> = Some(ethereum_types::H160([
@@ -252,6 +235,18 @@ pub struct CallCreateExecutive<'a> {
}
impl<'a> CallCreateExecutive<'a> {
/// Create new state with access list.
pub fn new_substate(params: &ActionParams, schedule: &'a Schedule) -> Substate {
if schedule.eip2929 {
let mut substate = Substate::from_access_list(&params.access_list);
substate.access_list.insert_address(params.address);
substate.access_list.insert_address(params.sender);
substate
} else {
Substate::default()
}
}
/// Create a new call executive using raw data.
pub fn new_call_raw(
params: ActionParams,
@@ -287,7 +282,8 @@ impl<'a> CallCreateExecutive<'a> {
CallCreateExecutiveKind::CallBuiltin(params)
} else {
if params.code.is_some() {
CallCreateExecutiveKind::ExecCall(params, Substate::new())
let substate = Self::new_substate(&params, schedule);
CallCreateExecutiveKind::ExecCall(params, substate)
} else {
CallCreateExecutiveKind::Transfer(params)
}
@@ -327,7 +323,8 @@ impl<'a> CallCreateExecutive<'a> {
let gas = params.gas;
let kind = CallCreateExecutiveKind::ExecCreate(params, Substate::new());
let substate = Self::new_substate(&params, schedule);
let kind = CallCreateExecutiveKind::ExecCreate(params, substate);
Self {
info,
@@ -457,6 +454,7 @@ impl<'a> CallCreateExecutive<'a> {
}
}
state.revert_to_checkpoint();
un_substate.access_list.rollback();
}
Ok(_) | Err(vm::Error::Internal(_)) => {
state.discard_checkpoint();
@@ -1188,7 +1186,14 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
});
}
let mut substate = Substate::new();
let mut access_list = AccessList::new(schedule.eip2929);
if schedule.eip2929 {
for (address, _) in self.machine.builtins() {
access_list.insert_address(*address);
}
}
let mut substate = Substate::from_access_list(&access_list);
// NOTE: there can be no invalid transactions from this point.
if !schedule.keep_unsigned_nonce || !t.is_unsigned() {
@@ -1221,6 +1226,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
access_list: access_list,
};
let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res {
@@ -1243,6 +1249,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
data: Some(t.data.clone()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
access_list: access_list,
};
let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res {
@@ -1325,48 +1332,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
result
}
/// Calls contract function with given contract params, if the stack depth is above a threshold, create a new thread
/// to execute it.
pub fn call_with_crossbeam<T, V>(
&mut self,
params: ActionParams,
substate: &mut Substate,
stack_depth: usize,
tracer: &mut T,
vm_tracer: &mut V,
) -> vm::Result<FinalizationResult>
where
T: Tracer,
V: VMTracer,
{
let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get());
let depth_threshold =
local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH;
if stack_depth != depth_threshold {
self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
} else {
thread::scope(|scope| {
let stack_size = cmp::max(
self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH,
local_stack_size,
);
scope
.builder()
.stack_size(stack_size)
.spawn(|_| {
self.call_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
})
.expect(
"Sub-thread creation cannot fail; the host might run out of resources; qed",
)
.join()
})
.expect("Sub-thread never panics; qed")
.expect("Sub-thread never panics; qed")
}
}
/// Calls contract function with given contract params.
pub fn call<T, V>(
&mut self,
@@ -1437,54 +1402,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
result
}
/// Creates contract with given contract params, if the stack depth is above a threshold, create a new thread to
/// execute it.
pub fn create_with_crossbeam<T, V>(
&mut self,
params: ActionParams,
substate: &mut Substate,
stack_depth: usize,
tracer: &mut T,
vm_tracer: &mut V,
) -> vm::Result<FinalizationResult>
where
T: Tracer,
V: VMTracer,
{
let local_stack_size = ::io::LOCAL_STACK_SIZE.with(|sz| sz.get());
let depth_threshold =
local_stack_size.saturating_sub(STACK_SIZE_ENTRY_OVERHEAD) / STACK_SIZE_PER_DEPTH;
if stack_depth != depth_threshold {
self.create_with_stack_depth(params, substate, stack_depth, tracer, vm_tracer)
} else {
thread::scope(|scope| {
let stack_size = cmp::max(
self.schedule.max_depth.saturating_sub(depth_threshold) * STACK_SIZE_PER_DEPTH,
local_stack_size,
);
scope
.builder()
.stack_size(stack_size)
.spawn(|_| {
self.create_with_stack_depth(
params,
substate,
stack_depth,
tracer,
vm_tracer,
)
})
.expect(
"Sub-thread creation cannot fail; the host might run out of resources; qed",
)
.join()
})
.expect("Sub-thread never panics; qed")
.expect("Sub-thread never panics; qed")
}
}
/// Creates contract with given contract params.
pub fn create<T, V>(
&mut self,

View File

@@ -24,8 +24,8 @@ use std::{cmp, sync::Arc};
use trace::{Tracer, VMTracer};
use types::transaction::UNSIGNED_SENDER;
use vm::{
self, ActionParams, ActionValue, CallType, ContractCreateResult, CreateContractAddress,
EnvInfo, Ext, MessageCallResult, ReturnData, Schedule, TrapKind,
self, AccessList, ActionParams, ActionValue, CallType, ContractCreateResult,
CreateContractAddress, EnvInfo, Ext, MessageCallResult, ReturnData, Schedule, TrapKind,
};
/// Policy for handling output data on `RETURN` opcode.
@@ -200,10 +200,11 @@ where
data: Some(H256::from(number).to_vec()),
call_type: CallType::Call,
params_type: vm::ParamsType::Separate,
access_list: AccessList::default(),
};
let mut ex = Executive::new(self.state, self.env_info, self.machine, self.schedule);
let r = ex.call_with_crossbeam(
let r = ex.call_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@@ -288,6 +289,7 @@ where
data: None,
call_type: CallType::None,
params_type: vm::ParamsType::Embedded,
access_list: self.substate.access_list.clone(),
};
if !self.static_flag {
@@ -312,7 +314,7 @@ where
self.depth,
self.static_flag,
);
let out = ex.create_with_crossbeam(
let out = ex.create_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@@ -322,6 +324,15 @@ where
Ok(into_contract_create_result(out, &address, self.substate))
}
fn calc_address(&self, code: &[u8], address_scheme: CreateContractAddress) -> Option<Address> {
match self.state.nonce(&self.origin_info.address) {
Ok(nonce) => {
Some(contract_address(address_scheme, &self.origin_info.address, &nonce, &code).0)
}
Err(_) => None,
}
}
fn call(
&mut self,
gas: &U256,
@@ -358,6 +369,7 @@ where
data: Some(data.to_vec()),
call_type: call_type,
params_type: vm::ParamsType::Separate,
access_list: self.substate.access_list.clone(),
};
if let Some(value) = value {
@@ -376,7 +388,7 @@ where
self.depth,
self.static_flag,
);
let out = ex.call_with_crossbeam(
let out = ex.call_with_stack_depth(
params,
self.substate,
self.stack_depth + 1,
@@ -518,6 +530,26 @@ where
fn trace_executed(&mut self, gas_used: U256, stack_push: &[U256], mem: &[u8]) {
self.vm_tracer.trace_executed(gas_used, stack_push, mem)
}
fn al_is_enabled(&self) -> bool {
self.substate.access_list.is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.substate.access_list.contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.substate.access_list.insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.substate.access_list.contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.substate.access_list.insert_address(address)
}
}
#[cfg(test)]

View File

@@ -167,6 +167,10 @@ where
Ok(ContractCreateResult::Created(contract_address, *gas))
}
fn calc_address(&self, code: &[u8], address: CreateContractAddress) -> Option<Address> {
Some(contract_address(address, &self.sender, &self.nonce, &code).0)
}
fn call(
&mut self,
gas: &U256,
@@ -238,6 +242,26 @@ where
fn sub_sstore_refund(&mut self, value: usize) {
self.ext.sub_sstore_refund(value)
}
fn al_is_enabled(&self) -> bool {
self.ext.al_is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.ext.al_contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.ext.al_insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.ext.al_contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.ext.al_insert_address(address)
}
}
/// run an json executive test

View File

@@ -68,7 +68,6 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
let pre: PodState = test.pre_state.into();
for (spec_name, states) in test.post_states {
let total = states.len();
let spec = match EvmTestClient::spec_from_json(&spec_name) {
Some(spec) => spec,
None => {
@@ -81,11 +80,10 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
for (i, state) in states.into_iter().enumerate() {
let info = format!(
" - state: {} | {:?} ({}/{}) ...",
name,
"TestState/{}/{:?}/{}/trie",
path.to_string_lossy(),
spec_name,
i + 1,
total
i
);
if skip_test(&state_test, &name, &spec.name, i + 1) {
println!("{}: SKIPPED", info);
@@ -112,7 +110,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
}
Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => {
println!(
"{} !!! State mismatch (got: {}, expect: {}",
"{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root
);
flushln!("{} fail", info);
@@ -124,7 +122,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
..
})) if state_root != post_root => {
println!(
"{} !!! State mismatch (got: {}, expect: {}",
"{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root
);
println!("{} !!! Execution error: {:?}", info, error);

View File

@@ -32,7 +32,8 @@ use types::{
BlockNumber,
};
use vm::{
ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType, Schedule,
AccessList, ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType,
Schedule,
};
use block::ExecutedBlock;
@@ -201,6 +202,7 @@ impl EthereumMachine {
data,
call_type: call_type.unwrap_or(CallType::Call),
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = self.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, self, &schedule);

View File

@@ -32,7 +32,7 @@ use parking_lot::RwLock;
use rlp::{Rlp, RlpStream};
use rustc_hex::FromHex;
use types::{header::Header, BlockNumber};
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use builtin::Builtin;
use engines::{
@@ -135,6 +135,8 @@ pub struct CommonParams {
pub eip2028_transition: BlockNumber,
/// Number of first block where EIP-2315 rules begin.
pub eip2315_transition: BlockNumber,
/// Number of first block where EIP-2929 rules begin.
pub eip2929_transition: BlockNumber,
/// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
pub dust_protection_transition: BlockNumber,
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled.
@@ -209,6 +211,7 @@ impl CommonParams {
|| block_number >= self.eip1283_reenable_transition;
schedule.eip1706 = block_number >= self.eip1706_transition;
schedule.have_subs = block_number >= self.eip2315_transition;
schedule.eip2929 = block_number >= self.eip2929_transition;
if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true;
@@ -222,6 +225,24 @@ impl CommonParams {
if block_number >= self.eip210_transition {
schedule.blockhash_gas = 800;
}
if block_number >= self.eip2929_transition {
schedule.eip2929 = true;
schedule.eip1283 = true;
schedule.call_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.balance_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodecopy_base_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodehash_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodesize_gas = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.cold_sload_cost = ::vm::schedule::EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = ::vm::schedule::EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.warm_storage_read_cost = ::vm::schedule::EIP2929_WARM_STORAGE_READ_COST;
schedule.sload_gas = ::vm::schedule::EIP2929_WARM_STORAGE_READ_COST;
schedule.sstore_reset_gas = ::vm::schedule::EIP2929_SSTORE_RESET_GAS;
}
if block_number >= self.dust_protection_transition {
schedule.kill_dust = match self.remove_dust_contracts {
true => ::vm::CleanDustMode::WithCodeAndStorage,
@@ -346,6 +367,9 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2315_transition: p
.eip2315_transition
.map_or_else(BlockNumber::max_value, Into::into),
eip2929_transition: p
.eip2929_transition
.map_or_else(BlockNumber::max_value, Into::into),
dust_protection_transition: p
.dust_protection_transition
.map_or_else(BlockNumber::max_value, Into::into),
@@ -757,6 +781,7 @@ impl Spec {
data: None,
call_type: CallType::None,
params_type: ParamsType::Embedded,
access_list: AccessList::default(),
};
let mut substate = Substate::new();

View File

@@ -20,6 +20,7 @@ use ethereum_types::Address;
use evm::{CleanDustMode, Schedule};
use std::collections::HashSet;
use types::log_entry::LogEntry;
use vm::access_list::AccessList;
/// State changes which should be applied in finalize,
/// after transaction is fully executed.
@@ -39,6 +40,9 @@ pub struct Substate {
/// Created contracts.
pub contracts_created: Vec<Address>,
/// List of accesses addresses and slots
pub access_list: AccessList,
}
impl Substate {
@@ -46,6 +50,17 @@ impl Substate {
pub fn new() -> Self {
Substate::default()
}
/// Creates a new substate from an access list
pub fn from_access_list(access_list: &AccessList) -> Self {
Self {
suicides: HashSet::default(),
touched: HashSet::default(),
logs: Vec::default(),
sstore_clears_refund: 0,
contracts_created: Vec::default(),
access_list: access_list.clone(),
}
}
/// Merge secondary substate `s` into self, accruing each element correspondingly.
pub fn accrue(&mut self, s: Substate) {

View File

@@ -24,7 +24,7 @@ use std::sync::Arc;
use test_helpers::get_temp_state_with_factory;
use trace::{NoopTracer, NoopVMTracer};
use types::transaction::SYSTEM_ADDRESS;
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use rustc_hex::FromHex;
@@ -62,6 +62,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: Some(H256::from(i - 1).to_vec()),
call_type: CallType::Call,
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);
@@ -85,6 +86,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: None,
call_type: CallType::Call,
params_type: ParamsType::Separate,
access_list: AccessList::default(),
};
let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);