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 = [
|
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",
|
||||||
|
@ -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]
|
||||||
|
@ -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!(
|
||||||
|
@ -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));
|
||||||
|
let gas = accessed_addresses_gas(&address, schedule.extcodecopy_base_gas);
|
||||||
|
Request::GasMemCopy(
|
||||||
|
gas,
|
||||||
mem_needed(stack.peek(1), stack.peek(3))?,
|
mem_needed(stack.peek(1), stack.peek(3))?,
|
||||||
Gas::from_u256(*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 {
|
||||||
|
// 1. If current value equals new value (this is a no-op).
|
||||||
schedule.sload_gas
|
schedule.sload_gas
|
||||||
} else {
|
} else {
|
||||||
// 2. If current value does not equal new value
|
// 2. If current value does not equal new value
|
||||||
if original == current {
|
if original == current {
|
||||||
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
// 2.1. If original value equals current value (this storage slot has not been changed by the current execution context)
|
||||||
if original.is_zero() {
|
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
|
schedule.sstore_set_gas
|
||||||
} else {
|
} else {
|
||||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
// 2.1.2. Otherwise.
|
||||||
schedule.sstore_reset_gas
|
schedule.sstore_reset_gas
|
||||||
|
|
||||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
|
||||||
}
|
}
|
||||||
} else {
|
} 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
|
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(
|
pub fn handle_eip1283_sstore_clears_refund(
|
||||||
|
@ -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());
|
||||||
|
@ -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;
|
||||||
|
@ -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,
|
||||||
|
@ -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": {
|
||||||
|
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())
|
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
|
||||||
|
@ -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"))
|
||||||
|
@ -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(¶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.
|
/// 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(¶ms, 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(¶ms, 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,
|
||||||
|
@ -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)]
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
|
@ -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);
|
||||||
|
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/>.
|
// 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(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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>;
|
||||||
}
|
}
|
||||||
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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,10 +385,17 @@ 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, _> =
|
||||||
|
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)?
|
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()),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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>,
|
||||||
|
@ -38,6 +38,7 @@ pub enum ForkSpec {
|
|||||||
ByzantiumToConstantinopleAt5,
|
ByzantiumToConstantinopleAt5,
|
||||||
ByzantiumToConstantinopleFixAt5,
|
ByzantiumToConstantinopleFixAt5,
|
||||||
Berlin,
|
Berlin,
|
||||||
|
Yolo3,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spec deserialization.
|
/// Spec deserialization.
|
||||||
|
@ -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();
|
||||||
|
Loading…
Reference in New Issue
Block a user