Skip to content

Commit 60c9736

Browse files
committed
core, params: implement EIP-3529 (ethereum#22733)
1 parent fe37712 commit 60c9736

File tree

6 files changed

+162
-119
lines changed

6 files changed

+162
-119
lines changed

core/blockchain_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1496,7 +1496,8 @@ func TestEIP2718Transition(t *testing.T) {
14961496
block := chain.GetBlockByNumber(1)
14971497

14981498
// Expected gas is intrinsic + 2 * pc + hot load + cold load, since only one load is in the access list
1499-
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas + vm.GasQuickStep*2 + vm.WarmStorageReadCostEIP2929 + vm.ColdSloadCostEIP2929
1499+
expected := params.TxGas + params.TxAccessListAddressGas + params.TxAccessListStorageKeyGas +
1500+
vm.GasQuickStep*2 + params.WarmStorageReadCostEIP2929 + params.ColdSloadCostEIP2929
15001501
if block.GasUsed() != expected {
15011502
t.Fatalf("incorrect amount of gas spent: expected %d, got %d", expected, block.GasUsed())
15021503
}

core/state_transition.go

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
223223
sender := st.from() // err checked in preCheck
224224

225225
homestead := st.evm.ChainConfig().IsHomestead(st.evm.BlockNumber)
226+
eip3529 := st.evm.ChainConfig().IsEIP1559(st.evm.Context.BlockNumber)
226227
contractCreation := msg.To() == nil
227228

228229
// Pay intrinsic gas
@@ -269,7 +270,13 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
269270
return nil, 0, false, vmerr, nil
270271
}
271272
}
272-
st.refundGas()
273+
if !eip3529 {
274+
// Before EIP-3529: refunds were capped to gasUsed / 2
275+
st.refundGas(params.RefundQuotient)
276+
} else {
277+
// After EIP-3529: refunds are capped to gasUsed / 5
278+
st.refundGas(params.RefundQuotientEIP3529)
279+
}
273280

