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());
|
||||
let refunds_bound = sstore_refunds + suicide_refunds;
|
||||
|
||||
// real ammount to refund
|
||||
// real amount to refund
|
||||
let gas_left_prerefund = match result {
|
||||
Ok(FinalizationResult { gas_left, .. }) => gas_left,
|
||||
_ => 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_used = t.tx().gas.saturating_sub(gas_left);
|
||||
|
@ -139,6 +139,8 @@ pub struct CommonParams {
|
||||
pub eip2929_transition: BlockNumber,
|
||||
/// Number of first block where EIP-2930 rules begin.
|
||||
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.
|
||||
pub dust_protection_transition: BlockNumber,
|
||||
/// 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.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 {
|
||||
schedule.kill_dust = match self.remove_dust_contracts {
|
||||
@ -378,6 +385,9 @@ impl From<ethjson::spec::Params> for CommonParams {
|
||||
eip2930_transition: p
|
||||
.eip2930_transition
|
||||
.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
|
||||
.map_or_else(BlockNumber::max_value, Into::into),
|
||||
@ -667,6 +677,7 @@ impl Spec {
|
||||
params.eip2315_transition,
|
||||
params.eip2929_transition,
|
||||
params.eip2930_transition,
|
||||
params.eip3529_transition,
|
||||
params.dust_protection_transition,
|
||||
params.wasm_activation_transition,
|
||||
params.wasm_disable_transition,
|
||||
|
@ -112,6 +112,8 @@ pub struct Params {
|
||||
/// See `CommonParams` docs.
|
||||
pub eip2930_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
pub eip3529_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
pub dust_protection_transition: Option<Uint>,
|
||||
/// See `CommonParams` docs.
|
||||
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));
|
||||
}
|
||||
|
||||
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(
|
||||
factory: &super::Factory,
|
||||
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;
|
||||
/// Gas per received address
|
||||
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.
|
||||
#[derive(Debug)]
|
||||
@ -155,6 +161,8 @@ pub struct Schedule {
|
||||
pub eip2929: bool,
|
||||
/// Enable EIP-2930 rules for optional access list transactions. it depends on EIP-2929
|
||||
pub eip2930: bool,
|
||||
/// Gas used in transaction divided by this number is the maximum refundable amount.
|
||||
pub max_refund_quotient: usize,
|
||||
}
|
||||
|
||||
/// Wasm cost table
|
||||
@ -302,6 +310,7 @@ impl Schedule {
|
||||
wasm: None,
|
||||
eip2929: false,
|
||||
eip2930: false,
|
||||
max_refund_quotient: MAX_REFUND_QUOTIENT,
|
||||
}
|
||||
}
|
||||
|
||||
@ -359,6 +368,17 @@ impl 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 {
|
||||
Schedule {
|
||||
exceptional_failed_code_deposit: efcd,
|
||||
@ -422,6 +442,7 @@ impl Schedule {
|
||||
wasm: None,
|
||||
eip2929: false,
|
||||
eip2930: false,
|
||||
max_refund_quotient: MAX_REFUND_QUOTIENT,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,7 @@ pub struct FakeCall {
|
||||
/// Can't do recursive calls.
|
||||
#[derive(Default)]
|
||||
pub struct FakeExt {
|
||||
pub initial_store: HashMap<H256, H256>,
|
||||
pub store: HashMap<H256, H256>,
|
||||
pub suicides: HashSet<Address>,
|
||||
pub calls: HashSet<FakeCall>,
|
||||
@ -130,6 +131,12 @@ impl FakeExt {
|
||||
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
|
||||
pub fn with_wasm(mut self) -> Self {
|
||||
self.schedule.wasm = Some(Default::default());
|
||||
@ -141,11 +148,30 @@ impl FakeExt {
|
||||
self.chain_id = chain_id;
|
||||
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 {
|
||||
fn initial_storage_at(&self, _key: &H256) -> Result<H256> {
|
||||
Ok(H256::default())
|
||||
fn initial_storage_at(&self, key: &H256) -> Result<H256> {
|
||||
match self.initial_store.get(key) {
|
||||
Some(value) => Ok(*value),
|
||||
None => Ok(H256::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn storage_at(&self, key: &H256) -> Result<H256> {
|
||||
|
Loading…
Reference in New Issue
Block a user