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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 921 additions and 199 deletions

1
Cargo.lock generated
View File

@ -1505,6 +1505,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bit-set", "bit-set",
"criterion 0.2.11", "criterion 0.2.11",
"ethcore-builtin",
"ethereum-types 0.4.2", "ethereum-types 0.4.2",
"heapsize", "heapsize",
"hex-literal", "hex-literal",

View File

@ -15,6 +15,7 @@ vm = { path = "../vm" }
keccak-hash = "0.1" keccak-hash = "0.1"
parking_lot = "0.7" parking_lot = "0.7"
memory-cache = { path = "../../util/memory-cache" } memory-cache = { path = "../../util/memory-cache" }
ethcore-builtin = { path = "../builtin" }
num-bigint = "0.2" num-bigint = "0.2"
[dev-dependencies] [dev-dependencies]

View File

@ -33,7 +33,7 @@ use criterion::{black_box, Bencher, Criterion};
use ethereum_types::{Address, U256}; use ethereum_types::{Address, U256};
use evm::Factory; use evm::Factory;
use rustc_hex::FromHex; use rustc_hex::FromHex;
use std::{str::FromStr, sync::Arc}; use std::{collections::BTreeMap, str::FromStr, sync::Arc};
use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result}; use vm::{tests::FakeExt, ActionParams, Ext, GasLeft, Result};
criterion_group!( criterion_group!(

View File

@ -15,9 +15,10 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use super::u256_to_address; use super::u256_to_address;
use ethereum_types::{H256, U256}; use ethereum_types::{Address, H256, U256};
use std::cmp; use std::cmp;
use super::stack::VecStack;
use evm; use evm;
use instructions::{self, Instruction, InstructionInfo}; use instructions::{self, Instruction, InstructionInfo};
use interpreter::stack::Stack; use interpreter::stack::Stack;
@ -115,26 +116,38 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
ext: &dyn vm::Ext, ext: &dyn vm::Ext,
instruction: Instruction, instruction: Instruction,
info: &InstructionInfo, info: &InstructionInfo,
stack: &dyn Stack<U256>, stack: &VecStack<U256>,
current_address: &Address,
current_mem_size: usize, current_mem_size: usize,
) -> vm::Result<InstructionRequirements<Gas>> { ) -> vm::Result<InstructionRequirements<Gas>> {
let schedule = ext.schedule(); let schedule = ext.schedule();
let tier = info.tier.idx(); let tier = info.tier.idx();
let default_gas = Gas::from(schedule.tier_step_gas[tier]); let default_gas = Gas::from(schedule.tier_step_gas[tier]);
let accessed_addresses_gas = |addr: &Address, cold_cost: usize| -> Gas {
if ext.al_contains_address(addr) {
schedule.warm_storage_read_cost.into()
} else {
cold_cost.into()
}
};
let cost = match instruction { let cost = match instruction {
instructions::JUMPDEST => Request::Gas(Gas::from(1)), instructions::JUMPDEST => Request::Gas(Gas::from(1)),
instructions::SSTORE => { instructions::SSTORE => {
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) { if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
return Err(vm::Error::OutOfGas); return Err(vm::Error::OutOfGas);
} }
let address = H256::from(stack.peek(0)); let key = H256::from(stack.peek(0));
let newval = stack.peek(1); let newval = stack.peek(1);
let val = U256::from(&*ext.storage_at(&address)?); let val = U256::from(&*ext.storage_at(&key)?);
let is_cold = !ext.al_contains_storage_key(current_address, &key);
let gas = if schedule.eip1283 { let gas = if schedule.eip1283 {
let orig = U256::from(&*ext.initial_storage_at(&address)?); let orig = U256::from(&*ext.initial_storage_at(&key)?);
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval) calculate_eip1283_eip2929_sstore_gas(schedule, is_cold, &orig, &val, &newval)
} else { } else {
if val.is_zero() && !newval.is_zero() { if val.is_zero() && !newval.is_zero() {
schedule.sstore_set_gas schedule.sstore_set_gas
@ -144,17 +157,40 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
schedule.sstore_reset_gas schedule.sstore_reset_gas
} }
}; };
Request::Gas(Gas::from(gas))
Request::Gas(gas.into())
}
instructions::SLOAD => {
let key = H256::from(stack.peek(0));
let gas = if ext.al_is_enabled() {
if ext.al_contains_storage_key(current_address, &key) {
schedule.warm_storage_read_cost
} else {
schedule.cold_sload_cost
}
} else {
schedule.sload_gas
};
Request::Gas(gas.into())
}
instructions::BALANCE => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.balance_gas))
}
instructions::EXTCODESIZE => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.extcodesize_gas))
}
instructions::EXTCODEHASH => {
let address = u256_to_address(stack.peek(0));
Request::Gas(accessed_addresses_gas(&address, schedule.extcodehash_gas))
} }
instructions::SLOAD => Request::Gas(Gas::from(schedule.sload_gas)),
instructions::BALANCE => Request::Gas(Gas::from(schedule.balance_gas)),
instructions::EXTCODESIZE => Request::Gas(Gas::from(schedule.extcodesize_gas)),
instructions::EXTCODEHASH => Request::Gas(Gas::from(schedule.extcodehash_gas)),
instructions::SUICIDE => { instructions::SUICIDE => {
let mut gas = Gas::from(schedule.suicide_gas); let mut gas = Gas::from(schedule.suicide_gas);
let is_value_transfer = !ext.origin_balance()?.is_zero(); let is_value_transfer = !ext.origin_balance()?.is_zero();
let address = u256_to_address(stack.peek(0)); let address = u256_to_address(stack.peek(0));
if (!schedule.no_empty && !ext.exists(&address)?) if (!schedule.no_empty && !ext.exists(&address)?)
|| (schedule.no_empty || (schedule.no_empty
&& is_value_transfer && is_value_transfer
@ -164,6 +200,13 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into())); overflowing!(gas.overflow_add(schedule.suicide_to_new_account_cost.into()));
} }
if !ext.al_contains_address(&address) {
// EIP2929 If the ETH recipient of a SELFDESTRUCT is not in accessed_addresses
// (regardless of whether or not the amount sent is nonzero),
// charge an additional COLD_ACCOUNT_ACCESS_COST on top of the existing gas costs,
gas = Gas::from(gas.as_usize() + schedule.cold_account_access_cost);
}
Request::Gas(gas) Request::Gas(gas)
} }
instructions::MSTORE | instructions::MLOAD => { instructions::MSTORE | instructions::MLOAD => {
@ -189,11 +232,15 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
Gas::from_u256(*stack.peek(2))?, Gas::from_u256(*stack.peek(2))?,
) )
} }
instructions::EXTCODECOPY => Request::GasMemCopy( instructions::EXTCODECOPY => {
schedule.extcodecopy_base_gas.into(), let address = u256_to_address(stack.peek(0));
mem_needed(stack.peek(1), stack.peek(3))?, let gas = accessed_addresses_gas(&address, schedule.extcodecopy_base_gas);
Gas::from_u256(*stack.peek(3))?, Request::GasMemCopy(
), gas,
mem_needed(stack.peek(1), stack.peek(3))?,
Gas::from_u256(*stack.peek(3))?,
)
}
instructions::LOG0 instructions::LOG0
| instructions::LOG1 | instructions::LOG1
| instructions::LOG2 | instructions::LOG2
@ -218,6 +265,8 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
); );
let address = u256_to_address(stack.peek(1)); let address = u256_to_address(stack.peek(1));
gas = accessed_addresses_gas(&address, gas.as_usize());
let is_value_transfer = !stack.peek(2).is_zero(); let is_value_transfer = !stack.peek(2).is_zero();
if instruction == instructions::CALL if instruction == instructions::CALL
@ -238,7 +287,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
Request::GasMemProvide(gas, mem, Some(requested)) Request::GasMemProvide(gas, mem, Some(requested))
} }
instructions::DELEGATECALL | instructions::STATICCALL => { instructions::DELEGATECALL | instructions::STATICCALL => {
let gas = Gas::from(schedule.call_gas); let mut gas = Gas::from(schedule.call_gas);
let address = u256_to_address(stack.peek(1));
gas = accessed_addresses_gas(&address, gas.as_usize());
let mem = cmp::max( let mem = cmp::max(
mem_needed(stack.peek(4), stack.peek(5))?, mem_needed(stack.peek(4), stack.peek(5))?,
mem_needed(stack.peek(2), stack.peek(3))?, mem_needed(stack.peek(2), stack.peek(3))?,
@ -389,41 +441,39 @@ fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
} }
#[inline] #[inline]
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>( fn calculate_eip1283_eip2929_sstore_gas<Gas: evm::CostType>(
schedule: &Schedule, schedule: &Schedule,
is_cold: bool,
original: &U256, original: &U256,
current: &U256, current: &U256,
new: &U256, new: &U256,
) -> Gas { ) -> Gas {
Gas::from(if current == new { Gas::from(
// 1. If current value equals new value (this is a no-op), 200 gas is deducted. if current == new {
schedule.sload_gas // 1. If current value equals new value (this is a no-op).
} else {
// 2. If current value does not equal new value
if original == current {
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
if original.is_zero() {
// 2.1.1. If original value is 0, 20000 gas is deducted.
schedule.sstore_set_gas
} else {
// 2.1.2. Otherwise, 5000 gas is deducted.
schedule.sstore_reset_gas
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
}
} else {
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
schedule.sload_gas schedule.sload_gas
} else {
// 2.2.1. If original value is not 0 // 2. If current value does not equal new value
// 2.2.1.1. If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0. if original == current {
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter. // 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
if original.is_zero() {
// 2.2.2. If original value equals new value (this storage slot is reset) // 2.1.1. If original value is 0.
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter. schedule.sstore_set_gas
// 2.2.2.2. Otherwise, add 4800 gas to refund counter. } else {
} // 2.1.2. Otherwise.
}) schedule.sstore_reset_gas
}
} else {
// 2.2. If original value does not equal current value (this storage slot is dirty).
schedule.sload_gas
}
} + if is_cold {
// EIP2929 SSTORE changes section
schedule.cold_sload_cost
} else {
0
},
)
} }
pub fn handle_eip1283_sstore_clears_refund( pub fn handle_eip1283_sstore_clears_refund(

View File

@ -398,8 +398,14 @@ impl<Cost: CostType> Interpreter<Cost> {
.gasometer .gasometer
.as_mut() .as_mut()
.expect(GASOMETER_PROOF) .expect(GASOMETER_PROOF)
.requirements(ext, instruction, info, &self.stack, self.mem.size()) .requirements(
{ ext,
instruction,
info,
&self.stack,
&self.params.address,
self.mem.size(),
) {
Ok(t) => t, Ok(t) => t,
Err(e) => return InterpreterResult::Done(Err(e)), Err(e) => return InterpreterResult::Done(Err(e)),
}; };
@ -709,6 +715,15 @@ impl<Cost: CostType> Interpreter<Cost> {
return Ok(InstructionResult::UnusedGas(create_gas)); return Ok(InstructionResult::UnusedGas(create_gas));
} }
let contract_address = {
let contract_code = self.mem.read_slice(init_off, init_size);
ext.calc_address(contract_code, address_scheme)
};
if let Some(contract_address) = contract_address {
ext.al_insert_address(contract_address);
}
let contract_code = self.mem.read_slice(init_off, init_size); let contract_code = self.mem.read_slice(init_off, init_size);
let create_result = ext.create( let create_result = ext.create(
@ -777,6 +792,8 @@ impl<Cost: CostType> Interpreter<Cost> {
)) ))
.0; .0;
ext.al_insert_address(code_address);
// Get sender & receive addresses, check if we have balance // Get sender & receive addresses, check if we have balance
let (sender_address, receive_address, has_balance, call_type) = match instruction { let (sender_address, receive_address, has_balance, call_type) = match instruction {
instructions::CALL => { instructions::CALL => {
@ -903,8 +920,9 @@ impl<Cost: CostType> Interpreter<Cost> {
return Ok(InstructionResult::StopExecution); return Ok(InstructionResult::StopExecution);
} }
instructions::SUICIDE => { instructions::SUICIDE => {
let address = self.stack.pop_back(); let address = u256_to_address(&self.stack.pop_back());
ext.suicide(&u256_to_address(&address))?; ext.al_insert_address(address.clone());
ext.suicide(&address)?;
return Ok(InstructionResult::StopExecution); return Ok(InstructionResult::StopExecution);
} }
instructions::LOG0 instructions::LOG0
@ -991,15 +1009,17 @@ impl<Cost: CostType> Interpreter<Cost> {
let key = H256::from(&self.stack.pop_back()); let key = H256::from(&self.stack.pop_back());
let word = U256::from(&*ext.storage_at(&key)?); let word = U256::from(&*ext.storage_at(&key)?);
self.stack.push(word); self.stack.push(word);
ext.al_insert_storage_key(self.params.address, key);
} }
instructions::SSTORE => { instructions::SSTORE => {
let address = H256::from(&self.stack.pop_back()); let key = H256::from(&self.stack.pop_back());
let val = self.stack.pop_back(); let val = self.stack.pop_back();
let current_val = U256::from(&*ext.storage_at(&address)?); let current_val = U256::from(&*ext.storage_at(&key)?);
// Increase refund for clear // Increase refund for clear
if ext.schedule().eip1283 { if ext.schedule().eip1283 {
let original_val = U256::from(&*ext.initial_storage_at(&address)?); let original_val = U256::from(&*ext.initial_storage_at(&key)?);
gasometer::handle_eip1283_sstore_clears_refund( gasometer::handle_eip1283_sstore_clears_refund(
ext, ext,
&original_val, &original_val,
@ -1012,7 +1032,8 @@ impl<Cost: CostType> Interpreter<Cost> {
ext.add_sstore_refund(sstore_clears_schedule); ext.add_sstore_refund(sstore_clears_schedule);
} }
} }
ext.set_storage(address, H256::from(&val))?; ext.set_storage(key, H256::from(&val))?;
ext.al_insert_storage_key(self.params.address, key);
} }
instructions::PC => { instructions::PC => {
self.stack.push(U256::from(self.reader.position - 1)); self.stack.push(U256::from(self.reader.position - 1));
@ -1031,6 +1052,7 @@ impl<Cost: CostType> Interpreter<Cost> {
let address = u256_to_address(&self.stack.pop_back()); let address = u256_to_address(&self.stack.pop_back());
let balance = ext.balance(&address)?; let balance = ext.balance(&address)?;
self.stack.push(balance); self.stack.push(balance);
ext.al_insert_address(address);
} }
instructions::CALLER => { instructions::CALLER => {
self.stack.push(address_to_u256(self.params.sender.clone())); self.stack.push(address_to_u256(self.params.sender.clone()));
@ -1068,11 +1090,15 @@ impl<Cost: CostType> Interpreter<Cost> {
instructions::EXTCODESIZE => { instructions::EXTCODESIZE => {
let address = u256_to_address(&self.stack.pop_back()); let address = u256_to_address(&self.stack.pop_back());
let len = ext.extcodesize(&address)?.unwrap_or(0); let len = ext.extcodesize(&address)?.unwrap_or(0);
ext.al_insert_address(address);
self.stack.push(U256::from(len)); self.stack.push(U256::from(len));
} }
instructions::EXTCODEHASH => { instructions::EXTCODEHASH => {
let address = u256_to_address(&self.stack.pop_back()); let address = u256_to_address(&self.stack.pop_back());
let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero); let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero);
ext.al_insert_address(address);
self.stack.push(U256::from(hash)); self.stack.push(U256::from(hash));
} }
instructions::CALLDATACOPY => { instructions::CALLDATACOPY => {
@ -1108,6 +1134,7 @@ impl<Cost: CostType> Interpreter<Cost> {
&mut self.stack, &mut self.stack,
code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]), code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]),
); );
ext.al_insert_address(address);
} }
instructions::GASPRICE => { instructions::GASPRICE => {
self.stack.push(self.params.gas_price.clone()); self.stack.push(self.params.gas_price.clone());

View File

@ -17,6 +17,7 @@
//! Ethereum virtual machine. //! Ethereum virtual machine.
extern crate bit_set; extern crate bit_set;
extern crate ethcore_builtin as builtin;
extern crate ethereum_types; extern crate ethereum_types;
extern crate heapsize; extern crate heapsize;
extern crate keccak_hash as hash; extern crate keccak_hash as hash;

View File

@ -1554,6 +1554,113 @@ fn test_sar(factory: super::Factory) {
); );
} }
// from https://gist.github.com/holiman/174548cad102096858583c6fbbb0649a
evm_test! {test_access_list_ext_at_precompiles: test_access_list_ext_at_precompiles_int}
fn test_access_list_ext_at_precompiles(factory: super::Factory) {
// 6001 3f 50
// 6002 3b 50
// 6003 31 50
// 60f1 3f 50
// 60f2 3b 50
// 60f3 31 50
// 60f2 3f 50
// 60f3 3b 50
// 60f1 31 50
// 32 31 50
// 30 31 50
// 00
let code = hex!(
"60013f5060023b506003315060f13f5060f23b5060f3315060f23f5060f33b5060f1315032315030315000"
)
.to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(8653);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[
Address::from("0x0000000000000000000000000000000000000001"),
Address::from("0x0000000000000000000000000000000000000002"),
Address::from("0x0000000000000000000000000000000000000003"),
],
);
let gas_left = {
let vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
};
assert_eq!(gas_left, U256::from(0));
}
evm_test! {test_access_list_extcodecopy_twice: test_access_list_extcodecopy_twice_int}
fn test_access_list_extcodecopy_twice(factory: super::Factory) {
let code = hex!("60006000600060ff3c60006000600060ff3c600060006000303c").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(2835);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[],
);
let gas_left = {
let vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
};
assert_eq!(gas_left, U256::from(0));
}
evm_test! {test_access_list_sload_sstore: test_access_list_sload_sstore_int}
fn test_access_list_sload_sstore(factory: super::Factory) {
// 6001 54 50 sload( 0x1) pop
// 6011 6001 55 sstore(loc: 0x01, val:0x11) 20000
// 6011 6002 55 sstore(loc: 0x02, val:0x11) 20000 + 2100
// 6011 6002 55 sstore(loc: 0x02, val:0x11) 100
// 6002 54 sload(0x2)
// 6001 54 sload(0x1)
let code = hex!("60015450 6011600155 6011600255 6011600255 600254 600154").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(44529);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[],
);
let gas_left = {
let vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
};
assert_eq!(gas_left, U256::from(0));
}
evm_test! {test_access_list_cheap_expensive_cheap: test_access_list_cheap_expensive_cheap_int}
fn test_access_list_cheap_expensive_cheap(factory: super::Factory) {
let code =
hex!("60008080808060046000f15060008080808060ff6000f15060008080808060ff6000fa50").to_vec();
let mut params = ActionParams::default();
params.gas = U256::from(2869);
params.code = Some(Arc::new(code));
let mut ext = FakeExt::new_yolo3(
Address::from("0x0000000000000000000000000000000000000000"),
Address::from("0x000000000000000000000000636F6E7472616374"),
&[Address::from("0x0000000000000000000000000000000000000004")],
);
let gas_left = {
let vm = factory.create(params, ext.schedule(), ext.depth());
test_finalize(vm.exec(&mut ext).ok().unwrap()).unwrap()
};
assert_eq!(gas_left, U256::from(0));
}
fn push_two_pop_one_constantinople_test( fn push_two_pop_one_constantinople_test(
factory: &super::Factory, factory: &super::Factory,
opcode: u8, opcode: u8,

View File

@ -1,5 +1,5 @@
{ {
"name": "Istanbul (test)", "name": "Berlin (test)",
"engine": { "engine": {
"Ethash": { "Ethash": {
"params": { "params": {
@ -44,7 +44,6 @@
"eip1884Transition": "0x0", "eip1884Transition": "0x0",
"eip2028Transition": "0x0", "eip2028Transition": "0x0",
"eip2315Transition": "0x0" "eip2315Transition": "0x0"
}, },
"genesis": { "genesis": {
"seal": { "seal": {

View File

@ -0,0 +1,123 @@
{
"name": "Yolo (test)",
"engine": {
"Ethash": {
"params": {
"minimumDifficulty": "0x020000",
"difficultyBoundDivisor": "0x0800",
"durationLimit": "0x0d",
"blockReward": "0x1BC16D674EC80000",
"homesteadTransition": "0x0",
"eip100bTransition": "0x0",
"difficultyBombDelays": {
"0": 5000000
}
}
}
},
"params": {
"gasLimitBoundDivisor": "0x0400",
"registrar" : "0xc6d9d2cd449a754c494264e1809c50e34d64562b",
"accountStartNonce": "0x00",
"maximumExtraDataSize": "0x20",
"minGasLimit": "0x1388",
"networkID" : "0x1",
"maxCodeSize": 24576,
"maxCodeSizeTransition": "0x0",
"eip150Transition": "0x0",
"eip160Transition": "0x0",
"eip161abcTransition": "0x0",
"eip161dTransition": "0x0",
"eip140Transition": "0x0",
"eip211Transition": "0x0",
"eip214Transition": "0x0",
"eip155Transition": "0x0",
"eip658Transition": "0x0",
"eip145Transition": "0x0",
"eip1014Transition": "0x0",
"eip1052Transition": "0x0",
"eip1283Transition": "0x0",
"eip1283DisableTransition": "0x0",
"eip1283ReenableTransition": "0x0",
"eip1344Transition": "0x0",
"eip1706Transition": "0x0",
"eip1884Transition": "0x0",
"eip2028Transition": "0x0",
"eip2315Transition": "0x0",
"eip2929Transition": "0x0"
},
"genesis": {
"seal": {
"ethereum": {
"nonce": "0x0000000000000042",
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
}
},
"difficulty": "0x400000000",
"author": "0x0000000000000000000000000000000000000000",
"timestamp": "0x00",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
"gasLimit": "0x1388"
},
"accounts": {
"0000000000000000000000000000000000000001": { "balance": "1", "builtin": { "name": "ecrecover", "pricing": { "linear": { "base": 3000, "word": 0 } } } },
"0000000000000000000000000000000000000002": { "balance": "1", "builtin": { "name": "sha256", "pricing": { "linear": { "base": 60, "word": 12 } } } },
"0000000000000000000000000000000000000003": { "balance": "1", "builtin": { "name": "ripemd160", "pricing": { "linear": { "base": 600, "word": 120 } } } },
"0000000000000000000000000000000000000004": { "balance": "1", "builtin": { "name": "identity", "pricing": { "linear": { "base": 15, "word": 3 } } } },
"0000000000000000000000000000000000000005": { "builtin": { "name": "modexp", "activate_at": "0x00", "pricing": { "modexp": { "divisor": 20 } } } },
"0000000000000000000000000000000000000006": {
"builtin": {
"name": "alt_bn128_add",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 500 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 150 }}
}
}
}
},
"0000000000000000000000000000000000000007": {
"builtin": {
"name": "alt_bn128_mul",
"pricing": {
"0": {
"price": { "alt_bn128_const_operations": { "price": 40000 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_const_operations": { "price": 6000 }}
}
}
}
},
"0000000000000000000000000000000000000008": {
"builtin": {
"name": "alt_bn128_pairing",
"pricing": {
"0": {
"price": { "alt_bn128_pairing": { "base": 100000, "pair": 80000 }}
},
"0": {
"info": "EIP 1108 transition",
"price": { "alt_bn128_pairing": { "base": 45000, "pair": 34000 }}
}
}
}
},
"0000000000000000000000000000000000000009": {
"builtin": {
"name": "blake2_f",
"activate_at": "0x00",
"pricing": {
"blake2_f": {
"gas_per_round": 1
}
}
}
}
}
}

View File

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

View File

@ -252,6 +252,11 @@ pub fn new_berlin_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/berlin_test.json")) 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. /// Create a new Musicoin-MCIP3-era spec.
pub fn new_mcip3_test() -> Spec { pub fn new_mcip3_test() -> Spec {
load(None, include_bytes!("../../res/ethereum/mcip3_test.json")) load(None, include_bytes!("../../res/ethereum/mcip3_test.json"))

View File

@ -16,7 +16,6 @@
//! Transaction Execution environment. //! Transaction Execution environment.
use bytes::{Bytes, BytesRef}; use bytes::{Bytes, BytesRef};
use crossbeam_utils::thread;
use ethereum_types::{Address, H256, U256, U512}; use ethereum_types::{Address, H256, U256, U512};
use evm::{CallType, FinalizationResult, Finalize}; use evm::{CallType, FinalizationResult, Finalize};
use executed::ExecutionError; use executed::ExecutionError;
@ -31,26 +30,10 @@ use trace::{self, Tracer, VMTracer};
use transaction_ext::Transaction; use transaction_ext::Transaction;
use types::transaction::{Action, SignedTransaction}; use types::transaction::{Action, SignedTransaction};
use vm::{ use vm::{
self, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo, ResumeCall, self, AccessList, ActionParams, ActionValue, CleanDustMode, CreateContractAddress, EnvInfo,
ResumeCreate, ReturnData, Schedule, TrapError, 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"))] #[cfg(any(test, feature = "test-helpers"))]
/// Precompile that can never be prunned from state trie (0x3, only in tests) /// Precompile that can never be prunned from state trie (0x3, only in tests)
const UNPRUNABLE_PRECOMPILE_ADDRESS: Option<Address> = Some(ethereum_types::H160([ const UNPRUNABLE_PRECOMPILE_ADDRESS: Option<Address> = Some(ethereum_types::H160([
@ -252,6 +235,18 @@ pub struct CallCreateExecutive<'a> {
} }
impl<'a> 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. /// Create a new call executive using raw data.
pub fn new_call_raw( pub fn new_call_raw(
params: ActionParams, params: ActionParams,
@ -287,7 +282,8 @@ impl<'a> CallCreateExecutive<'a> {
CallCreateExecutiveKind::CallBuiltin(params) CallCreateExecutiveKind::CallBuiltin(params)
} else { } else {
if params.code.is_some() { if params.code.is_some() {
CallCreateExecutiveKind::ExecCall(params, Substate::new()) let substate = Self::new_substate(&params, schedule);
CallCreateExecutiveKind::ExecCall(params, substate)
} else { } else {
CallCreateExecutiveKind::Transfer(params) CallCreateExecutiveKind::Transfer(params)
} }
@ -327,7 +323,8 @@ impl<'a> CallCreateExecutive<'a> {
let gas = params.gas; 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 { Self {
info, info,
@ -457,6 +454,7 @@ impl<'a> CallCreateExecutive<'a> {
} }
} }
state.revert_to_checkpoint(); state.revert_to_checkpoint();
un_substate.access_list.rollback();
} }
Ok(_) | Err(vm::Error::Internal(_)) => { Ok(_) | Err(vm::Error::Internal(_)) => {
state.discard_checkpoint(); 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. // NOTE: there can be no invalid transactions from this point.
if !schedule.keep_unsigned_nonce || !t.is_unsigned() { if !schedule.keep_unsigned_nonce || !t.is_unsigned() {
@ -1221,6 +1226,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: vm::ParamsType::Embedded, params_type: vm::ParamsType::Embedded,
access_list: access_list,
}; };
let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer); let res = self.create(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res { let out = match &res {
@ -1243,6 +1249,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
data: Some(t.data.clone()), data: Some(t.data.clone()),
call_type: CallType::Call, call_type: CallType::Call,
params_type: vm::ParamsType::Separate, params_type: vm::ParamsType::Separate,
access_list: access_list,
}; };
let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer); let res = self.call(params, &mut substate, &mut tracer, &mut vm_tracer);
let out = match &res { let out = match &res {
@ -1325,48 +1332,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
result 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. /// Calls contract function with given contract params.
pub fn call<T, V>( pub fn call<T, V>(
&mut self, &mut self,
@ -1437,54 +1402,6 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
result 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. /// Creates contract with given contract params.
pub fn create<T, V>( pub fn create<T, V>(
&mut self, &mut self,

View File

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

View File

@ -167,6 +167,10 @@ where
Ok(ContractCreateResult::Created(contract_address, *gas)) 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( fn call(
&mut self, &mut self,
gas: &U256, gas: &U256,
@ -238,6 +242,26 @@ where
fn sub_sstore_refund(&mut self, value: usize) { fn sub_sstore_refund(&mut self, value: usize) {
self.ext.sub_sstore_refund(value) 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 /// 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(); let pre: PodState = test.pre_state.into();
for (spec_name, states) in test.post_states { for (spec_name, states) in test.post_states {
let total = states.len();
let spec = match EvmTestClient::spec_from_json(&spec_name) { let spec = match EvmTestClient::spec_from_json(&spec_name) {
Some(spec) => spec, Some(spec) => spec,
None => { None => {
@ -81,11 +80,10 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
for (i, state) in states.into_iter().enumerate() { for (i, state) in states.into_iter().enumerate() {
let info = format!( let info = format!(
" - state: {} | {:?} ({}/{}) ...", "TestState/{}/{:?}/{}/trie",
name, path.to_string_lossy(),
spec_name, spec_name,
i + 1, i
total
); );
if skip_test(&state_test, &name, &spec.name, i + 1) { if skip_test(&state_test, &name, &spec.name, i + 1) {
println!("{}: SKIPPED", info); 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 => { Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => {
println!( println!(
"{} !!! State mismatch (got: {}, expect: {}", "{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root info, state_root, post_root
); );
flushln!("{} fail", info); flushln!("{} fail", info);
@ -124,7 +122,7 @@ pub fn json_chain_test<H: FnMut(&str, HookType)>(
.. ..
})) if state_root != post_root => { })) if state_root != post_root => {
println!( println!(
"{} !!! State mismatch (got: {}, expect: {}", "{}: post state root mismatch: got {:?}, want {:?}",
info, state_root, post_root info, state_root, post_root
); );
println!("{} !!! Execution error: {:?}", info, error); println!("{} !!! Execution error: {:?}", info, error);

View File

@ -32,7 +32,8 @@ use types::{
BlockNumber, BlockNumber,
}; };
use vm::{ use vm::{
ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType, Schedule, AccessList, ActionParams, ActionValue, CallType, CreateContractAddress, EnvInfo, ParamsType,
Schedule,
}; };
use block::ExecutedBlock; use block::ExecutedBlock;
@ -201,6 +202,7 @@ impl EthereumMachine {
data, data,
call_type: call_type.unwrap_or(CallType::Call), call_type: call_type.unwrap_or(CallType::Call),
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
access_list: AccessList::default(),
}; };
let schedule = self.schedule(env_info.number); let schedule = self.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, self, &schedule); 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 rlp::{Rlp, RlpStream};
use rustc_hex::FromHex; use rustc_hex::FromHex;
use types::{header::Header, BlockNumber}; use types::{header::Header, BlockNumber};
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType}; use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use builtin::Builtin; use builtin::Builtin;
use engines::{ use engines::{
@ -135,6 +135,8 @@ pub struct CommonParams {
pub eip2028_transition: BlockNumber, pub eip2028_transition: BlockNumber,
/// Number of first block where EIP-2315 rules begin. /// Number of first block where EIP-2315 rules begin.
pub eip2315_transition: BlockNumber, 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. /// Number of first block where dust cleanup rules (EIP-168 and EIP169) begin.
pub dust_protection_transition: BlockNumber, pub dust_protection_transition: BlockNumber,
/// Nonce cap increase per block. Nonce cap is only checked if dust protection is enabled. /// 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; || block_number >= self.eip1283_reenable_transition;
schedule.eip1706 = block_number >= self.eip1706_transition; schedule.eip1706 = block_number >= self.eip1706_transition;
schedule.have_subs = block_number >= self.eip2315_transition; schedule.have_subs = block_number >= self.eip2315_transition;
schedule.eip2929 = block_number >= self.eip2929_transition;
if block_number >= self.eip1884_transition { if block_number >= self.eip1884_transition {
schedule.have_selfbalance = true; schedule.have_selfbalance = true;
@ -222,6 +225,24 @@ impl CommonParams {
if block_number >= self.eip210_transition { if block_number >= self.eip210_transition {
schedule.blockhash_gas = 800; 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 { if block_number >= self.dust_protection_transition {
schedule.kill_dust = match self.remove_dust_contracts { schedule.kill_dust = match self.remove_dust_contracts {
true => ::vm::CleanDustMode::WithCodeAndStorage, true => ::vm::CleanDustMode::WithCodeAndStorage,
@ -346,6 +367,9 @@ impl From<ethjson::spec::Params> for CommonParams {
eip2315_transition: p eip2315_transition: p
.eip2315_transition .eip2315_transition
.map_or_else(BlockNumber::max_value, Into::into), .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: p
.dust_protection_transition .dust_protection_transition
.map_or_else(BlockNumber::max_value, Into::into), .map_or_else(BlockNumber::max_value, Into::into),
@ -757,6 +781,7 @@ impl Spec {
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: ParamsType::Embedded, params_type: ParamsType::Embedded,
access_list: AccessList::default(),
}; };
let mut substate = Substate::new(); let mut substate = Substate::new();

View File

@ -20,6 +20,7 @@ use ethereum_types::Address;
use evm::{CleanDustMode, Schedule}; use evm::{CleanDustMode, Schedule};
use std::collections::HashSet; use std::collections::HashSet;
use types::log_entry::LogEntry; use types::log_entry::LogEntry;
use vm::access_list::AccessList;
/// State changes which should be applied in finalize, /// State changes which should be applied in finalize,
/// after transaction is fully executed. /// after transaction is fully executed.
@ -39,6 +40,9 @@ pub struct Substate {
/// Created contracts. /// Created contracts.
pub contracts_created: Vec<Address>, pub contracts_created: Vec<Address>,
/// List of accesses addresses and slots
pub access_list: AccessList,
} }
impl Substate { impl Substate {
@ -46,6 +50,17 @@ impl Substate {
pub fn new() -> Self { pub fn new() -> Self {
Substate::default() 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. /// Merge secondary substate `s` into self, accruing each element correspondingly.
pub fn accrue(&mut self, s: Substate) { 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 test_helpers::get_temp_state_with_factory;
use trace::{NoopTracer, NoopVMTracer}; use trace::{NoopTracer, NoopVMTracer};
use types::transaction::SYSTEM_ADDRESS; use types::transaction::SYSTEM_ADDRESS;
use vm::{ActionParams, ActionValue, CallType, EnvInfo, ParamsType}; use vm::{AccessList, ActionParams, ActionValue, CallType, EnvInfo, ParamsType};
use rustc_hex::FromHex; use rustc_hex::FromHex;
@ -62,6 +62,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: Some(H256::from(i - 1).to_vec()), data: Some(H256::from(i - 1).to_vec()),
call_type: CallType::Call, call_type: CallType::Call,
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
access_list: AccessList::default(),
}; };
let schedule = machine.schedule(env_info.number); let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);
@ -85,6 +86,7 @@ fn test_blockhash_eip210(factory: Factory) {
data: None, data: None,
call_type: CallType::Call, call_type: CallType::Call,
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
access_list: AccessList::default(),
}; };
let schedule = machine.schedule(env_info.number); let schedule = machine.schedule(env_info.number);
let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule); let mut ex = Executive::new(&mut state, &env_info, &machine, &schedule);

View File

@ -0,0 +1,260 @@
use ethereum_types::{Address, H256};
use std::{
borrow::Borrow,
collections::HashMap,
hash::{Hash, Hasher},
};
use std::{cell::RefCell, rc::Rc};
// Implementation of a hasheable borrowed pair
trait KeyPair<A, B> {
fn a(&self) -> &A;
fn b(&self) -> &B;
}
impl<'a, A, B> Borrow<dyn KeyPair<A, B> + 'a> for (A, B)
where
A: Eq + Hash + 'a,
B: Eq + Hash + 'a,
{
fn borrow(&self) -> &(dyn KeyPair<A, B> + 'a) {
self
}
}
impl<A: Hash, B: Hash> Hash for (dyn KeyPair<A, B> + '_) {
fn hash<H: Hasher>(&self, state: &mut H) {
self.a().hash(state);
self.b().hash(state);
}
}
impl<A: Eq, B: Eq> PartialEq for (dyn KeyPair<A, B> + '_) {
fn eq(&self, other: &Self) -> bool {
self.a() == other.a() && self.b() == other.b()
}
}
impl<A: Eq, B: Eq> Eq for (dyn KeyPair<A, B> + '_) {}
impl<A, B> KeyPair<A, B> for (A, B) {
fn a(&self) -> &A {
&self.0
}
fn b(&self) -> &B {
&self.1
}
}
impl<A, B> KeyPair<A, B> for (&A, &B) {
fn a(&self) -> &A {
self.0
}
fn b(&self) -> &B {
self.1
}
}
#[derive(Debug)]
struct Journal {
enabled: bool,
last_id: usize,
addresses: HashMap<Address, usize>,
storage_keys: HashMap<(Address, H256), usize>,
}
#[derive(Debug)]
pub struct AccessList {
id: usize,
journal: Rc<RefCell<Journal>>,
}
impl Clone for AccessList {
fn clone(&self) -> Self {
let mut journal = self.journal.as_ref().borrow_mut();
let id = journal.last_id + 1;
journal.last_id = id;
Self {
id: id,
journal: self.journal.clone(),
}
}
}
impl Default for AccessList {
fn default() -> Self {
AccessList::new(false)
}
}
impl std::fmt::Display for AccessList {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let journal = self.journal.as_ref().borrow();
for (addr, id) in journal.addresses.iter() {
write!(f, "| ADDR {} -> {}\n", addr, id)?;
}
for ((addr, slot), id) in journal.storage_keys.iter() {
write!(f, "| SLOT {}:{} -> {}\n", addr, slot, id)?;
}
Ok(())
}
}
impl AccessList {
/// Returns if the list is enabled
pub fn new(enabled: bool) -> Self {
let journal = Journal {
enabled,
last_id: 0,
addresses: HashMap::new(),
storage_keys: HashMap::new(),
};
Self {
id: 0,
journal: Rc::new(RefCell::new(journal)),
}
}
/// Returns if the list is enabled
pub fn is_enabled(&self) -> bool {
let journal = self.journal.as_ref().borrow();
journal.enabled
}
/// Enable the access list control
pub fn enable(&mut self) {
let mut journal = self.journal.as_ref().borrow_mut();
journal.enabled = true;
}
/// Checks if contains an storage key
pub fn contains_storage_key(&self, address: &Address, key: &H256) -> bool {
let journal = self.journal.as_ref().borrow();
if journal.enabled {
journal
.storage_keys
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
} else {
false
}
}
/// Inserts a storage key
pub fn insert_storage_key(&mut self, address: Address, key: H256) {
let mut journal = self.journal.as_ref().borrow_mut();
if journal.enabled
&& !journal
.storage_keys
.contains_key(&(address, key) as &dyn KeyPair<Address, H256>)
{
journal.storage_keys.insert((address, key), self.id);
}
}
/// Checks if contains an address
pub fn contains_address(&self, address: &Address) -> bool {
let journal = self.journal.as_ref().borrow();
if journal.enabled {
journal.addresses.contains_key(&address)
} else {
false
}
}
/// Inserts an address
pub fn insert_address(&mut self, address: Address) {
let mut journal = self.journal.as_ref().borrow_mut();
if journal.enabled && !journal.addresses.contains_key(&address) {
journal.addresses.insert(address, self.id);
}
}
/// Removes all changes in journal
pub fn rollback(&self) {
let mut journal = self.journal.as_ref().borrow_mut();
// `id < self.id` instead `id != self.if` is to take care about recursive calls
journal.addresses.retain(|_, id| *id < self.id);
journal.storage_keys.retain(|_, id| *id < self.id);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_accesslist_is_disabled() {
let access_list = AccessList::default();
assert_eq!(false, access_list.is_enabled());
}
#[test]
fn default_disabled_accesslist_does_nothing() {
let mut access_list = AccessList::default();
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
assert_eq!(false, access_list.contains_address(&Address::from(1)));
assert_eq!(
false,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
}
#[test]
fn default_enabled_accesslist_registers() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
assert_eq!(true, access_list.contains_address(&Address::from(1)));
assert_eq!(
true,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
}
#[test]
fn cloned_accesslist_registers_in_parent() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
let mut access_list_call = access_list.clone();
assert_eq!(true, access_list_call.contains_address(&Address::from(1)));
assert_eq!(
true,
access_list_call.contains_storage_key(&Address::from(2), &H256::from(3))
);
access_list.insert_address(Address::from(4));
assert_eq!(true, access_list_call.contains_address(&Address::from(4)));
assert_eq!(true, access_list.contains_address(&Address::from(4)));
}
#[test]
fn cloned_accesslist_rollbacks_in_parent() {
let mut access_list = AccessList::default();
access_list.enable();
assert_eq!(true, access_list.is_enabled());
access_list.insert_address(Address::from(1));
access_list.insert_storage_key(Address::from(2), H256::from(3));
let mut access_list_call = access_list.clone();
access_list_call.insert_address(Address::from(1));
access_list_call.insert_storage_key(Address::from(2), H256::from(3));
access_list_call.insert_address(Address::from(4));
let mut access_list_call_call = access_list.clone();
access_list_call_call.insert_address(Address::from(1));
access_list_call_call.insert_storage_key(Address::from(2), H256::from(3));
access_list_call_call.insert_address(Address::from(5));
access_list_call_call.insert_storage_key(Address::from(6), H256::from(7));
access_list_call.rollback();
assert_eq!(true, access_list.contains_address(&Address::from(1)));
assert_eq!(false, access_list.contains_address(&Address::from(4)));
assert_eq!(false, access_list.contains_address(&Address::from(5)));
assert_eq!(
true,
access_list.contains_storage_key(&Address::from(2), &H256::from(3))
);
assert_eq!(
false,
access_list.contains_storage_key(&Address::from(6), &H256::from(7))
);
}
}

View File

@ -15,13 +15,13 @@
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>. // along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! Evm input params. //! Evm input params.
use super::access_list::AccessList;
use bytes::Bytes; use bytes::Bytes;
use call_type::CallType;
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
use ethjson; use ethjson;
use hash::{keccak, KECCAK_EMPTY}; use hash::{keccak, KECCAK_EMPTY};
use call_type::CallType;
use std::sync::Arc; use std::sync::Arc;
/// Transaction value /// Transaction value
@ -90,6 +90,8 @@ pub struct ActionParams {
pub call_type: CallType, pub call_type: CallType,
/// Param types encoding /// Param types encoding
pub params_type: ParamsType, pub params_type: ParamsType,
/// Current access list
pub access_list: AccessList,
} }
impl Default for ActionParams { impl Default for ActionParams {
@ -108,6 +110,7 @@ impl Default for ActionParams {
data: None, data: None,
call_type: CallType::None, call_type: CallType::None,
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
access_list: AccessList::default(),
} }
} }
} }
@ -131,6 +134,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
false => CallType::Call, false => CallType::Call,
}, // TODO @debris is this correct? }, // TODO @debris is this correct?
params_type: ParamsType::Separate, params_type: ParamsType::Separate,
access_list: AccessList::default(),
} }
} }
} }

View File

@ -101,6 +101,9 @@ pub trait Ext {
trap: bool, trap: bool,
) -> ::std::result::Result<ContractCreateResult, TrapKind>; ) -> ::std::result::Result<ContractCreateResult, TrapKind>;
/// Returns the address that will be created in the create call
fn calc_address(&self, code: &[u8], address: CreateContractAddress) -> Option<Address>;
/// Message call. /// Message call.
/// ///
/// Returns Err, if we run out of gas. /// Returns Err, if we run out of gas.
@ -184,4 +187,19 @@ pub trait Ext {
/// Check if running in static context. /// Check if running in static context.
fn is_static(&self) -> bool; fn is_static(&self) -> bool;
/// Returns if the list is enabled
fn al_is_enabled(&self) -> bool;
/// Checks if contains an storage key
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool;
/// Inserts an storage key into the list
fn al_insert_storage_key(&mut self, address: Address, key: H256);
/// Checks if contains an address
fn al_contains_address(&self, address: &Address) -> bool;
/// Inserts an address into the list
fn al_insert_address(&mut self, address: Address);
} }

View File

@ -23,16 +23,18 @@ extern crate parity_bytes as bytes;
extern crate patricia_trie_ethereum as ethtrie; extern crate patricia_trie_ethereum as ethtrie;
extern crate rlp; extern crate rlp;
pub mod access_list;
mod action_params; mod action_params;
mod call_type; mod call_type;
mod env_info; mod env_info;
mod error; mod error;
mod ext; mod ext;
mod return_data; mod return_data;
mod schedule; pub mod schedule;
pub mod tests; pub mod tests;
pub use access_list::AccessList;
pub use action_params::{ActionParams, ActionValue, ParamsType}; pub use action_params::{ActionParams, ActionValue, ParamsType};
pub use call_type::CallType; pub use call_type::CallType;
pub use env_info::{EnvInfo, LastHashes}; pub use env_info::{EnvInfo, LastHashes};
@ -42,7 +44,7 @@ pub use return_data::{GasLeft, ReturnData};
pub use schedule::{CleanDustMode, Schedule, WasmCosts}; pub use schedule::{CleanDustMode, Schedule, WasmCosts};
/// Virtual Machine interface /// Virtual Machine interface
pub trait Exec: Send { pub trait Exec {
/// This function should be used to execute transaction. /// This function should be used to execute transaction.
/// It returns either an error, a known amount of gas left, or parameters to be used /// It returns either an error, a known amount of gas left, or parameters to be used
/// to compute the final gas left. /// to compute the final gas left.
@ -50,13 +52,13 @@ pub trait Exec: Send {
} }
/// Resume call interface /// Resume call interface
pub trait ResumeCall: Send { pub trait ResumeCall {
/// Resume an execution for call, returns back the Vm interface. /// Resume an execution for call, returns back the Vm interface.
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<dyn Exec>; fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<dyn Exec>;
} }
/// Resume create interface /// Resume create interface
pub trait ResumeCreate: Send { pub trait ResumeCreate {
/// Resume an execution from create, returns back the Vm interface. /// Resume an execution from create, returns back the Vm interface.
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<dyn Exec>; fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<dyn Exec>;
} }

View File

@ -16,6 +16,15 @@
//! Cost schedule and other parameterisations for the EVM. //! Cost schedule and other parameterisations for the EVM.
// Gas per non accessed address when sload
pub const EIP2929_COLD_SLOAD_COST: usize = 2100;
// Gas per non accessed address accessing account from other opcodes defined in EIP2929
pub const EIP2929_COLD_ACCOUNT_ACCESS_COST: usize = 2600;
// Gas per already accessed address
pub const EIP2929_WARM_STORAGE_READ_COST: usize = 100;
// Gas per sstore reset
pub const EIP2929_SSTORE_RESET_GAS: usize = 5000 - EIP2929_COLD_SLOAD_COST;
/// Definition of the cost schedule and other parameterisations for the EVM. /// Definition of the cost schedule and other parameterisations for the EVM.
#[derive(Debug)] #[derive(Debug)]
pub struct Schedule { pub struct Schedule {
@ -63,6 +72,12 @@ pub struct Schedule {
pub create_gas: usize, pub create_gas: usize,
/// Gas price for `*CALL*` opcodes /// Gas price for `*CALL*` opcodes
pub call_gas: usize, pub call_gas: usize,
/// EIP-2929 COLD_SLOAD_COST
pub cold_sload_cost: usize,
/// EIP-2929 COLD_ACCOUNT_ACCESS_COST
pub cold_account_access_cost: usize,
/// EIP-2929 WARM_STORAGE_READ_COST
pub warm_storage_read_cost: usize,
/// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0` /// Stipend for transfer for `CALL|CALLCODE` opcode when `value>0`
pub call_stipend: usize, pub call_stipend: usize,
/// Additional gas required for value transfer (`CALL|CALLCODE`) /// Additional gas required for value transfer (`CALL|CALLCODE`)
@ -132,6 +147,8 @@ pub struct Schedule {
pub keep_unsigned_nonce: bool, pub keep_unsigned_nonce: bool,
/// Wasm extra schedule settings, if wasm activated /// Wasm extra schedule settings, if wasm activated
pub wasm: Option<WasmCosts>, pub wasm: Option<WasmCosts>,
/// Enable EIP-2929 rules
pub eip2929: bool,
} }
/// Wasm cost table /// Wasm cost table
@ -245,6 +262,9 @@ impl Schedule {
log_topic_gas: 375, log_topic_gas: 375,
create_gas: 32000, create_gas: 32000,
call_gas: 700, call_gas: 700,
cold_account_access_cost: 0,
cold_sload_cost: 0,
warm_storage_read_cost: 0,
call_stipend: 2300, call_stipend: 2300,
call_value_transfer_gas: 9000, call_value_transfer_gas: 9000,
call_new_account_gas: 25000, call_new_account_gas: 25000,
@ -274,6 +294,7 @@ impl Schedule {
eip1706: false, eip1706: false,
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
wasm: None, wasm: None,
eip2929: false,
} }
} }
@ -290,7 +311,8 @@ impl Schedule {
/// Schedule for the Constantinople fork of the Ethereum main net. /// Schedule for the Constantinople fork of the Ethereum main net.
pub fn new_constantinople() -> Schedule { pub fn new_constantinople() -> Schedule {
let mut schedule = Self::new_byzantium(); let mut schedule = Self::new_byzantium();
schedule.have_bitwise_shifting = true; schedule.have_bitwise_shifting = true; // EIP 145
schedule.have_extcodehash = true; // EIP 1052
schedule schedule
} }
@ -310,6 +332,30 @@ impl Schedule {
pub fn new_berlin() -> Schedule { pub fn new_berlin() -> Schedule {
let mut schedule = Self::new_istanbul(); let mut schedule = Self::new_istanbul();
schedule.have_subs = true; // EIP 2315 schedule.have_subs = true; // EIP 2315
schedule
}
/// Schedule for the Yolov3 testnet of the Ethereum main net.
pub fn new_yolo3() -> Schedule {
let mut schedule = Self::new_istanbul();
schedule.have_subs = true; // EIP 2315
schedule.eip1283 = true;
schedule.eip2929 = true;
schedule.cold_sload_cost = EIP2929_COLD_SLOAD_COST;
schedule.cold_account_access_cost = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.warm_storage_read_cost = EIP2929_WARM_STORAGE_READ_COST;
schedule.sload_gas = EIP2929_WARM_STORAGE_READ_COST;
schedule.call_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.balance_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodecopy_base_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodehash_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.extcodesize_gas = EIP2929_COLD_ACCOUNT_ACCESS_COST;
schedule.sstore_reset_gas = EIP2929_SSTORE_RESET_GAS;
schedule schedule
} }
@ -342,6 +388,9 @@ impl Schedule {
log_topic_gas: 375, log_topic_gas: 375,
create_gas: 32000, create_gas: 32000,
call_gas: 40, call_gas: 40,
cold_account_access_cost: 0,
cold_sload_cost: 0,
warm_storage_read_cost: 0,
call_stipend: 2300, call_stipend: 2300,
call_value_transfer_gas: 9000, call_value_transfer_gas: 9000,
call_new_account_gas: 25000, call_new_account_gas: 25000,
@ -371,6 +420,7 @@ impl Schedule {
eip1706: false, eip1706: false,
keep_unsigned_nonce: false, keep_unsigned_nonce: false,
wasm: None, wasm: None,
eip2929: false,
} }
} }

View File

@ -19,6 +19,7 @@ use std::{
sync::Arc, sync::Arc,
}; };
use crate::access_list::AccessList;
use bytes::Bytes; use bytes::Bytes;
use error::TrapKind; use error::TrapKind;
use ethereum_types::{Address, H256, U256}; use ethereum_types::{Address, H256, U256};
@ -75,6 +76,7 @@ pub struct FakeExt {
pub balances: HashMap<Address, U256>, pub balances: HashMap<Address, U256>,
pub tracing: bool, pub tracing: bool,
pub is_static: bool, pub is_static: bool,
pub access_list: AccessList,
chain_id: u64, chain_id: u64,
} }
@ -122,6 +124,19 @@ impl FakeExt {
ext ext
} }
/// New fake externalities with YoloV2 schedule rules
pub fn new_yolo3(from: Address, to: Address, builtins: &[Address]) -> Self {
let mut ext = FakeExt::default();
ext.schedule = Schedule::new_yolo3();
ext.access_list.enable();
ext.access_list.insert_address(from);
ext.access_list.insert_address(to);
for builtin in builtins {
ext.access_list.insert_address(*builtin);
}
ext
}
/// Alter fake externalities to allow wasm /// Alter fake externalities to allow wasm
pub fn with_wasm(mut self) -> Self { pub fn with_wasm(mut self) -> Self {
self.schedule.wasm = Some(Default::default()); self.schedule.wasm = Some(Default::default());
@ -162,7 +177,7 @@ impl Ext for FakeExt {
} }
fn balance(&self, address: &Address) -> Result<U256> { fn balance(&self, address: &Address) -> Result<U256> {
Ok(self.balances[address]) Ok(self.balances.get(address).cloned().unwrap_or(U256::zero()))
} }
fn blockhash(&mut self, number: &U256) -> H256 { fn blockhash(&mut self, number: &U256) -> H256 {
@ -191,6 +206,10 @@ impl Ext for FakeExt {
Ok(ContractCreateResult::Failed) Ok(ContractCreateResult::Failed)
} }
fn calc_address(&self, _code: &[u8], _address: CreateContractAddress) -> Option<Address> {
None
}
fn call( fn call(
&mut self, &mut self,
gas: &U256, gas: &U256,
@ -276,4 +295,24 @@ impl Ext for FakeExt {
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool { fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool {
self.tracing self.tracing
} }
fn al_is_enabled(&self) -> bool {
self.access_list.is_enabled()
}
fn al_contains_storage_key(&self, address: &Address, key: &H256) -> bool {
self.access_list.contains_storage_key(address, key)
}
fn al_insert_storage_key(&mut self, address: Address, key: H256) {
self.access_list.insert_storage_key(address, key)
}
fn al_contains_address(&self, address: &Address) -> bool {
self.access_list.contains_address(address)
}
fn al_insert_address(&mut self, address: Address) {
self.access_list.insert_address(address)
}
} }

View File

@ -84,7 +84,7 @@ State test options:
--chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158, --chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158,
Frontier, Homestead, Byzantium, Constantinople, Frontier, Homestead, Byzantium, Constantinople,
ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5, ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5,
HomesteadToDaoAt5, HomesteadToEIP150At5). HomesteadToDaoAt5, HomesteadToEIP150At5, Berlin, Yolo3).
--only NAME Runs only a single test matching the name. --only NAME Runs only a single test matching the name.
General options: General options:
@ -197,7 +197,6 @@ fn run_state_test(args: Args) {
{ {
continue; continue;
} }
for (idx, state) in states.into_iter().enumerate() { for (idx, state) in states.into_iter().enumerate() {
let post_root = state.hash.into(); let post_root = state.hash.into();
let transaction = multitransaction.select(&state.indexes).into(); let transaction = multitransaction.select(&state.indexes).into();
@ -287,8 +286,16 @@ fn run_call<T: Informant>(args: Args, informant: T) {
if code.is_none() && to == Address::default() { if code.is_none() && to == Address::default() {
die("Either --code or --to is required."); die("Either --code or --to is required.");
} }
let mut params = ActionParams::default(); let mut params = ActionParams::default();
if spec.engine.params().eip2929_transition == 0 {
params.access_list.enable();
params.access_list.insert_address(from);
params.access_list.insert_address(to);
for (builtin, _) in spec.engine.builtins() {
params.access_list.insert_address(*builtin);
}
}
params.call_type = if code.is_none() { params.call_type = if code.is_none() {
CallType::Call CallType::Call
} else { } else {
@ -378,9 +385,16 @@ impl Args {
pub fn spec(&self) -> Result<spec::Spec, String> { pub fn spec(&self) -> Result<spec::Spec, String> {
Ok(match self.flag_chain { Ok(match self.flag_chain {
Some(ref filename) => { Some(ref spec_name) => {
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?; let fork_spec: Result<ethjson::spec::ForkSpec, _> =
spec::Spec::load(&::std::env::temp_dir(), file)? serde_json::from_str(&format!("{:?}", spec_name));
if let Ok(fork_spec) = fork_spec {
ethcore::client::EvmTestClient::spec_from_json(&fork_spec)
.expect("this forkspec is not defined")
} else {
let file = fs::File::open(spec_name).map_err(|e| format!("{}", e))?;
spec::Spec::load(&::std::env::temp_dir(), file)?
}
} }
None => ethcore::ethereum::new_foundation(&::std::env::temp_dir()), None => ethcore::ethereum::new_foundation(&::std::env::temp_dir()),
}) })

View File

@ -106,6 +106,8 @@ pub struct Params {
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub eip2315_transition: Option<Uint>, pub eip2315_transition: Option<Uint>,
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub eip2929_transition: Option<Uint>,
/// See `CommonParams` docs.
pub dust_protection_transition: Option<Uint>, pub dust_protection_transition: Option<Uint>,
/// See `CommonParams` docs. /// See `CommonParams` docs.
pub nonce_cap_increment: Option<Uint>, pub nonce_cap_increment: Option<Uint>,

View File

@ -38,6 +38,7 @@ pub enum ForkSpec {
ByzantiumToConstantinopleAt5, ByzantiumToConstantinopleAt5,
ByzantiumToConstantinopleFixAt5, ByzantiumToConstantinopleFixAt5,
Berlin, Berlin,
Yolo3,
} }
/// Spec deserialization. /// Spec deserialization.

View File

@ -27,7 +27,6 @@ use parking_lot::Mutex;
use types::transaction::{Action, Transaction}; use types::transaction::{Action, Transaction};
use ethkey::Secret; use ethkey::Secret;
use rustc_hex::ToHex;
use serde_json::to_value; use serde_json::to_value;
use v1::{ use v1::{
helpers::{ helpers::{
@ -92,6 +91,9 @@ fn setup_with(c: Config) -> PersonalTester {
tester tester
} }
#[cfg(test)]
use rustc_hex::ToHex;
#[test] #[test]
fn accounts() { fn accounts() {
let tester = setup(); let tester = setup();