EIP-3529: Reduction in gas refunds (#406)
* Implement changed gas refund rules according to EIP-3529 * Add a unit test for changed gas refunds * Add missing comment * Add fork block to spec.hard_forks
This commit is contained in:
parent
f9f492638c
commit
f14d3e5a5c
@ -1463,12 +1463,18 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> {
|
|||||||
U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len());
|
U256::from(schedule.suicide_refund_gas) * U256::from(substate.suicides.len());
|
||||||
let refunds_bound = sstore_refunds + suicide_refunds;
|
let refunds_bound = sstore_refunds + suicide_refunds;
|
||||||
|
|
||||||
// real ammount to refund
|
// real amount to refund
|
||||||
let gas_left_prerefund = match result {
|
let gas_left_prerefund = match result {
|
||||||
Ok(FinalizationResult { gas_left, .. }) => gas_left,
|
Ok(FinalizationResult { gas_left, .. }) => gas_left,
|
||||||
_ => 0.into(),
|
_ => 0.into(),
|
||||||
};
|
};
|
||||||
let refunded = cmp::min(refunds_bound, (t.tx().gas - gas_left_prerefund) >> 1);
|
let refunded = if refunds_bound.is_zero() {
|
||||||
|
refunds_bound
|
||||||
|
} else {
|
||||||
|
let gas_used = t.tx().gas - gas_left_prerefund;
|
||||||
|
let max_refund = gas_used / schedule.max_refund_quotient;
|
||||||
|
cmp::min(max_refund, refunds_bound)
|
||||||
|
};
|
||||||
let gas_left = gas_left_prerefund + refunded;
|
let gas_left = gas_left_prerefund + refunded;
|
||||||
|
|
||||||
let gas_used = t.tx().gas.saturating_sub(gas_left);
|
let gas_used = t.tx().gas.saturating_sub(gas_left);
|
||||||
|
@ -139,6 +139,8 @@ pub struct CommonParams {
|
|||||||
pub eip2929_transition: BlockNumber,
|
pub eip2929_transition: BlockNumber,
|
||||||
/// Number of first block where EIP-2930 rules begin.
|
/// Number of first block where EIP-2930 rules begin.
|
||||||
pub eip2930_transition: BlockNumber,
|
pub eip2930_transition: BlockNumber,
|
||||||
|
/// Number of first block where EIP-3529 rules begin.
|
||||||
|
pub eip3529_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.
|
||||||
@ -245,6 +247,11 @@ impl CommonParams {
|
|||||||
schedule.sload_gas = ::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;
|
schedule.sstore_reset_gas = ::vm::schedule::EIP2929_SSTORE_RESET_GAS;
|
||||||
}
|
}
|
||||||
|
if block_number >= self.eip3529_transition {
|
||||||
|
schedule.suicide_refund_gas = 0;
|
||||||
|
schedule.sstore_refund_gas = ::vm::schedule::EIP3529_SSTORE_CLEARS_SCHEDULE;
|
||||||
|
schedule.max_refund_quotient = ::vm::schedule::EIP3529_MAX_REFUND_QUOTIENT;
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
@ -378,6 +385,9 @@ impl From<ethjson::spec::Params> for CommonParams {
|
|||||||
eip2930_transition: p
|
eip2930_transition: p
|
||||||
.eip2930_transition
|
.eip2930_transition
|
||||||
.map_or_else(BlockNumber::max_value, Into::into),
|
.map_or_else(BlockNumber::max_value, Into::into),
|
||||||
|
eip3529_transition: p
|
||||||
|
.eip3529_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),
|
||||||
@ -667,6 +677,7 @@ impl Spec {
|
|||||||
params.eip2315_transition,
|
params.eip2315_transition,
|
||||||
params.eip2929_transition,
|
params.eip2929_transition,
|
||||||
params.eip2930_transition,
|
params.eip2930_transition,
|
||||||
|
params.eip3529_transition,
|
||||||
params.dust_protection_transition,
|
params.dust_protection_transition,
|
||||||
params.wasm_activation_transition,
|
params.wasm_activation_transition,
|
||||||
params.wasm_disable_transition,
|
params.wasm_disable_transition,
|
||||||
|
@ -112,6 +112,8 @@ pub struct Params {
|
|||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
pub eip2930_transition: Option<Uint>,
|
pub eip2930_transition: Option<Uint>,
|
||||||
/// See `CommonParams` docs.
|
/// See `CommonParams` docs.
|
||||||
|
pub eip3529_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>,
|
||||||
|
@ -1661,6 +1661,61 @@ fn test_access_list_cheap_expensive_cheap(factory: super::Factory) {
|
|||||||
assert_eq!(gas_left, U256::from(0));
|
assert_eq!(gas_left, U256::from(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evm_test! {test_refund_post_london: test_refund_post_london_int}
|
||||||
|
fn test_refund_post_london(factory: super::Factory) {
|
||||||
|
// Compare EIP-3529 for the test cases
|
||||||
|
|
||||||
|
let code = hex!("60006000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[], 0);
|
||||||
|
|
||||||
|
let code = hex!("60006000556001600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[], 0);
|
||||||
|
|
||||||
|
let code = hex!("60016000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[], 19900);
|
||||||
|
|
||||||
|
let code = hex!("60006000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 4800);
|
||||||
|
|
||||||
|
let code = hex!("60006000556001600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 2800);
|
||||||
|
|
||||||
|
let code = hex!("60006000556002600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 0);
|
||||||
|
|
||||||
|
let code = hex!("60026000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 4800);
|
||||||
|
|
||||||
|
let code = hex!("60026000556001600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 2800);
|
||||||
|
|
||||||
|
let code = hex!("60016000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[], 19900);
|
||||||
|
|
||||||
|
let code = hex!("600060005560016000556000600055").to_vec();
|
||||||
|
london_refund_test(&factory, code, &[1], 7600);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn london_refund_test(
|
||||||
|
factory: &super::Factory,
|
||||||
|
code: Vec<u8>,
|
||||||
|
fill: &[u64],
|
||||||
|
expected_refund: i128,
|
||||||
|
) {
|
||||||
|
let mut params = ActionParams::default();
|
||||||
|
params.gas = U256::from(22318);
|
||||||
|
params.code = Some(Arc::new(code));
|
||||||
|
let mut ext = FakeExt::new_london(
|
||||||
|
Address::from_str("0000000000000000000000000000000000000000").unwrap(),
|
||||||
|
Address::from_str("000000000000000000000000636F6E7472616374").unwrap(),
|
||||||
|
&[],
|
||||||
|
);
|
||||||
|
ext.prefill(fill);
|
||||||
|
let vm = factory.create(params, ext.schedule(), ext.depth());
|
||||||
|
vm.exec(&mut ext).ok().unwrap().unwrap();
|
||||||
|
assert_eq!(ext.sstore_clears, expected_refund);
|
||||||
|
}
|
||||||
|
|
||||||
fn push_two_pop_one_constantinople_test(
|
fn push_two_pop_one_constantinople_test(
|
||||||
factory: &super::Factory,
|
factory: &super::Factory,
|
||||||
opcode: u8,
|
opcode: u8,
|
||||||
|
@ -28,6 +28,12 @@ pub const EIP2929_SSTORE_RESET_GAS: usize = 5000 - EIP2929_COLD_SLOAD_COST;
|
|||||||
pub const EIP2930_ACCESS_LIST_STORAGE_KEY_COST: usize = 1900;
|
pub const EIP2930_ACCESS_LIST_STORAGE_KEY_COST: usize = 1900;
|
||||||
/// Gas per received address
|
/// Gas per received address
|
||||||
pub const EIP2930_ACCESS_LIST_ADDRESS_COST: usize = 2400;
|
pub const EIP2930_ACCESS_LIST_ADDRESS_COST: usize = 2400;
|
||||||
|
/// Gas used per transaction divided by this number is the maximum refundable amount
|
||||||
|
pub const MAX_REFUND_QUOTIENT: usize = 2;
|
||||||
|
pub const EIP3529_MAX_REFUND_QUOTIENT: usize = 5;
|
||||||
|
/// Reduced SSTORE refund as by EIP-3529
|
||||||
|
pub const EIP3529_SSTORE_CLEARS_SCHEDULE: usize =
|
||||||
|
EIP2929_SSTORE_RESET_GAS + EIP2930_ACCESS_LIST_STORAGE_KEY_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)]
|
||||||
@ -155,6 +161,8 @@ pub struct Schedule {
|
|||||||
pub eip2929: bool,
|
pub eip2929: bool,
|
||||||
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
|
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
|
||||||
pub eip2930: bool,
|
pub eip2930: bool,
|
||||||
|
/// Gas used in transaction divided by this number is the maximum refundable amount.
|
||||||
|
pub max_refund_quotient: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wasm cost table
|
/// Wasm cost table
|
||||||
@ -302,6 +310,7 @@ impl Schedule {
|
|||||||
wasm: None,
|
wasm: None,
|
||||||
eip2929: false,
|
eip2929: false,
|
||||||
eip2930: false,
|
eip2930: false,
|
||||||
|
max_refund_quotient: MAX_REFUND_QUOTIENT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,6 +368,17 @@ impl Schedule {
|
|||||||
schedule
|
schedule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_london() -> Schedule {
|
||||||
|
let mut schedule = Self::new_berlin();
|
||||||
|
|
||||||
|
// EIP-3529 changes
|
||||||
|
schedule.suicide_refund_gas = 0;
|
||||||
|
schedule.sstore_refund_gas = EIP3529_SSTORE_CLEARS_SCHEDULE;
|
||||||
|
schedule.max_refund_quotient = EIP3529_MAX_REFUND_QUOTIENT;
|
||||||
|
|
||||||
|
schedule
|
||||||
|
}
|
||||||
|
|
||||||
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
fn new(efcd: bool, hdc: bool, tcg: usize) -> Schedule {
|
||||||
Schedule {
|
Schedule {
|
||||||
exceptional_failed_code_deposit: efcd,
|
exceptional_failed_code_deposit: efcd,
|
||||||
@ -422,6 +442,7 @@ impl Schedule {
|
|||||||
wasm: None,
|
wasm: None,
|
||||||
eip2929: false,
|
eip2929: false,
|
||||||
eip2930: false,
|
eip2930: false,
|
||||||
|
max_refund_quotient: MAX_REFUND_QUOTIENT,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@ pub struct FakeCall {
|
|||||||
/// Can't do recursive calls.
|
/// Can't do recursive calls.
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct FakeExt {
|
pub struct FakeExt {
|
||||||
|
pub initial_store: HashMap<H256, H256>,
|
||||||
pub store: HashMap<H256, H256>,
|
pub store: HashMap<H256, H256>,
|
||||||
pub suicides: HashSet<Address>,
|
pub suicides: HashSet<Address>,
|
||||||
pub calls: HashSet<FakeCall>,
|
pub calls: HashSet<FakeCall>,
|
||||||
@ -130,6 +131,12 @@ impl FakeExt {
|
|||||||
ext
|
ext
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn new_london(from: Address, to: Address, builtins: &[Address]) -> Self {
|
||||||
|
let mut ext = FakeExt::new_berlin(from, to, builtins);
|
||||||
|
ext.schedule = Schedule::new_london();
|
||||||
|
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());
|
||||||
@ -141,11 +148,30 @@ impl FakeExt {
|
|||||||
self.chain_id = chain_id;
|
self.chain_id = chain_id;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn set_initial_storage(&mut self, key: H256, value: H256) {
|
||||||
|
self.initial_store.insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fill the storage before the transaction with `data`, i. e. set
|
||||||
|
/// both original and current values, beginning from address 0.
|
||||||
|
pub fn prefill(&mut self, data: &[u64]) {
|
||||||
|
for (k, v) in data.iter().enumerate() {
|
||||||
|
let key = H256::from_low_u64_be(k as u64);
|
||||||
|
let value = H256::from_low_u64_be(*v);
|
||||||
|
self.set_initial_storage(key, value);
|
||||||
|
self.set_storage(key, value)
|
||||||
|
.expect("FakeExt::set_storage() never returns an Err.");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Ext for FakeExt {
|
impl Ext for FakeExt {
|
||||||
fn initial_storage_at(&self, _key: &H256) -> Result<H256> {
|
fn initial_storage_at(&self, key: &H256) -> Result<H256> {
|
||||||
Ok(H256::default())
|
match self.initial_store.get(key) {
|
||||||
|
Some(value) => Ok(*value),
|
||||||
|
None => Ok(H256::default()),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn storage_at(&self, key: &H256) -> Result<H256> {
|
fn storage_at(&self, key: &H256) -> Result<H256> {
|
||||||
|
Loading…
Reference in New Issue
Block a user