EIP2929 with journaling + Yolov3 (#79)

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

View File

@@ -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(

View File

@@ -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());

View File

@@ -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;

View File

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