Skip to content

Commit 1c9daf9

Browse files
authored
EIP-3529 Reduction in Refunds (#2020)
* EIP-3529 Implementation: - Replace SSTORE_CLEARS_SCHEDULE, - Remove SELFDESTRUCT refund, - Add new MAX_REFUND_QUOTIENT * Add newsfragment
1 parent 2da165f commit 1c9daf9

File tree

7 files changed

+184
-7
lines changed

7 files changed

+184
-7
lines changed

eth/vm/forks/frontier/state.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -142,21 +142,28 @@ def build_computation(self,
142142

143143
return computation
144144

145-
def finalize_computation(self,
146-
transaction: SignedTransactionAPI,
147-
computation: ComputationAPI) -> ComputationAPI:
148-
transaction_context = self.vm_state.get_transaction_context(transaction)
149-
145+
@classmethod
146+
def calculate_gas_refund(cls,
147+
computation: ComputationAPI,
148+
gas_used: int) -> int:
150149
# Self Destruct Refunds
151150
num_deletions = len(computation.get_accounts_for_deletion())
152151
if num_deletions:
153152
computation.refund_gas(REFUND_SELFDESTRUCT * num_deletions)
154153

155154
# Gas Refunds
156-
gas_remaining = computation.get_gas_remaining()
157155
gas_refunded = computation.get_gas_refund()
156+
157+
return min(gas_refunded, gas_used // 2)
158+
159+
def finalize_computation(self,
160+
transaction: SignedTransactionAPI,
161+
computation: ComputationAPI) -> ComputationAPI:
162+
transaction_context = self.vm_state.get_transaction_context(transaction)
163+
164+
gas_remaining = computation.get_gas_remaining()
158165
gas_used = transaction.gas - gas_remaining
159-
gas_refund = min(gas_refunded, gas_used // 2)
166+
gas_refund = self.calculate_gas_refund(computation, gas_used)
160167
gas_refund_amount = (gas_refund + gas_remaining) * transaction_context.gas_price
161168

162169
if gas_refund_amount:

eth/vm/forks/london/constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
ELASTICITY_MULTIPLIER = 2
1414

1515
EIP3541_RESERVED_STARTING_BYTE = b'\xef'
16+
EIP3529_MAX_REFUND_QUOTIENT = 5

eth/vm/forks/london/opcodes.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,23 @@
1919
from eth.vm.forks.berlin.opcodes import (
2020
BERLIN_OPCODES,
2121
)
22+
from eth.vm.forks.byzantium.opcodes import (
23+
ensure_no_static,
24+
)
2225

26+
from . import storage
2327

2428
UPDATED_OPCODES: Dict[int, Opcode] = {
2529
opcode_values.BASEFEE: as_opcode(
2630
gas_cost=constants.GAS_BASE,
2731
logic_fn=block.basefee,
2832
mnemonic=mnemonics.BASEFEE,
2933
),
34+
opcode_values.SSTORE: as_opcode(
35+
gas_cost=constants.GAS_NULL,
36+
logic_fn=ensure_no_static(storage.sstore_eip3529),
37+
mnemonic=mnemonics.SSTORE,
38+
),
3039
}
3140

3241

eth/vm/forks/london/state.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from eth.abc import (
99
AccountDatabaseAPI,
10+
ComputationAPI,
1011
MessageAPI,
1112
SignedTransactionAPI,
1213
StateAPI,
@@ -32,6 +33,7 @@
3233

3334
from .computation import LondonComputation
3435
from .validation import validate_london_normalized_transaction
36+
from .constants import EIP3529_MAX_REFUND_QUOTIENT
3537

3638

3739
class LondonTransactionExecutor(BerlinTransactionExecutor):
@@ -86,6 +88,16 @@ def build_evm_message(self, transaction: SignedTransactionAPI) -> MessageAPI:
8688
)
8789
return message
8890

91+
@classmethod
92+
def calculate_gas_refund(cls,
93+
computation: ComputationAPI,
94+
gas_used: int) -> int:
95+
# Self destruct refunds were added in Frontier
96+
# London removes them in EIP-3529
97+
gas_refunded = computation.get_gas_refund()
98+
99+
return min(gas_refunded, gas_used // EIP3529_MAX_REFUND_QUOTIENT)
100+
89101

90102
class LondonState(BerlinState):
91103
account_db_class: Type[AccountDatabaseAPI] = AccountDB

eth/vm/forks/london/storage.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from eth_utils.toolz import partial
2+
3+
from eth.vm.forks.berlin import constants as berlin_constants
4+
from eth.vm.forks.berlin.logic import (
5+
GAS_SCHEDULE_EIP2929,
6+
sstore_eip2929_generic,
7+
)
8+
9+
10+
SSTORE_CLEARS_SCHEDULE_EIP_3529 = (
11+
GAS_SCHEDULE_EIP2929.sstore_reset_gas
12+
+ berlin_constants.ACCESS_LIST_STORAGE_KEY_COST_EIP_2930
13+
)
14+
15+
16+
GAS_SCHEDULE_EIP3529 = GAS_SCHEDULE_EIP2929._replace(
17+
sstore_clears_schedule=SSTORE_CLEARS_SCHEDULE_EIP_3529
18+
)
19+
20+
sstore_eip3529 = partial(sstore_eip2929_generic, GAS_SCHEDULE_EIP3529)

newsfragments/2020.feature.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Implement EIP-3529:
2+
- Remove self destruct refunds
3+
- Change max gas refund from gas_used // 2 -> gas_used // 5
4+
- Replace sstore_clears_schedule with sstore_reset_gas + access_list_storage_key_cost

tests/core/opcodes/test_opcodes.py

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,130 @@ def test_extcodehash(vm_class, address, expected):
960960
19200,
961961
1,
962962
),
963+
# London reduces refund by changing SSTORE_CLEARS_SCHEDULE
964+
# Test cases from: https://eips.ethereum.org/EIPS/eip-3529
965+
# Note: test cases assume the storage slot is already warm,
966+
# the difference being a one-time cost of 2100 gas.
967+
# so gas_used becomes gas_used from EIP-3529 test case + 2100
968+
(
969+
LondonVM,
970+
"0x60006000556000600055",
971+
212 + 2100,
972+
0,
973+
0,
974+
),
975+
(
976+
LondonVM,
977+
"0x60006000556001600055",
978+
20112 + 2100,
979+
0,
980+
0,
981+
),
982+
(
983+
LondonVM,
984+
"0x60016000556000600055",
985+
20112 + 2100,
986+
19900,
987+
0,
988+
),
989+
(
990+
LondonVM,
991+
"0x60016000556002600055",
992+
20112 + 2100,
993+
0,
994+
0,
995+
),
996+
(
997+
LondonVM,
998+
"0x60016000556001600055",
999+
20112 + 2100,
1000+
0,
1001+
0,
1002+
),
1003+
(
1004+
LondonVM,
1005+
"0x60006000556000600055",
1006+
3012 + 2100,
1007+
4800,
1008+
1,
1009+
),
1010+
(
1011+
LondonVM,
1012+
"0x60006000556001600055",
1013+
3012 + 2100,
1014+
2800,
1015+
1,
1016+
),
1017+
(
1018+
LondonVM,
1019+
"0x60006000556002600055",
1020+
3012 + 2100,
1021+
0,
1022+
1,
1023+
),
1024+
(
1025+
LondonVM,
1026+
"0x60026000556000600055",
1027+
3012 + 2100,
1028+
4800,
1029+
1,
1030+
),
1031+
(
1032+
LondonVM,
1033+
"0x60026000556003600055",
1034+
3012 + 2100,
1035+
0,
1036+
1,
1037+
),
1038+
(
1039+
LondonVM,
1040+
"0x60026000556001600055",
1041+
3012 + 2100,
1042+
2800,
1043+
1,
1044+
),
1045+
(
1046+
LondonVM,
1047+
"0x60026000556002600055",
1048+
3012 + 2100,
1049+
0,
1050+
1,
1051+
),
1052+
(
1053+
LondonVM,
1054+
"0x60016000556000600055",
1055+
3012 + 2100,
1056+
4800,
1057+
1,
1058+
),
1059+
(
1060+
LondonVM,
1061+
"0x60016000556002600055",
1062+
3012 + 2100,
1063+
0,
1064+
1,
1065+
),
1066+
(
1067+
LondonVM,
1068+
"0x60016000556001600055",
1069+
212 + 2100,
1070+
0,
1071+
1,
1072+
),
1073+
(
1074+
LondonVM,
1075+
"0x600160005560006000556001600055",
1076+
40118 + 2100,
1077+
19900,
1078+
0,
1079+
),
1080+
(
1081+
LondonVM,
1082+
"0x600060005560016000556000600055",
1083+
5918 + 2100,
1084+
7600,
1085+
1,
1086+
),
9631087
)
9641088
)
9651089
def test_sstore(vm_class, code, gas_used, refund, original):

0 commit comments

Comments
 (0)