mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-24 23:46:17 +00:00
Claude fixes for Glam-6
- Refund state gas on failing call - Keep balance on selfdestruct to self
This commit is contained in:
parent
3523721bd4
commit
dd073df1cd
6 changed files with 65 additions and 37 deletions
|
|
@ -122,6 +122,28 @@ func (s *stateObject) markSelfdestructed() {
|
|||
s.selfDestructed = true
|
||||
}
|
||||
|
||||
// clearPreservingBalance resets the account's nonce, code, and storage while
|
||||
// keeping its balance, mirroring the spec's clear_account_preserving_balance
|
||||
// (EIP-8246: SELFDESTRUCT no longer burns the balance). It also revokes the
|
||||
// self-destruct and new-contract flags so the cleared account is finalised as
|
||||
// an ordinary balance-only account rather than being deleted. Only valid for
|
||||
// EIP-6780-eligible (same-transaction) accounts, whose pre-transaction nonce,
|
||||
// code, and storage are empty, so discarding the in-transaction writes leaves
|
||||
// the persistent trie untouched.
|
||||
func (s *stateObject) clearPreservingBalance() {
|
||||
s.data.Nonce = 0
|
||||
s.data.CodeHash = types.EmptyCodeHash.Bytes()
|
||||
s.data.Root = types.EmptyRootHash
|
||||
s.code = nil
|
||||
s.dirtyCode = false
|
||||
s.originStorage = make(Storage)
|
||||
s.dirtyStorage = make(Storage)
|
||||
s.pendingStorage = make(Storage)
|
||||
s.uncommittedStorage = make(Storage)
|
||||
s.selfDestructed = false
|
||||
s.newContract = false
|
||||
}
|
||||
|
||||
func (s *stateObject) touch() {
|
||||
s.db.journal.touchChange(s.address)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -829,6 +829,14 @@ func (s *StateDB) Finalise(deleteEmptyObjects bool) *bal.ConstructionBlockAccess
|
|||
// finalise or delete, so ignore it here.
|
||||
continue
|
||||
}
|
||||
// EIP-8246 (Amsterdam, gated by BAL being enabled): a self-destructed
|
||||
// same-transaction account that still holds balance is not deleted.
|
||||
// Instead its nonce, code, and storage are cleared while the balance is
|
||||
// preserved, so it survives as a balance-only account. The cleared
|
||||
// object then falls through to the ordinary update path below.
|
||||
if obj.selfDestructed && s.stateAccessList != nil && !obj.Balance().IsZero() {
|
||||
obj.clearPreservingBalance()
|
||||
}
|
||||
if obj.selfDestructed || (deleteEmptyObjects && obj.empty()) {
|
||||
delete(s.stateObjects, obj.address)
|
||||
s.markDelete(addr)
|
||||
|
|
|
|||
|
|
@ -121,6 +121,12 @@ type EVM struct {
|
|||
// applied in opCall*.
|
||||
callGasTemp uint64
|
||||
|
||||
// callNewAccountChargedTemp records whether the current CALL charged the
|
||||
// EIP-8037 NEW_ACCOUNT state gas (value transfer to an empty account). It is
|
||||
// set in gasCallIntrinsic and read in opCall to refund that charge in LIFO
|
||||
// order when the sub-call fails (spec generic_call credit_state_gas_refund).
|
||||
callNewAccountChargedTemp bool
|
||||
|
||||
// precompiles holds the precompiled contracts for the current epoch
|
||||
precompiles map[common.Address]PrecompiledContract
|
||||
|
||||
|
|
@ -587,8 +593,7 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
contract.SetCallCode(common.Hash{}, code)
|
||||
contract.IsDeployment = true
|
||||
|
||||
var depositHalt bool
|
||||
ret, depositHalt, err = evm.initNewContract(contract, address)
|
||||
ret, _, err = evm.initNewContract(contract, address)
|
||||
|
||||
// Special case: ErrCodeStoreOutOfGas pre-Homestead does NOT roll back
|
||||
// state and gas is preserved (i.e., treated as success).
|
||||
|
|
@ -596,14 +601,10 @@ func (evm *EVM) create(caller common.Address, code []byte, gas GasBudget, value
|
|||
evm.StateDB.RevertToSnapshot(snapshot)
|
||||
|
||||
// EIP-8037: a code-deposit halt (initcode body succeeded, deposit step
|
||||
// failed) keeps the state gas the body consumed for discard at the tx
|
||||
// level, rather than refunding the reservoir like a mid-execution halt.
|
||||
var exit GasBudget
|
||||
if depositHalt && evm.chainRules.IsAmsterdam {
|
||||
exit = contract.Gas.ExitCodeDepositHalt()
|
||||
} else {
|
||||
exit = contract.Gas.Exit(err)
|
||||
}
|
||||
// failed) is metered as an ordinary exceptional halt — the spec's
|
||||
// process_create_message exception handler runs refill_frame_state_gas
|
||||
// then burns the remaining regular gas, exactly like ExitHalt.
|
||||
exit := contract.Gas.Exit(err)
|
||||
if err != ErrExecutionReverted {
|
||||
if evm.Config.Tracer.HasGasHook() {
|
||||
evm.Config.Tracer.EmitGasChange(contract.Gas.AsTracing(), exit.AsTracing(), tracing.GasChangeCallFailedExecution)
|
||||
|
|
|
|||
|
|
@ -514,6 +514,7 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
// reservoir, spilling into regular gas only when the reservoir is
|
||||
// exhausted, mirroring the spec's inline charge_state_gas in
|
||||
// system.call.
|
||||
evm.callNewAccountChargedTemp = false
|
||||
if transfersValue && evm.StateDB.Empty(address) {
|
||||
stateGas := params.AccountCreationSize * evm.Context.CostPerStateByte
|
||||
regularAfterCall := contract.Gas.RegularGas - gas
|
||||
|
|
@ -523,6 +524,7 @@ func gasCallIntrinsic(evm *EVM, contract *Contract, stack *Stack, mem *Memory, m
|
|||
if !contract.chargeState(stateGas, evm.Config.Tracer, tracing.GasChangeAccountCreation) {
|
||||
return 0, ErrOutOfGas
|
||||
}
|
||||
evm.callNewAccountChargedTemp = true
|
||||
}
|
||||
return gas, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,10 +61,10 @@ func (g GasCosts) String() string {
|
|||
// - At absorption: the caller's Absorb method merges the child's leftover
|
||||
// budget into its own running budget.
|
||||
type GasBudget struct {
|
||||
RegularGas uint64 // remaining regular-gas balance (or leftover for caller to absorb)
|
||||
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
||||
UsedRegularGas uint64 // gross regular gas consumed in this frame
|
||||
UsedStateGas int64 // signed net state-gas consumed in this frame
|
||||
RegularGas uint64 // remaining regular-gas balance (or leftover for caller to absorb)
|
||||
StateGas uint64 // remaining state-gas reservoir (or leftover for caller to absorb)
|
||||
UsedRegularGas uint64 // gross regular gas consumed in this frame
|
||||
UsedStateGas int64 // signed net state-gas consumed in this frame
|
||||
SpilledStateGas uint64 // state gas that spilled from the reservoir into regular gas in this frame
|
||||
}
|
||||
|
||||
|
|
@ -264,26 +264,6 @@ func (g GasBudget) ExitHalt() GasBudget {
|
|||
}
|
||||
}
|
||||
|
||||
// ExitCodeDepositHalt produces the leftover for a CREATE/CREATE2 frame whose
|
||||
// initcode body ran to completion but then failed during the code-deposit step
|
||||
// (oversized code, 0xEF prefix, or insufficient gas for the hash/deposit
|
||||
// charge). Per the spec's process_create_message exception handler, this path
|
||||
// differs from a mid-execution ExceptionalHalt: only the remaining regular gas
|
||||
// is burned, while the state gas the (successful) body consumed is KEPT in
|
||||
// UsedStateGas rather than refunded to the reservoir. The state changes are
|
||||
// reverted by the caller, but the state-gas accounting is propagated upward so
|
||||
// the top-level tx settlement can discard it as the state dimension (rather
|
||||
// than folding it back into the combined balance, which would wrongly deflate
|
||||
// the regular dimension).
|
||||
func (g GasBudget) ExitCodeDepositHalt() GasBudget {
|
||||
return GasBudget{
|
||||
RegularGas: 0,
|
||||
StateGas: g.StateGas,
|
||||
UsedRegularGas: g.UsedRegularGas + g.RegularGas,
|
||||
UsedStateGas: g.UsedStateGas,
|
||||
}
|
||||
}
|
||||
|
||||
// Exit dispatches on err to the appropriate exit-form constructor
|
||||
// for the post-evm.Run path:
|
||||
//
|
||||
|
|
|
|||
|
|
@ -741,6 +741,8 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
// We can use this as a temporary value
|
||||
temp := stack.pop()
|
||||
gas := evm.callGasTemp
|
||||
// Capture before the sub-call: nested CALLs would overwrite the flag.
|
||||
newAccountCharged := evm.callNewAccountChargedTemp
|
||||
// Pop other call parameters.
|
||||
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
toAddr := common.Address(addr.Bytes20())
|
||||
|
|
@ -772,6 +774,13 @@ func opCall(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
|
|||
}
|
||||
scope.Contract.refundGas(result, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
|
||||
|
||||
// EIP-8037: when a value-bearing CALL to an empty account fails, no account
|
||||
// is created, so refund the NEW_ACCOUNT state gas in LIFO order (spec
|
||||
// generic_call credit_state_gas_refund on child error).
|
||||
if evm.chainRules.IsAmsterdam && err != nil && newAccountCharged {
|
||||
scope.Contract.Gas.CreditStateRefund(params.AccountCreationSize * evm.Context.CostPerStateByte)
|
||||
}
|
||||
|
||||
evm.returnData = ret
|
||||
return ret, nil
|
||||
}
|
||||
|
|
@ -941,12 +950,18 @@ func opSelfdestruct6780(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, erro
|
|||
beneficiary = common.Address(top.Bytes20())
|
||||
newContract = evm.StateDB.IsNewContract(this)
|
||||
)
|
||||
// Contract is new and will actually be deleted.
|
||||
// Contract is new and is eligible for deletion (EIP-6780).
|
||||
if newContract {
|
||||
if this != beneficiary { // Skip no-op transfer when self-destructing to self.
|
||||
if this != beneficiary {
|
||||
// Transfer the balance to a distinct beneficiary.
|
||||
evm.StateDB.AddBalance(beneficiary, balance, tracing.BalanceIncreaseSelfdestruct)
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
} else if !evm.chainRules.IsAmsterdam {
|
||||
// Pre-EIP-8246: self-destructing to self burns the balance.
|
||||
// EIP-8246 (Amsterdam) preserves it instead; the account is cleared
|
||||
// preserving balance at the transaction boundary (StateDB.Finalise).
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
}
|
||||
evm.StateDB.SubBalance(this, balance, tracing.BalanceDecreaseSelfdestruct)
|
||||
evm.StateDB.SelfDestruct(this)
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue