diff --git a/crates/ethcore/src/executive.rs b/crates/ethcore/src/executive.rs index 4fe13d27a..f0df8c289 100644 --- a/crates/ethcore/src/executive.rs +++ b/crates/ethcore/src/executive.rs @@ -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); diff --git a/crates/ethcore/src/spec/spec.rs b/crates/ethcore/src/spec/spec.rs index 395afea39..12a9dbbe3 100644 --- a/crates/ethcore/src/spec/spec.rs +++ b/crates/ethcore/src/spec/spec.rs @@ -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 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, diff --git a/crates/ethjson/src/spec/params.rs b/crates/ethjson/src/spec/params.rs index 7933208fe..6fb26241c 100644 --- a/crates/ethjson/src/spec/params.rs +++ b/crates/ethjson/src/spec/params.rs @@ -112,6 +112,8 @@ pub struct Params { /// See `CommonParams` docs. pub eip2930_transition: Option, /// See `CommonParams` docs. + pub eip3529_transition: Option, + /// See `CommonParams` docs. pub dust_protection_transition: Option, /// See `CommonParams` docs. pub nonce_cap_increment: Option, diff --git a/crates/vm/evm/src/tests.rs b/crates/vm/evm/src/tests.rs index 32daa37e1..9de1dccc1 100644 --- a/crates/vm/evm/src/tests.rs +++ b/crates/vm/evm/src/tests.rs @@ -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, + 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, diff --git a/crates/vm/vm/src/schedule.rs b/crates/vm/vm/src/schedule.rs index 80643acd2..c6ba27e7d 100644 --- a/crates/vm/vm/src/schedule.rs +++ b/crates/vm/vm/src/schedule.rs @@ -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, } } diff --git a/crates/vm/vm/src/tests.rs b/crates/vm/vm/src/tests.rs index 11daf4be9..71fdc2d23 100644 --- a/crates/vm/vm/src/tests.rs +++ b/crates/vm/vm/src/tests.rs @@ -63,6 +63,7 @@ pub struct FakeCall { /// Can't do recursive calls. #[derive(Default)] pub struct FakeExt { + pub initial_store: HashMap, pub store: HashMap, pub suicides: HashSet
, pub calls: HashSet, @@ -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 { - Ok(H256::default()) + fn initial_storage_at(&self, key: &H256) -> Result { + match self.initial_store.get(key) { + Some(value) => Ok(*value), + None => Ok(H256::default()), + } } fn storage_at(&self, key: &H256) -> Result {