core/vm: more fixes from ai

This commit is contained in:
Marius van der Wijden 2026-03-09 13:27:42 +01:00
parent da4700411e
commit 22738576fc
3 changed files with 25 additions and 5 deletions

View file

@ -603,12 +603,22 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
var receiptGasUsed uint64
if rules.IsAmsterdam {
receiptGasUsed = msg.GasLimit - st.gasRemaining.RegularGas - st.gasRemaining.StateGas - st.stateGasRefund
// On successful execution, refund regular gas that was consumed for
// state operations in child calls that subsequently reverted. This gas
// paid for state growth that didn't persist, so the user shouldn't pay.
// On failed execution (exceptional halt), all gas is consumed — no refund.
if vmerr == nil {
receiptGasUsed -= st.gasRemaining.RevertedStateGasSpill
}
}
// Return gas to the user
if rules.IsAmsterdam {
// In Amsterdam, return regular gas + unspent state gas reservoir + state gas refund.
gasReturn := st.gasRemaining.RegularGas + st.gasRemaining.StateGas + st.stateGasRefund
if vmerr == nil {
gasReturn += st.gasRemaining.RevertedStateGasSpill
}
remaining := uint256.NewInt(gasReturn)
remaining.Mul(remaining, uint256.MustFromBig(st.msg.GasPrice))
st.state.AddBalance(st.msg.From, remaining, tracing.BalanceIncreaseGasReturn)
@ -785,7 +795,7 @@ func (st *stateTransition) blockGasUsed(intrinsicGas, execGasStart vm.GasCosts)
// This gas was consumed from the regular pool but was for state operations
// that didn't persist, so it shouldn't count in either dimension for block
// accounting (invisible to the block). This matches nethermind's approach.
execRegularUsed := totalExecUsed - execStateUsed - st.gasRemaining.RevertedStateGasSpill
execRegularUsed := totalExecUsed - execStateUsed - st.gasRemaining.RevertedStateGasSpill - st.gasRemaining.CollisionConsumedGas
txRegular := intrinsicGas.RegularGas + execRegularUsed
txState := intrinsicGas.StateGas + execStateUsed - st.stateGasRefund

View file

@ -17,7 +17,14 @@ type GasCosts struct {
// call fails, its state changes are undone, but the regular gas was already
// consumed. Block gas accounting must exclude this amount from the regular
// gas dimension since it was for state operations that didn't persist.
// This gas is refunded to the user (invisible to both block and receipt).
RevertedStateGasSpill uint64
// CollisionConsumedGas tracks regular gas consumed on CREATE/CREATE2 address
// collision. On collision, the child's regular gas is consumed (user pays)
// but must be excluded from block regular gas accounting to preserve 2D
// block gas semantics. Unlike RevertedStateGasSpill, this is NOT refunded.
CollisionConsumedGas uint64
}
func (g GasCosts) Max() uint64 {
@ -64,6 +71,7 @@ func (g *GasCosts) Add(b GasCosts) {
g.StateGas += b.StateGas
g.TotalStateGasCharged += b.TotalStateGasCharged
g.RevertedStateGasSpill += b.RevertedStateGasSpill
g.CollisionConsumedGas += b.CollisionConsumedGas
}
// RevertStateGas handles state gas accounting when a call reverts (EIP-8037).

View file

@ -704,9 +704,10 @@ func opCreate(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
returnGas.StateGas = stateGas
}
// On address collision, child's regular gas is consumed (not returned).
// Track as RevertedStateGasSpill so block 2D accounting is unaffected.
// Track as CollisionConsumedGas so block 2D accounting is unaffected
// while the user still pays for the consumed gas (not refunded).
if evm.chainRules.IsAmsterdam && errors.Is(suberr, ErrContractAddressCollision) {
returnGas.RevertedStateGasSpill += returnGas.RegularGas
returnGas.CollisionConsumedGas += returnGas.RegularGas
returnGas.RegularGas = 0
}
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)
@ -770,9 +771,10 @@ func opCreate2(pc *uint64, evm *EVM, scope *ScopeContext) ([]byte, error) {
returnGas.StateGas = stateGas
}
// On address collision, child's regular gas is consumed (not returned).
// Track as RevertedStateGasSpill so block 2D accounting is unaffected.
// Track as CollisionConsumedGas so block 2D accounting is unaffected
// while the user still pays for the consumed gas (not refunded).
if evm.chainRules.IsAmsterdam && errors.Is(suberr, ErrContractAddressCollision) {
returnGas.RevertedStateGasSpill += returnGas.RegularGas
returnGas.CollisionConsumedGas += returnGas.RegularGas
returnGas.RegularGas = 0
}
scope.Contract.RefundGas(returnGas, evm.Config.Tracer, tracing.GasChangeCallLeftOverRefunded)