274281
if st.evm.BlockNumber.Cmp(common.TIPTRC21Fee) > 0 {
275282
if (owner != common.Address{}) {
@@ -282,9 +289,9 @@ func (st *StateTransition) TransitionDb(owner common.Address) (ret []byte, usedG
282289
return ret, st.gasUsed(), vmerr != nil, nil, vmerr
283290
}
284291

285-
func (st *StateTransition) refundGas() {
286-
// Apply refund counter, capped to half of the used gas.
287-
refund := st.gasUsed() / 2
292+
func (st *StateTransition) refundGas(refundQuotient uint64) {
293+
/// Apply refund counter, capped to a refund quotient
294+
refund := st.gasUsed() / refundQuotient
288295
if refund > st.state.GetRefund() {
289296
refund = st.state.GetRefund()
290297
}

core/vm/eips.go

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ func EnableEIP(eipNum int, jt *JumpTable) error {
3131
switch eipNum {
3232
case 3855:
3333
enable3855(jt)
34+
case 3529:
35+
enable3529(jt)
3436
case 3198:
3537
enable3198(jt)
3638
case 2929:
@@ -106,28 +108,28 @@ func enable2929(jt *JumpTable) {
106108
jt[SLOAD].constantGas = 0
107109
jt[SLOAD].dynamicGas = gasSLoadEIP2929
108110

109-
jt[EXTCODECOPY].constantGas = WarmStorageReadCostEIP2929
111+
jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
110112
jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
111113

112-
jt[EXTCODESIZE].constantGas = WarmStorageReadCostEIP2929
114+
jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
113115
jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
114116

115-
jt[EXTCODEHASH].constantGas = WarmStorageReadCostEIP2929
117+
jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
116118
jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
117119

118-
jt[BALANCE].constantGas = WarmStorageReadCostEIP2929
120+
jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
119121
jt[BALANCE].dynamicGas = gasEip2929AccountCheck
120122

121-
jt[CALL].constantGas = WarmStorageReadCostEIP2929
123+
jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
122124
jt[CALL].dynamicGas = gasCallEIP2929
123125

124-
jt[CALLCODE].constantGas = WarmStorageReadCostEIP2929
126+
jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
125127
jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
126128

127-
jt[STATICCALL].constantGas = WarmStorageReadCostEIP2929
129+
jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
128130
jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
129131

130-
jt[DELEGATECALL].constantGas = WarmStorageReadCostEIP2929
132+
jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
131133
jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
132134

133135
// This was previously part of the dynamic cost, but we're using it as a constantGas
@@ -136,6 +138,15 @@ func enable2929(jt *JumpTable) {
136138
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
137139
}
138140

141+
// enable3529 enabled "EIP-3529: Reduction in refunds":
142+
// - Removes refunds for selfdestructs
143+
// - Reduces refunds for SSTORE
144+
// - Reduces max refunds to 20% gas
145+
func enable3529(jt *JumpTable) {
146+
jt[SSTORE].dynamicGas = gasSStoreEIP3529
147+
jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
148+
}
149+
139150
// enable3198 applies EIP-3198 (BASEFEE Opcode)
140151
// - Adds an opcode that returns the current block's base fee.
141152
func enable3198(jt *JumpTable) {

core/vm/jump_table.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ type JumpTable [256]*operation
6363
func newEip1559InstructionSet() JumpTable {
6464
instructionSet := newShanghaiInstructionSet()
6565
enable2929(&instructionSet) // Gas cost increases for state access opcodes https://eips.ethereum.org/EIPS/eip-2929
66+
enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
6667
return instructionSet
6768
}
6869

@@ -87,7 +88,6 @@ func newMergeInstructionSet() JumpTable {
8788
// constantinople, istanbul, petersburg, berlin and london instructions.
8889
func newLondonInstructionSet() JumpTable {
8990
instructionSet := newBerlinInstructionSet()
90-
// enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
9191
enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
9292
return instructionSet
9393
}

core/vm/operations_acl.go

Lines changed: 109 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -24,91 +24,75 @@ import (
2424
"github.com/XinFinOrg/XDPoSChain/params"
2525
)
2626

27-
const (
28-
ColdAccountAccessCostEIP2929 = uint64(2600) // COLD_ACCOUNT_ACCESS_COST
29-
ColdSloadCostEIP2929 = uint64(2100) // COLD_SLOAD_COST
30-
WarmStorageReadCostEIP2929 = uint64(100) // WARM_STORAGE_READ_COST
31-
)
32-
33-
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929"
34-
//
35-
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
36-
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
37-
// Additionally, modify the parameters defined in EIP 2200 as follows:
38-
//
39-
// Parameter Old value New value
40-
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
41-
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
42-
//
43-
// The other parameters defined in EIP 2200 are unchanged.
44-
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
45-
func gasSStoreEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
46-
// If we fail the minimum gas availability invariant, fail (0)
47-
if contract.Gas <= params.SstoreSentryGasEIP2200 {
48-
return 0, errors.New("not enough gas for reentrancy sentry")
49-
}
50-
// Gas sentry honoured, do the actual gas calculation based on the stored value
51-
var (
52-
y, x = stack.Back(1), stack.peek()
53-
slot = common.Hash(x.Bytes32())
54-
current = evm.StateDB.GetState(contract.Address(), slot)
55-
cost = uint64(0)
56-
)
57-
// Check slot presence in the access list
58-
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
59-
cost = ColdSloadCostEIP2929
60-
// If the caller cannot afford the cost, this change will be rolled back
61-
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
62-
if !addrPresent {
63-
// Once we're done with YOLOv2 and schedule this for mainnet, might
64-
// be good to remove this panic here, which is just really a
65-
// canary to have during testing
66-
panic("impossible case: address was not present in access list during sstore op")
27+
func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
28+
return func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
29+
// If we fail the minimum gas availability invariant, fail (0)
30+
if contract.Gas <= params.SstoreSentryGasEIP2200 {
31+
return 0, errors.New("not enough gas for reentrancy sentry")
6732
}
68-
}
69-
value := common.Hash(y.Bytes32())
33+
// Gas sentry honoured, do the actual gas calculation based on the stored value
34+
var (
35+
y, x = stack.Back(1), stack.peek()
36+
slot = common.Hash(x.Bytes32())
37+
current = evm.StateDB.GetState(contract.Address(), slot)
38+
cost = uint64(0)
39+
)
40+
// Check slot presence in the access list
41+
if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
42+
cost = params.ColdSloadCostEIP2929
43+
// If the caller cannot afford the cost, this change will be rolled back
44+
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
45+
if !addrPresent {
46+
// Once we're done with YOLOv2 and schedule this for mainnet, might
47+
// be good to remove this panic here, which is just really a
48+
// canary to have during testing
49+
panic("impossible case: address was not present in access list during sstore op")
50+
}
51+
}
52+
value := common.Hash(y.Bytes32())
7053

71-
if current == value { // noop (1)
72-
// EIP 2200 original clause:
73-
// return params.SloadGasEIP2200, nil
74-
return cost + WarmStorageReadCostEIP2929, nil // SLOAD_GAS
75-
}
76-
original := evm.StateDB.GetCommittedState(contract.Address(), common.Hash(x.Bytes32()))
77-
if original == current {
78-
if original == (common.Hash{}) { // create slot (2.1.1)
79-
return cost + params.SstoreSetGasEIP2200, nil
54+
if current == value { // noop (1)
55+
// EIP 2200 original clause:
56+
// return params.SloadGasEIP2200, nil
57+
return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
8058
}
81-
if value == (common.Hash{}) { // delete slot (2.1.2b)
82-
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
59+
original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
60+
if original == current {
61+
if original == (common.Hash{}) { // create slot (2.1.1)
62+
return cost + params.SstoreSetGasEIP2200, nil
63+
}
64+
if value == (common.Hash{}) { // delete slot (2.1.2b)
65+
evm.StateDB.AddRefund(clearingRefund)
66+
}
67+
// EIP-2200 original clause:
68+
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
69+
return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
8370
}
84-
// EIP-2200 original clause:
85-
// return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
86-
return cost + (params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
87-
}
88-
if original != (common.Hash{}) {
89-
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
90-
evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
91-
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
92-
evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
71+
if original != (common.Hash{}) {
72+
if current == (common.Hash{}) { // recreate slot (2.2.1.1)
73+
evm.StateDB.SubRefund(clearingRefund)
74+
} else if value == (common.Hash{}) { // delete slot (2.2.1.2)
75+
evm.StateDB.AddRefund(clearingRefund)
76+
}
9377
}
94-
}
95-
if original == value {
96-
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
97-
// EIP 2200 Original clause:
98-
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
99-
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - WarmStorageReadCostEIP2929)
100-
} else { // reset to original existing slot (2.2.2.2)
101-
// EIP 2200 Original clause:
102-
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
103-
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
104-
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
105-
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
106-
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - ColdSloadCostEIP2929) - WarmStorageReadCostEIP2929)
78+
if original == value {
79+
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
80+
// EIP 2200 Original clause:
81+
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
82+
evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
83+
} else { // reset to original existing slot (2.2.2.2)
84+
// EIP 2200 Original clause:
85+
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
86+
// - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
87+
// - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
88+
// Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
89+
evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
90+
}
10791
}
92+
// EIP-2200 original clause:
93+
//return params.SloadGasEIP2200, nil // dirty update (2.2)
94+
return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
10895
}
109-
// EIP-2200 original clause:
110-
//return params.SloadGasEIP2200, nil // dirty update (2.2)
111-
return cost + WarmStorageReadCostEIP2929, nil // dirty update (2.2)
11296
}
11397

11498
// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
@@ -124,9 +108,9 @@ func gasSLoadEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, me
124108
// If the caller cannot afford the cost, this change will be rolled back
125109
// If he does afford it, we can skip checking the same thing later on, during execution
126110
evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
127-
return ColdSloadCostEIP2929, nil
111+
return params.ColdSloadCostEIP2929, nil
128112
}
129-
return WarmStorageReadCostEIP2929, nil
113+
return params.WarmStorageReadCostEIP2929, nil
130114
}
131115

132116
// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
@@ -146,7 +130,7 @@ func gasExtCodeCopyEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memo
146130
evm.StateDB.AddAddressToAccessList(addr)
147131
var overflow bool
148132
// We charge (cold-warm), since 'warm' is already charged as constantGas
149-
if gas, overflow = math.SafeAdd(gas, ColdAccountAccessCostEIP2929-WarmStorageReadCostEIP2929); overflow {
133+
if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
150134
return 0, ErrGasUintOverflow
151135
}
152136
return gas, nil
@@ -168,7 +152,7 @@ func gasEip2929AccountCheck(evm *EVM, contract *Contract, stack *Stack, mem *Mem
168152
// If the caller cannot afford the cost, this change will be rolled back
169153
evm.StateDB.AddAddressToAccessList(addr)
170154
// The warm storage read cost is already charged as constantGas
171-
return ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929, nil
155+
return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
172156
}
173157
return 0, nil
174158
}
@@ -180,7 +164,7 @@ func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
180164
warmAccess := evm.StateDB.AddressInAccessList(addr)
181165
// The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
182166
// the cost to charge for cold access, if any, is Cold - Warm
183-
coldCost := ColdAccountAccessCostEIP2929 - WarmStorageReadCostEIP2929
167+
coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
184168
if !warmAccess {
185169
evm.StateDB.AddAddressToAccessList(addr)
186170
// Charge the remaining difference here already, to correctly calculate available
@@ -212,24 +196,49 @@ var (
212196
gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
213197
gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
214198
gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
199+
gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
200+
// gasSelfdestructEIP3529 implements the changes in EIP-3529 (no refunds)
201+
gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
202+
203+
// gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
204+
//
205+
// When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
206+
// If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
207+
// Additionally, modify the parameters defined in EIP 2200 as follows:
208+
//
209+
// Parameter Old value New value
210+
// SLOAD_GAS 800 = WARM_STORAGE_READ_COST
211+
// SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
212+
//
213+
//The other parameters defined in EIP 2200 are unchanged.
214+
// see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
215+
gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
216+
217+
// gasSStoreEIP3529 implements gas cost for SSTORE according to EIP-3529
218+
// Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
219+
gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
215220
)
216221

217-
func gasSelfdestructEIP2929(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
218-
var (
219-
gas uint64
220-
address = common.Address(stack.peek().Bytes20())
221-
)
222-
if !evm.StateDB.AddressInAccessList(address) {
223-
// If the caller cannot afford the cost, this change will be rolled back
224-
evm.StateDB.AddAddressToAccessList(address)
225-
gas = ColdAccountAccessCostEIP2929
226-
}
227-
// if empty and transfers value
228-
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
229-
gas += params.CreateBySelfdestructGas
230-
}
231-
if !evm.StateDB.HasSuicided(contract.Address()) {
232-
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
222+
// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
223+
func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
224+
gasFunc := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
225+
var (
226+
gas uint64
227+
address = common.Address(stack.peek().Bytes20())
228+
)
229+
if !evm.StateDB.AddressInAccessList(address) {
230+
// If the caller cannot afford the cost, this change will be rolled back
231+
evm.StateDB.AddAddressToAccessList(address)
232+
gas = params.ColdAccountAccessCostEIP2929
233+
}
234+
// if empty and transfers value
235+
if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
236+
gas += params.CreateBySelfdestructGas
237+
}
238+
if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
239+
evm.StateDB.AddRefund(params.SelfdestructRefundGas)
240+
}
241+
return gas, nil
233242
}
234-
return gas, nil
243+
return gasFunc
235244
}

0 commit comments

Comments
 (0)