EIP2929 with journaling + Yolov3 (#79)
This commit is contained in:
@@ -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(),
|
||||
mem_needed(stack.peek(1), stack.peek(3))?,
|
||||
Gas::from_u256(*stack.peek(3))?,
|
||||
),
|
||||
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.
|
||||
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.
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// 2.1.2. Otherwise, 5000 gas is deducted.
|
||||
schedule.sstore_reset_gas
|
||||
|
||||
// 2.1.2.1. If new value is 0, add 15000 gas to refund counter.
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
|
||||
Gas::from(
|
||||
if current == new {
|
||||
// 1. If current value equals new value (this is a no-op).
|
||||
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.
|
||||
}
|
||||
})
|
||||
} 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.
|
||||
schedule.sstore_set_gas
|
||||
} else {
|
||||
// 2.1.2. Otherwise.
|
||||
schedule.sstore_reset_gas
|
||||
}
|
||||
} else {
|
||||
// 2.2. If original value does not equal current value (this storage slot is dirty).
|
||||
schedule.sload_gas
|
||||
}
|
||||
} + if is_cold {
|
||||
// EIP2929 SSTORE changes section
|
||||
schedule.cold_sload_cost
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handle_eip1283_sstore_clears_refund(
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user