mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-08 07:58:40 +00:00
core: apply fixes for 8037
This commit is contained in:
parent
2d708415d7
commit
fa45d1a38f
8 changed files with 122 additions and 10 deletions
|
|
@ -687,6 +687,36 @@ func (s *StateDB) IsNewContract(addr common.Address) bool {
|
|||
return obj.newContract
|
||||
}
|
||||
|
||||
// SameTxSelfDestructs returns the addresses that were both created and
|
||||
// self-destructed in the current transaction (EIP-6780). Used for the
|
||||
// EIP-8037 same-tx selfdestruct state-gas refund.
|
||||
func (s *StateDB) SameTxSelfDestructs() []common.Address {
|
||||
var out []common.Address
|
||||
for addr, obj := range s.stateObjects {
|
||||
if obj.newContract && obj.selfDestructed {
|
||||
out = append(out, addr)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// NewStorageSlotCount returns the number of storage slots that were written
|
||||
// to a non-zero value in the current transaction on the given account. Used
|
||||
// for the EIP-8037 same-tx selfdestruct state-gas refund.
|
||||
func (s *StateDB) NewStorageSlotCount(addr common.Address) int {
|
||||
obj, ok := s.stateObjects[addr]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
var count int
|
||||
for _, v := range obj.dirtyStorage {
|
||||
if v != (common.Hash{}) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
// Copy creates a deep, independent copy of the state.
|
||||
// Snapshots of the copied state cannot be applied to the copy.
|
||||
func (s *StateDB) Copy() *StateDB {
|
||||
|
|
|
|||
|
|
@ -80,6 +80,14 @@ func (s *hookedStateDB) GetCodeSize(addr common.Address) int {
|
|||
return s.inner.GetCodeSize(addr)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) SameTxSelfDestructs() []common.Address {
|
||||
return s.inner.SameTxSelfDestructs()
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) NewStorageSlotCount(addr common.Address) int {
|
||||
return s.inner.NewStorageSlotCount(addr)
|
||||
}
|
||||
|
||||
func (s *hookedStateDB) AddRefund(u uint64) {
|
||||
s.inner.AddRefund(u)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ func IntrinsicGas(data []byte, accessList types.AccessList, authList []types.Set
|
|||
gas.RegularGas += uint64(len(authList)) * params.TxAuthTupleRegularGas
|
||||
gas.StateGas += uint64(len(authList)) * (params.AuthorizationCreationSize + params.AccountCreationSize) * costPerStateByte
|
||||
} else {
|
||||
gas.RegularGas += uint64(len(authList)) * params.TxAuthTupleGas
|
||||
gas.RegularGas += uint64(len(authList)) * params.CallNewAccountGas
|
||||
}
|
||||
}
|
||||
dataLen := uint64(len(data))
|
||||
|
|
@ -607,6 +607,42 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
ret, st.gasRemaining, execGasUsed, vmerr = st.evm.Call(msg.From, st.to(), msg.Data, st.gasRemaining, value)
|
||||
}
|
||||
|
||||
// On outer level tx failure, no state is written.
|
||||
if rules.IsAmsterdam && vmerr != nil {
|
||||
st.gasRemaining.StateGas += execGasUsed.StateGas
|
||||
execGasUsed.StateGas = 0
|
||||
}
|
||||
|
||||
// Refund costs for selfdestructed accounts and slots.
|
||||
if rules.IsAmsterdam && vmerr == nil {
|
||||
cpsb := st.evm.Context.CostPerGasByte
|
||||
stateGasUsed := execGasUsed.StateGas + cost.StateGas
|
||||
var sdRefund uint64
|
||||
for _, addr := range st.state.SameTxSelfDestructs() {
|
||||
r := params.AccountCreationSize * cpsb
|
||||
r += uint64(st.state.NewStorageSlotCount(addr)) * params.StorageCreationSize * cpsb
|
||||
r += uint64(st.state.GetCodeSize(addr)) * cpsb
|
||||
sdRefund += r
|
||||
}
|
||||
if sdRefund > stateGasUsed {
|
||||
sdRefund = stateGasUsed
|
||||
}
|
||||
if sdRefund > 0 {
|
||||
st.gasRemaining.StateGas += sdRefund
|
||||
if execGasUsed.StateGas >= sdRefund {
|
||||
execGasUsed.StateGas -= sdRefund
|
||||
} else {
|
||||
extra := sdRefund - execGasUsed.StateGas
|
||||
execGasUsed.StateGas = 0
|
||||
if cost.StateGas >= extra {
|
||||
cost.StateGas -= extra
|
||||
} else {
|
||||
cost.StateGas = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Record the gas used excluding gas refunds. This value represents the actual
|
||||
// gas allowance required to complete execution.
|
||||
peakGasUsed := st.gasUsed()
|
||||
|
|
|
|||
|
|
@ -160,6 +160,18 @@ func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBud
|
|||
c.GasUsed.RegularGas = initialRegularGasUsed + gasUsed.RegularGas
|
||||
}
|
||||
|
||||
// Refunds the account creation state costs if a CREATE/CREATE2 call fails.
|
||||
func (c *Contract) RefundCreateStateGas(refund uint64) {
|
||||
if refund > 0 {
|
||||
c.Gas.StateGas += refund
|
||||
if c.GasUsed.StateGas >= refund {
|
||||
c.GasUsed.StateGas -= refund
|
||||
} else {
|
||||
c.GasUsed.StateGas = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Address returns the contracts address
|
||||
func (c *Contract) Address() common.Address {
|
||||
return c.address
|
||||
|
|
|
|||
|
|
@ -544,8 +544,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
if evm.Config.Tracer != nil && evm.Config.Tracer.OnGasChange != nil {
|
||||
evm.Config.Tracer.OnGasChange(gas.RegularGas, 0, tracing.GasChangeCallFailedExecution)
|
||||
}
|
||||
// Record all burned gas
|
||||
burned := gas.RegularGas
|
||||
gas.Exhaust()
|
||||
return nil, common.Address{}, gas, GasCosts{}, ErrContractAddressCollision
|
||||
return nil, common.Address{}, gas, GasUsed{RegularGas: burned}, ErrContractAddressCollision
|
||||
}
|
||||
// Create a new account on the state only if the object was not present.
|
||||
// It might be possible the contract code is deployed to a pre-existent
|
||||
|
|
|
|||
|
|
@ -671,12 +671,14 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
}
|
||||
if original == current {
|
||||
if original == (common.Hash{}) { // create slot (2.1.1)
|
||||
// EIP-8037: Return both regular and state gas. The interpreter
|
||||
// charges regular gas before state gas, preventing reservoir
|
||||
// inflation when the regular charge OOGs.
|
||||
// EIP-8037: Return both regular and state gas. System calls do not charge state gas.
|
||||
var stateGas uint64
|
||||
if !contract.IsSystemCall {
|
||||
stateGas = params.StorageCreationSize * evm.Context.CostPerGasByte
|
||||
}
|
||||
return GasCosts{
|
||||
RegularGas: cost.RegularGas + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929,
|
||||
StateGas: params.StorageCreationSize * evm.Context.CostPerGasByte,
|
||||
StateGas: stateGas,
|
||||
}, nil
|
||||
}
|
||||
if value == (common.Hash{}) { // delete slot (2.1.2b)
|
||||
|
|
@ -695,9 +697,18 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
}
|
||||
if original == value {
|
||||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// EIP 2200 Original clause:
|
||||
//evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
|
||||
evm.StateDB.AddRefund(params.StorageCreationSize*evm.Context.CostPerGasByte + params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
// EIP-8037 point (2): refund state gas directly to the reservoir
|
||||
// at the SSTORE restoration point (0→x→0 in same tx); not to the
|
||||
// refund counter, which is capped at gas_used/5.
|
||||
stateRefund := params.StorageCreationSize * evm.Context.CostPerGasByte
|
||||
contract.Gas.StateGas += stateRefund
|
||||
if contract.GasUsed.StateGas >= stateRefund {
|
||||
contract.GasUsed.StateGas -= stateRefund
|
||||
} else {
|
||||
contract.GasUsed.StateGas = 0
|
||||
}
|
||||
// Regular portion of the refund still goes through the refund counter.
|
||||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
} else { // reset to original existing slot (2.2.2.2)
|
||||
// EIP 2200 Original clause:
|
||||
// evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
|
||||
|
|
|
|||
|
|
@ -682,7 +682,9 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Stack.push(&stackvalue)
|
||||
|
||||
scope.Contract.RefundGas(suberr, regularGasUsed, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||
scope.Contract.RefundCreateStateGas(params.AccountCreationSize * evm.Context.CostPerGasByte)
|
||||
}
|
||||
if suberr == ErrExecutionReverted {
|
||||
evm.returnData = res // set REVERT data to return data buffer
|
||||
return res, nil
|
||||
|
|
@ -719,6 +721,9 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
scope.Stack.push(&stackvalue)
|
||||
|
||||
scope.Contract.RefundGas(suberr, regularGasUsed, returnGas, childGasUsed, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
if evm.chainRules.IsAmsterdam && suberr != nil {
|
||||
scope.Contract.RefundCreateStateGas(params.AccountCreationSize * evm.Context.CostPerGasByte)
|
||||
}
|
||||
|
||||
if suberr == ErrExecutionReverted {
|
||||
evm.returnData = res // set REVERT data to return data buffer
|
||||
|
|
|
|||
|
|
@ -71,6 +71,14 @@ type StateDB interface {
|
|||
// during the current transaction.
|
||||
IsNewContract(addr common.Address) bool
|
||||
|
||||
// SameTxSelfDestructs returns addresses that were created and then
|
||||
// self-destructed in the current transaction.
|
||||
SameTxSelfDestructs() []common.Address
|
||||
|
||||
// NewStorageSlotCount returns the number of storage slots written to a
|
||||
// non-zero value in the current transaction on the given address.
|
||||
NewStorageSlotCount(addr common.Address) int
|
||||
|
||||
// Empty returns whether the given account is empty. Empty
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
|
|
|
|||
Loading…
Reference in a new issue