EIP2929 with journaling + Yolov3 (#79)
This commit is contained in:
parent
50a4d5fa57
commit
6078eeaed7
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1505,6 +1505,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"criterion 0.2.11",
|
||||
"ethcore-builtin",
|
||||
"ethereum-types 0.4.2",
|
||||
"heapsize",
|
||||
"hex-literal",
|
||||
|
@ -15,6 +15,7 @@ vm = { path = "../vm" }
|
||||
keccak-hash = "0.1"
|
||||
parking_lot = "0.7"
|
||||
memory-cache = { path = "../../util/memory-cache" }
|
||||
ethcore-builtin = { path = "../builtin" }
|
||||
num-bigint = "0.2"
|
||||
|
||||
[dev-dependencies]
|
||||
|
@ -33,7 +33,7 @@ use criterion::{black_box, Bencher, Criterion};
|
||||
use ethereum_types::{Address, U256};
|
||||
use evm::Factory;
|
||||
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};
|
||||
|
||||
criterion_group!(
|
||||
|
@ -15,9 +15,10 @@
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::u256_to_address;
|
||||
use ethereum_types::{H256, U256};
|
||||
use ethereum_types::{Address, H256, U256};
|
||||
use std::cmp;
|
||||
|
||||
use super::stack::VecStack;
|
||||
use evm;
|
||||
use instructions::{self, Instruction, InstructionInfo};
|
||||
use interpreter::stack::Stack;
|
||||
@ -115,26 +116,38 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
ext: &dyn vm::Ext,
|
||||
instruction: Instruction,
|
||||
info: &InstructionInfo,
|
||||
stack: &dyn Stack<U256>,
|
||||
stack: &VecStack<U256>,
|
||||
current_address: &Address,
|
||||
current_mem_size: usize,
|
||||
) -> vm::Result<InstructionRequirements<Gas>> {
|
||||
let schedule = ext.schedule();
|
||||
|
||||
let tier = info.tier.idx();
|
||||
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 {
|
||||
instructions::JUMPDEST => Request::Gas(Gas::from(1)),
|
||||
instructions::SSTORE => {
|
||||
if schedule.eip1706 && self.current_gas <= Gas::from(schedule.call_stipend) {
|
||||
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 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 orig = U256::from(&*ext.initial_storage_at(&address)?);
|
||||
calculate_eip1283_sstore_gas(schedule, &orig, &val, &newval)
|
||||
let orig = U256::from(&*ext.initial_storage_at(&key)?);
|
||||
calculate_eip1283_eip2929_sstore_gas(schedule, is_cold, &orig, &val, &newval)
|
||||
} else {
|
||||
if val.is_zero() && !newval.is_zero() {
|
||||
schedule.sstore_set_gas
|
||||
@ -144,17 +157,40 @@ impl<Gas: evm::CostType> Gasometer<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 => {
|
||||
let mut gas = Gas::from(schedule.suicide_gas);
|
||||
|
||||
let is_value_transfer = !ext.origin_balance()?.is_zero();
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
|
||||
if (!schedule.no_empty && !ext.exists(&address)?)
|
||||
|| (schedule.no_empty
|
||||
&& is_value_transfer
|
||||
@ -164,6 +200,13 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
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)
|
||||
}
|
||||
instructions::MSTORE | instructions::MLOAD => {
|
||||
@ -189,11 +232,15 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
Gas::from_u256(*stack.peek(2))?,
|
||||
)
|
||||
}
|
||||
instructions::EXTCODECOPY => Request::GasMemCopy(
|
||||
schedule.extcodecopy_base_gas.into(),
|
||||
instructions::EXTCODECOPY => {
|
||||
let address = u256_to_address(stack.peek(0));
|
||||
let gas = accessed_addresses_gas(&address, schedule.extcodecopy_base_gas);
|
||||
Request::GasMemCopy(
|
||||
gas,
|
||||
mem_needed(stack.peek(1), stack.peek(3))?,
|
||||
Gas::from_u256(*stack.peek(3))?,
|
||||
),
|
||||
)
|
||||
}
|
||||
instructions::LOG0
|
||||
| instructions::LOG1
|
||||
| instructions::LOG2
|
||||
@ -218,6 +265,8 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
);
|
||||
|
||||
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();
|
||||
|
||||
if instruction == instructions::CALL
|
||||
@ -238,7 +287,10 @@ impl<Gas: evm::CostType> Gasometer<Gas> {
|
||||
Request::GasMemProvide(gas, mem, Some(requested))
|
||||
}
|
||||
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(
|
||||
mem_needed(stack.peek(4), stack.peek(5))?,
|
||||
mem_needed(stack.peek(2), stack.peek(3))?,
|
||||
@ -389,41 +441,39 @@ fn to_word_size<Gas: evm::CostType>(value: Gas) -> (Gas, bool) {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn calculate_eip1283_sstore_gas<Gas: evm::CostType>(
|
||||
fn calculate_eip1283_eip2929_sstore_gas<Gas: evm::CostType>(
|
||||
schedule: &Schedule,
|
||||
is_cold: bool,
|
||||
original: &U256,
|
||||
current: &U256,
|
||||
new: &U256,
|
||||
) -> Gas {
|
||||
Gas::from(if current == new {
|
||||
// 1. If current value equals new value (this is a no-op), 200 gas is deducted.
|
||||
Gas::from(
|
||||
if current == new {
|
||||
// 1. If current value equals new value (this is a no-op).
|
||||
schedule.sload_gas
|
||||
} 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.
|
||||
// 2.1.1. If original value is 0.
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
// 2.1.2. Otherwise.
|
||||
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.
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty).
|
||||
schedule.sload_gas
|
||||
|
||||
// 2.2.1. If original value is not 0
|
||||
// 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.
|
||||
// 2.2.1.2. If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
|
||||
|
||||
// 2.2.2. If original value equals new value (this storage slot is reset)
|
||||
// 2.2.2.1. If original value is 0, add 19800 gas to refund counter.
|
||||
// 2.2.2.2. Otherwise, add 4800 gas to refund counter.
|
||||
}
|
||||
})
|
||||
} + if is_cold {
|
||||
// EIP2929 SSTORE changes section
|
||||
schedule.cold_sload_cost
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handle_eip1283_sstore_clears_refund(
|
||||
|
@ -398,8 +398,14 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
.gasometer
|
||||
.as_mut()
|
||||
.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,
|
||||
Err(e) => return InterpreterResult::Done(Err(e)),
|
||||
};
|
||||
@ -709,6 +715,15 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
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 create_result = ext.create(
|
||||
@ -777,6 +792,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
))
|
||||
.0;
|
||||
|
||||
ext.al_insert_address(code_address);
|
||||
|
||||
// Get sender & receive addresses, check if we have balance
|
||||
let (sender_address, receive_address, has_balance, call_type) = match instruction {
|
||||
instructions::CALL => {
|
||||
@ -903,8 +920,9 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
return Ok(InstructionResult::StopExecution);
|
||||
}
|
||||
instructions::SUICIDE => {
|
||||
let address = self.stack.pop_back();
|
||||
ext.suicide(&u256_to_address(&address))?;
|
||||
let address = u256_to_address(&self.stack.pop_back());
|
||||
ext.al_insert_address(address.clone());
|
||||
ext.suicide(&address)?;
|
||||
return Ok(InstructionResult::StopExecution);
|
||||
}
|
||||
instructions::LOG0
|
||||
@ -991,15 +1009,17 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
let key = H256::from(&self.stack.pop_back());
|
||||
let word = U256::from(&*ext.storage_at(&key)?);
|
||||
self.stack.push(word);
|
||||
|
||||
ext.al_insert_storage_key(self.params.address, key);
|
||||
}
|
||||
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 current_val = U256::from(&*ext.storage_at(&address)?);
|
||||
let current_val = U256::from(&*ext.storage_at(&key)?);
|
||||
// Increase refund for clear
|
||||
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(
|
||||
ext,
|
||||
&original_val,
|
||||
@ -1012,7 +1032,8 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
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 => {
|
||||
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 balance = ext.balance(&address)?;
|
||||
self.stack.push(balance);
|
||||
ext.al_insert_address(address);
|
||||
}
|
||||
instructions::CALLER => {
|
||||
self.stack.push(address_to_u256(self.params.sender.clone()));
|
||||
@ -1068,11 +1090,15 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
instructions::EXTCODESIZE => {
|
||||
let address = u256_to_address(&self.stack.pop_back());
|
||||
let len = ext.extcodesize(&address)?.unwrap_or(0);
|
||||
|
||||
ext.al_insert_address(address);
|
||||
self.stack.push(U256::from(len));
|
||||
}
|
||||
instructions::EXTCODEHASH => {
|
||||
let address = u256_to_address(&self.stack.pop_back());
|
||||
let hash = ext.extcodehash(&address)?.unwrap_or_else(H256::zero);
|
||||
|
||||
ext.al_insert_address(address);
|
||||
self.stack.push(U256::from(hash));
|
||||
}
|
||||
instructions::CALLDATACOPY => {
|
||||
@ -1108,6 +1134,7 @@ impl<Cost: CostType> Interpreter<Cost> {
|
||||
&mut self.stack,
|
||||
code.as_ref().map(|c| &(*c)[..]).unwrap_or(&[]),
|
||||
);
|
||||
ext.al_insert_address(address);
|
||||
}
|
||||
instructions::GASPRICE => {
|
||||
self.stack.push(self.params.gas_price.clone());
|
||||
|
@ -17,6 +17,7 @@
|
||||
//! Ethereum virtual machine.
|
||||
|
||||
extern crate bit_set;
|
||||
extern crate ethcore_builtin as builtin;
|
||||
extern crate ethereum_types;
|
||||
extern crate heapsize;
|
||||
extern crate keccak_hash as hash;
|
||||
|
@ -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(
|
||||
factory: &super::Factory,
|
||||
opcode: u8,
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "Istanbul (test)",
|
||||
"name": "Berlin (test)",
|
||||
"engine": {
|
||||
"Ethash": {
|
||||
"params": {
|
||||
@ -44,7 +44,6 @@
|
||||
"eip1884Transition": "0x0",
|
||||
"eip2028Transition": "0x0",
|
||||
"eip2315Transition": "0x0"
|
||||
|
||||
},
|
||||
"genesis": {
|
||||
"seal": {
|
||||
|
123
ethcore/res/ethereum/yolo3_test.json
Normal file
123
ethcore/res/ethereum/yolo3_test.json
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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
|
||||
|
@ -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"))
|
||||
|
@ -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(¶ms.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(¶ms, 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(¶ms, 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,
|
||||
|
@ -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)]
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
260
ethcore/vm/src/access_list.rs
Normal file
260
ethcore/vm/src/access_list.rs
Normal 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))
|
||||
);
|
||||
}
|
||||
}
|
@ -15,13 +15,13 @@
|
||||
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! Evm input params.
|
||||
use super::access_list::AccessList;
|
||||
use bytes::Bytes;
|
||||
use call_type::CallType;
|
||||
use ethereum_types::{Address, H256, U256};
|
||||
use ethjson;
|
||||
use hash::{keccak, KECCAK_EMPTY};
|
||||
|
||||
use call_type::CallType;
|
||||
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Transaction value
|
||||
@ -90,6 +90,8 @@ pub struct ActionParams {
|
||||
pub call_type: CallType,
|
||||
/// Param types encoding
|
||||
pub params_type: ParamsType,
|
||||
/// Current access list
|
||||
pub access_list: AccessList,
|
||||
}
|
||||
|
||||
impl Default for ActionParams {
|
||||
@ -108,6 +110,7 @@ impl Default for ActionParams {
|
||||
data: None,
|
||||
call_type: CallType::None,
|
||||
params_type: ParamsType::Separate,
|
||||
access_list: AccessList::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -131,6 +134,7 @@ impl From<ethjson::vm::Transaction> for ActionParams {
|
||||
false => CallType::Call,
|
||||
}, // TODO @debris is this correct?
|
||||
params_type: ParamsType::Separate,
|
||||
access_list: AccessList::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +101,9 @@ pub trait Ext {
|
||||
trap: bool,
|
||||
) -> ::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.
|
||||
///
|
||||
/// Returns Err, if we run out of gas.
|
||||
@ -184,4 +187,19 @@ pub trait Ext {
|
||||
|
||||
/// Check if running in static context.
|
||||
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);
|
||||
}
|
||||
|
@ -23,16 +23,18 @@ extern crate parity_bytes as bytes;
|
||||
extern crate patricia_trie_ethereum as ethtrie;
|
||||
extern crate rlp;
|
||||
|
||||
pub mod access_list;
|
||||
mod action_params;
|
||||
mod call_type;
|
||||
mod env_info;
|
||||
mod error;
|
||||
mod ext;
|
||||
mod return_data;
|
||||
mod schedule;
|
||||
pub mod schedule;
|
||||
|
||||
pub mod tests;
|
||||
|
||||
pub use access_list::AccessList;
|
||||
pub use action_params::{ActionParams, ActionValue, ParamsType};
|
||||
pub use call_type::CallType;
|
||||
pub use env_info::{EnvInfo, LastHashes};
|
||||
@ -42,7 +44,7 @@ pub use return_data::{GasLeft, ReturnData};
|
||||
pub use schedule::{CleanDustMode, Schedule, WasmCosts};
|
||||
|
||||
/// Virtual Machine interface
|
||||
pub trait Exec: Send {
|
||||
pub trait Exec {
|
||||
/// This function should be used to execute transaction.
|
||||
/// It returns either an error, a known amount of gas left, or parameters to be used
|
||||
/// to compute the final gas left.
|
||||
@ -50,13 +52,13 @@ pub trait Exec: Send {
|
||||
}
|
||||
|
||||
/// Resume call interface
|
||||
pub trait ResumeCall: Send {
|
||||
pub trait ResumeCall {
|
||||
/// Resume an execution for call, returns back the Vm interface.
|
||||
fn resume_call(self: Box<Self>, result: MessageCallResult) -> Box<dyn Exec>;
|
||||
}
|
||||
|
||||
/// Resume create interface
|
||||
pub trait ResumeCreate: Send {
|
||||
pub trait ResumeCreate {
|
||||
/// Resume an execution from create, returns back the Vm interface.
|
||||
fn resume_create(self: Box<Self>, result: ContractCreateResult) -> Box<dyn Exec>;
|
||||
}
|
||||
|
@ -16,6 +16,15 @@
|
||||
|
||||
//! 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.
|
||||
#[derive(Debug)]
|
||||
pub struct Schedule {
|
||||
@ -63,6 +72,12 @@ pub struct Schedule {
|
||||
pub create_gas: usize,
|
||||
/// Gas price for `*CALL*` opcodes
|
||||
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`
|
||||
pub call_stipend: usize,
|
||||
/// Additional gas required for value transfer (`CALL|CALLCODE`)
|
||||
@ -132,6 +147,8 @@ pub struct Schedule {
|
||||
pub keep_unsigned_nonce: bool,
|
||||
/// Wasm extra schedule settings, if wasm activated
|
||||
pub wasm: Option<WasmCosts>,
|
||||
/// Enable EIP-2929 rules
|
||||
pub eip2929: bool,
|
||||
}
|
||||
|
||||
/// Wasm cost table
|
||||
@ -245,6 +262,9 @@ impl Schedule {
|
||||
log_topic_gas: 375,
|
||||
create_gas: 32000,
|
||||
call_gas: 700,
|
||||
cold_account_access_cost: 0,
|
||||
cold_sload_cost: 0,
|
||||
warm_storage_read_cost: 0,
|
||||
call_stipend: 2300,
|
||||
call_value_transfer_gas: 9000,
|
||||
call_new_account_gas: 25000,
|
||||
@ -274,6 +294,7 @@ impl Schedule {
|
||||
eip1706: false,
|
||||
keep_unsigned_nonce: false,
|
||||
wasm: None,
|
||||
eip2929: false,
|
||||
}
|
||||
}
|
||||
|
||||
@ -290,7 +311,8 @@ impl Schedule {
|
||||
/// Schedule for the Constantinople fork of the Ethereum main net.
|
||||
pub fn new_constantinople() -> Schedule {
|
||||
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
|
||||
}
|
||||
|
||||
@ -310,6 +332,30 @@ impl Schedule {
|
||||
pub fn new_berlin() -> Schedule {
|
||||
let mut schedule = Self::new_istanbul();
|
||||
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
|
||||
}
|
||||
|
||||
@ -342,6 +388,9 @@ impl Schedule {
|
||||
log_topic_gas: 375,
|
||||
create_gas: 32000,
|
||||
call_gas: 40,
|
||||
cold_account_access_cost: 0,
|
||||
cold_sload_cost: 0,
|
||||
warm_storage_read_cost: 0,
|
||||
call_stipend: 2300,
|
||||
call_value_transfer_gas: 9000,
|
||||
call_new_account_gas: 25000,
|
||||
@ -371,6 +420,7 @@ impl Schedule {
|
||||
eip1706: false,
|
||||
keep_unsigned_nonce: false,
|
||||
wasm: None,
|
||||
eip2929: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ use std::{
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use crate::access_list::AccessList;
|
||||
use bytes::Bytes;
|
||||
use error::TrapKind;
|
||||
use ethereum_types::{Address, H256, U256};
|
||||
@ -75,6 +76,7 @@ pub struct FakeExt {
|
||||
pub balances: HashMap<Address, U256>,
|
||||
pub tracing: bool,
|
||||
pub is_static: bool,
|
||||
pub access_list: AccessList,
|
||||
|
||||
chain_id: u64,
|
||||
}
|
||||
@ -122,6 +124,19 @@ impl FakeExt {
|
||||
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
|
||||
pub fn with_wasm(mut self) -> Self {
|
||||
self.schedule.wasm = Some(Default::default());
|
||||
@ -162,7 +177,7 @@ impl Ext for FakeExt {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -191,6 +206,10 @@ impl Ext for FakeExt {
|
||||
Ok(ContractCreateResult::Failed)
|
||||
}
|
||||
|
||||
fn calc_address(&self, _code: &[u8], _address: CreateContractAddress) -> Option<Address> {
|
||||
None
|
||||
}
|
||||
|
||||
fn call(
|
||||
&mut self,
|
||||
gas: &U256,
|
||||
@ -276,4 +295,24 @@ impl Ext for FakeExt {
|
||||
fn trace_next_instruction(&mut self, _pc: usize, _instruction: u8, _gas: U256) -> bool {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +84,7 @@ State test options:
|
||||
--chain CHAIN Run only from specific chain name (i.e. one of EIP150, EIP158,
|
||||
Frontier, Homestead, Byzantium, Constantinople,
|
||||
ConstantinopleFix, Istanbul, EIP158ToByzantiumAt5, FrontierToHomesteadAt5,
|
||||
HomesteadToDaoAt5, HomesteadToEIP150At5).
|
||||
HomesteadToDaoAt5, HomesteadToEIP150At5, Berlin, Yolo3).
|
||||
--only NAME Runs only a single test matching the name.
|
||||
|
||||
General options:
|
||||
@ -197,7 +197,6 @@ fn run_state_test(args: Args) {
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (idx, state) in states.into_iter().enumerate() {
|
||||
let post_root = state.hash.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() {
|
||||
die("Either --code or --to is required.");
|
||||
}
|
||||
|
||||
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() {
|
||||
CallType::Call
|
||||
} else {
|
||||
@ -378,10 +385,17 @@ impl Args {
|
||||
|
||||
pub fn spec(&self) -> Result<spec::Spec, String> {
|
||||
Ok(match self.flag_chain {
|
||||
Some(ref filename) => {
|
||||
let file = fs::File::open(filename).map_err(|e| format!("{}", e))?;
|
||||
Some(ref spec_name) => {
|
||||
let fork_spec: Result<ethjson::spec::ForkSpec, _> =
|
||||
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()),
|
||||
})
|
||||
}
|
||||
|
@ -106,6 +106,8 @@ pub struct Params {
|
||||
/// See `CommonParams` docs.
|
||||
pub eip2315_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
pub eip2929_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
pub dust_protection_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
pub nonce_cap_increment: Option<Uint>,
|
||||
|
@ -38,6 +38,7 @@ pub enum ForkSpec {
|
||||
ByzantiumToConstantinopleAt5,
|
||||
ByzantiumToConstantinopleFixAt5,
|
||||
Berlin,
|
||||
Yolo3,
|
||||
}
|
||||
|
||||
/// Spec deserialization.
|
||||
|
@ -27,7 +27,6 @@ use parking_lot::Mutex;
|
||||
use types::transaction::{Action, Transaction};
|
||||
|
||||
use ethkey::Secret;
|
||||
use rustc_hex::ToHex;
|
||||
use serde_json::to_value;
|
||||
use v1::{
|
||||
helpers::{
|
||||
@ -92,6 +91,9 @@ fn setup_with(c: Config) -> PersonalTester {
|
||||
tester
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use rustc_hex::ToHex;
|
||||
|
||||
#[test]
|
||||
fn accounts() {
|
||||
let tester = setup();
|
||||
|
Loading…
Reference in New Issue
Block a user