mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-05 20:51:16 +00:00
core/vm: fix EIP-8037 CALL state gas ordering
Charge new-account state gas BEFORE the 63/64 child gas allocation rather than after. When state gas spills from an empty reservoir to regular gas, it must reduce the gas available for callGasTemp. Otherwise the spillover exceeds the 1/64 remainder after child gas allocation, causing Underflow → OOG on CALLs that should succeed. This matches nethermind's implementation which calls ConsumeNewAccountCreation() before the 63/64 calculation (EvmInstructions.Call.cs:187-213). Verified: geth+besu in sync through 159 blocks with spamoor load, geth+nethermind in sync through 50+ post-Amsterdam blocks. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
51018a536f
commit
c33c10cb1b
2 changed files with 26 additions and 2 deletions
|
|
@ -783,11 +783,13 @@ func (st *stateTransition) blockGasUsed(intrinsicGas, execGasStart vm.GasCosts)
|
||||||
execStateUsed := st.gasRemaining.TotalStateGasCharged
|
execStateUsed := st.gasRemaining.TotalStateGasCharged
|
||||||
// Exclude state gas that was charged from regular gas but then reverted.
|
// Exclude state gas that was charged from regular gas but then reverted.
|
||||||
// This gas was consumed from the regular pool but was for state operations
|
// This gas was consumed from the regular pool but was for state operations
|
||||||
// that didn't persist, so it shouldn't count in the regular dimension.
|
// 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
|
||||||
|
|
||||||
txRegular := intrinsicGas.RegularGas + execRegularUsed
|
txRegular := intrinsicGas.RegularGas + execRegularUsed
|
||||||
txState := intrinsicGas.StateGas + execStateUsed - st.stateGasRefund
|
txState := intrinsicGas.StateGas + execStateUsed - st.stateGasRefund
|
||||||
|
|
||||||
return txRegular, txState
|
return txRegular, txState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -295,6 +295,21 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EIP-8037: Charge state gas for new account creation BEFORE the 63/64
|
||||||
|
// child gas allocation. State gas that spills from an empty reservoir to
|
||||||
|
// regular gas must reduce the gas available for callGasTemp, otherwise
|
||||||
|
// the Underflow check in UseGas will fail when the spillover exceeds the
|
||||||
|
// tiny 1/64 remainder after child gas allocation.
|
||||||
|
var stateGasCharged uint64
|
||||||
|
if evm.chainRules.IsAmsterdam && oldStateful.StateGas > 0 {
|
||||||
|
stateGasCharged = oldStateful.StateGas
|
||||||
|
stateGasCost := GasCosts{StateGas: stateGasCharged}
|
||||||
|
if contract.Gas.Underflow(stateGasCost) {
|
||||||
|
return GasCosts{}, ErrOutOfGas
|
||||||
|
}
|
||||||
|
contract.Gas.Sub(stateGasCost)
|
||||||
|
}
|
||||||
|
|
||||||
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, eip150BaseGas.RegularGas, stack.Back(0))
|
evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas.RegularGas, eip150BaseGas.RegularGas, stack.Back(0))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return GasCosts{}, err
|
return GasCosts{}, err
|
||||||
|
|
@ -331,6 +346,13 @@ func makeCallVariantGasCall(oldCalculatorStateful, oldCalculatorStateless gasFun
|
||||||
return GasCosts{}, ErrGasUintOverflow
|
return GasCosts{}, ErrGasUintOverflow
|
||||||
}
|
}
|
||||||
|
|
||||||
return GasCosts{RegularGas: totalCost, StateGas: oldStateful.StateGas}, nil
|
// If state gas was already charged directly (Amsterdam), don't include
|
||||||
|
// it in the returned cost — it would be double-charged by the
|
||||||
|
// interpreter's UseGas/Sub which increments TotalStateGasCharged again.
|
||||||
|
returnedStateGas := oldStateful.StateGas
|
||||||
|
if stateGasCharged > 0 {
|
||||||
|
returnedStateGas = 0
|
||||||
|
}
|
||||||
|
return GasCosts{RegularGas: totalCost, StateGas: returnedStateGas}, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue