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
a91cb20ae9
commit
0cb107dfd6
4 changed files with 66 additions and 38 deletions
|
|
@ -322,7 +322,7 @@ func (st *stateTransition) to() common.Address {
|
|||
return *st.msg.To
|
||||
}
|
||||
|
||||
func (st *stateTransition) buyGas() error {
|
||||
func (st *stateTransition) buyGas() (uint64, error) {
|
||||
mgval := new(big.Int).SetUint64(st.msg.GasLimit)
|
||||
mgval.Mul(mgval, st.msg.GasPrice)
|
||||
balanceCheck := new(big.Int).Set(mgval)
|
||||
|
|
@ -346,13 +346,10 @@ func (st *stateTransition) buyGas() error {
|
|||
}
|
||||
balanceCheckU256, overflow := uint256.FromBig(balanceCheck)
|
||||
if overflow {
|
||||
return fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
|
||||
return 0, fmt.Errorf("%w: address %v required balance exceeds 256 bits", ErrInsufficientFunds, st.msg.From.Hex())
|
||||
}
|
||||
if have, want := st.state.GetBalance(st.msg.From), balanceCheckU256; have.Cmp(want) < 0 {
|
||||
return fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||
}
|
||||
if err := st.gp.SubGas(st.msg.GasLimit); err != nil {
|
||||
return err
|
||||
return 0, fmt.Errorf("%w: address %v have %v want %v", ErrInsufficientFunds, st.msg.From.Hex(), have, want)
|
||||
}
|
||||
|
||||
if st.evm.Config.Tracer != nil && st.evm.Config.Tracer.OnGasChange != nil {
|
||||
|
|
@ -368,23 +365,23 @@ func (st *stateTransition) buyGas() error {
|
|||
st.gasRemaining = st.initialBudget.Copy()
|
||||
mgvalU256, _ := uint256.FromBig(mgval)
|
||||
st.state.SubBalance(st.msg.From, mgvalU256, tracing.BalanceDecreaseGasBuy)
|
||||
return nil
|
||||
return st.msg.GasLimit, nil
|
||||
}
|
||||
|
||||
func (st *stateTransition) preCheck() error {
|
||||
func (st *stateTransition) preCheck() (uint64, error) {
|
||||
// Only check transactions that are not fake
|
||||
msg := st.msg
|
||||
if !msg.SkipNonceChecks {
|
||||
// Make sure this transaction's nonce is correct.
|
||||
stNonce := st.state.GetNonce(msg.From)
|
||||
if msgNonce := msg.Nonce; stNonce < msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
||||
return 0, fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooHigh,
|
||||
msg.From.Hex(), msgNonce, stNonce)
|
||||
} else if stNonce > msgNonce {
|
||||
return fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
||||
return 0, fmt.Errorf("%w: address %v, tx: %d state: %d", ErrNonceTooLow,
|
||||
msg.From.Hex(), msgNonce, stNonce)
|
||||
} else if stNonce+1 < stNonce {
|
||||
return fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
||||
return 0, fmt.Errorf("%w: address %v, nonce: %d", ErrNonceMax,
|
||||
msg.From.Hex(), stNonce)
|
||||
}
|
||||
}
|
||||
|
|
@ -393,13 +390,13 @@ func (st *stateTransition) preCheck() error {
|
|||
if !msg.SkipTransactionChecks {
|
||||
// Verify tx gas limit does not exceed EIP-7825 cap.
|
||||
if !isAmsterdam && isOsaka && msg.GasLimit > params.MaxTxGas {
|
||||
return fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||
return 0, fmt.Errorf("%w (cap: %d, tx: %d)", ErrGasLimitTooHigh, params.MaxTxGas, msg.GasLimit)
|
||||
}
|
||||
// Make sure the sender is an EOA
|
||||
code := st.state.GetCode(msg.From)
|
||||
_, delegated := types.ParseDelegation(code)
|
||||
if len(code) > 0 && !delegated {
|
||||
return fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code))
|
||||
return 0, fmt.Errorf("%w: address %v, len(code): %d", ErrSenderNoEOA, msg.From.Hex(), len(code))
|
||||
}
|
||||
}
|
||||
// Make sure that transaction gasFeeCap is greater than the baseFee (post london)
|
||||
|
|
@ -408,21 +405,21 @@ func (st *stateTransition) preCheck() error {
|
|||
skipCheck := st.evm.Config.NoBaseFee && msg.GasFeeCap.BitLen() == 0 && msg.GasTipCap.BitLen() == 0
|
||||
if !skipCheck {
|
||||
if l := msg.GasFeeCap.BitLen(); l > 256 {
|
||||
return fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
|
||||
return 0, fmt.Errorf("%w: address %v, maxFeePerGas bit length: %d", ErrFeeCapVeryHigh,
|
||||
msg.From.Hex(), l)
|
||||
}
|
||||
if l := msg.GasTipCap.BitLen(); l > 256 {
|
||||
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
|
||||
return 0, fmt.Errorf("%w: address %v, maxPriorityFeePerGas bit length: %d", ErrTipVeryHigh,
|
||||
msg.From.Hex(), l)
|
||||
}
|
||||
if msg.GasFeeCap.Cmp(msg.GasTipCap) < 0 {
|
||||
return fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
|
||||
return 0, fmt.Errorf("%w: address %v, maxPriorityFeePerGas: %s, maxFeePerGas: %s", ErrTipAboveFeeCap,
|
||||
msg.From.Hex(), msg.GasTipCap, msg.GasFeeCap)
|
||||
}
|
||||
// This will panic if baseFee is nil, but basefee presence is verified
|
||||
// as part of header validation.
|
||||
if msg.GasFeeCap.Cmp(st.evm.Context.BaseFee) < 0 {
|
||||
return fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow,
|
||||
return 0, fmt.Errorf("%w: address %v, maxFeePerGas: %s, baseFee: %s", ErrFeeCapTooLow,
|
||||
msg.From.Hex(), msg.GasFeeCap, st.evm.Context.BaseFee)
|
||||
}
|
||||
}
|
||||
|
|
@ -433,17 +430,17 @@ func (st *stateTransition) preCheck() error {
|
|||
// has it as a non-nillable value, so any msg derived from blob transaction has it non-nil.
|
||||
// However, messages created through RPC (eth_call) don't have this restriction.
|
||||
if msg.To == nil {
|
||||
return ErrBlobTxCreate
|
||||
return 0, ErrBlobTxCreate
|
||||
}
|
||||
if len(msg.BlobHashes) == 0 {
|
||||
return ErrMissingBlobHashes
|
||||
return 0, ErrMissingBlobHashes
|
||||
}
|
||||
if isOsaka && len(msg.BlobHashes) > params.BlobTxMaxBlobs {
|
||||
return ErrTooManyBlobs
|
||||
return 0, ErrTooManyBlobs
|
||||
}
|
||||
for i, hash := range msg.BlobHashes {
|
||||
if !kzg4844.IsValidVersionedHash(hash[:]) {
|
||||
return fmt.Errorf("blob %d has invalid hash version", i)
|
||||
return 0, fmt.Errorf("blob %d has invalid hash version", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -456,7 +453,7 @@ func (st *stateTransition) preCheck() error {
|
|||
// This will panic if blobBaseFee is nil, but blobBaseFee presence
|
||||
// is verified as part of header validation.
|
||||
if msg.BlobGasFeeCap.Cmp(st.evm.Context.BlobBaseFee) < 0 {
|
||||
return fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow,
|
||||
return 0, fmt.Errorf("%w: address %v blobGasFeeCap: %v, blobBaseFee: %v", ErrBlobFeeCapTooLow,
|
||||
msg.From.Hex(), msg.BlobGasFeeCap, st.evm.Context.BlobBaseFee)
|
||||
}
|
||||
}
|
||||
|
|
@ -465,10 +462,10 @@ func (st *stateTransition) preCheck() error {
|
|||
// Check that EIP-7702 authorization list signatures are well formed.
|
||||
if msg.SetCodeAuthorizations != nil {
|
||||
if msg.To == nil {
|
||||
return fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From)
|
||||
return 0, fmt.Errorf("%w (sender %v)", ErrSetCodeTxCreate, msg.From)
|
||||
}
|
||||
if len(msg.SetCodeAuthorizations) == 0 {
|
||||
return fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)
|
||||
return 0, fmt.Errorf("%w (sender %v)", ErrEmptyAuthList, msg.From)
|
||||
}
|
||||
}
|
||||
return st.buyGas()
|
||||
|
|
@ -496,7 +493,8 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// 6. caller has enough balance to cover asset transfer for **topmost** call
|
||||
|
||||
// Check clauses 1-3, buy gas if everything is correct
|
||||
if err := st.preCheck(); err != nil {
|
||||
gas, err := st.preCheck()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -506,12 +504,33 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
contractCreation = msg.To == nil
|
||||
floorDataGas uint64
|
||||
)
|
||||
|
||||
if !rules.IsAmsterdam {
|
||||
if err := st.gp.SubGas(gas); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Check clauses 4-5, subtract intrinsic gas if everything is correct
|
||||
cost, err := IntrinsicGas(msg.Data, msg.AccessList, msg.SetCodeAuthorizations, contractCreation, rules, st.evm.Context.CostPerGasByte)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Regular gas check for block inclusion post-amsterdam includes state gas.
|
||||
if rules.IsAmsterdam {
|
||||
subGasAmount := msg.GasLimit
|
||||
if subGasAmount > cost.StateGas {
|
||||
subGasAmount -= cost.StateGas
|
||||
} else {
|
||||
subGasAmount = 0
|
||||
}
|
||||
subGasAmount = min(subGasAmount, params.MaxTxGas)
|
||||
if err := st.gp.SubGas(subGasAmount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Compute the floor data cost (EIP-7623), needed for both Prague and Amsterdam validation.
|
||||
if rules.IsPrague {
|
||||
floorDataGas, err = FloorDataGas(rules, msg.Data, msg.AccessList)
|
||||
|
|
|
|||
|
|
@ -141,22 +141,21 @@ func (c *Contract) UseGas(cost GasCosts, logger *tracing.Hooks, reason tracing.G
|
|||
|
||||
// RefundGas refunds gas to the contract.
|
||||
func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBudget, gasUsed GasUsed, logger *tracing.Hooks, reason tracing.GasChangeReason) {
|
||||
// If the preceding call errored, return the child's state-gas reservoir
|
||||
// and any net state-gas consumption to the parent. A negative
|
||||
// gasUsed.StateGas (an unabsorbed SSTORE 0→x→0 refund) is an inflation
|
||||
// to undo: the matching state mutation was reverted with the child, so
|
||||
// the refund must not leak back to the parent.
|
||||
if err != nil {
|
||||
if gasUsed.StateGas >= 0 {
|
||||
if gasUsed.StateGas > 0 {
|
||||
gas.StateGas += uint64(gasUsed.StateGas)
|
||||
} else if undo := uint64(-gasUsed.StateGas); gas.StateGas >= undo {
|
||||
gas.StateGas -= undo
|
||||
} else {
|
||||
gas.StateGas = 0
|
||||
}
|
||||
gasUsed.StateGas = 0
|
||||
if gas.StateGasRefund > 0 {
|
||||
if gas.StateGas >= gas.StateGasRefund {
|
||||
gas.StateGas -= gas.StateGasRefund
|
||||
} else {
|
||||
gas.StateGas = 0
|
||||
}
|
||||
gas.StateGasRefund = 0
|
||||
}
|
||||
}
|
||||
if gas.RegularGas == 0 && gas.StateGas == 0 && gasUsed.StateGas == 0 && gasUsed.RegularGas == 0 {
|
||||
if gas.RegularGas == 0 && gas.StateGas == 0 && gasUsed.StateGas == 0 && gasUsed.RegularGas == 0 && gas.StateGasRefund == 0 {
|
||||
return
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil && reason != tracing.GasChangeIgnored {
|
||||
|
|
@ -164,6 +163,9 @@ func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBud
|
|||
}
|
||||
c.Gas.RegularGas += gas.RegularGas
|
||||
c.Gas.StateGas = gas.StateGas
|
||||
// Propagate the child's applied inline refund so a later ancestor
|
||||
// error can still undo the reservoir inflation.
|
||||
c.Gas.StateGasRefund += gas.StateGasRefund
|
||||
c.GasUsed.StateGas += gasUsed.StateGas
|
||||
c.GasUsed.RegularGas = initialRegularGasUsed + gasUsed.RegularGas
|
||||
}
|
||||
|
|
@ -172,6 +174,7 @@ func (c *Contract) RefundGas(err error, initialRegularGasUsed uint64, gas GasBud
|
|||
func (c *Contract) RefundCreateStateGas(refund uint64) {
|
||||
if refund > 0 {
|
||||
c.Gas.StateGas += refund
|
||||
c.Gas.StateGasRefund += refund
|
||||
c.GasUsed.StateGas -= int64(refund)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -699,9 +699,11 @@ func gasSStore8037(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memo
|
|||
if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
|
||||
// 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.
|
||||
// refund counter, which is capped at gas_used/5. StateGasRefund
|
||||
// tracks the reservoir inflation so a frame error undoes it.
|
||||
stateRefund := params.StorageCreationSize * evm.Context.CostPerGasByte
|
||||
contract.Gas.StateGas += stateRefund
|
||||
contract.Gas.StateGasRefund += stateRefund
|
||||
contract.GasUsed.StateGas -= int64(stateRefund)
|
||||
// Regular portion of the refund still goes through the refund counter.
|
||||
evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929 - params.WarmStorageReadCostEIP2929)
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ package vm
|
|||
import "fmt"
|
||||
|
||||
// GasUsed is the per-frame accumulator for gas consumption.
|
||||
// StateGas is signed because of 0 -> X -> 0 SSTORE refunds.
|
||||
// StateGas is signed, because it can be negative in a 0 -> x -> 0 scenario.
|
||||
type GasUsed struct {
|
||||
RegularGas uint64
|
||||
StateGas int64
|
||||
|
|
@ -55,6 +55,10 @@ func (g GasCosts) String() string {
|
|||
type GasBudget struct {
|
||||
RegularGas uint64 // The leftover gas for execution and state gas usage
|
||||
StateGas uint64 // The state gas reservoir
|
||||
|
||||
// Tracks the gas refunds in this call frame. Needed so we can
|
||||
// revert the refunds if the call frame reverts.
|
||||
StateGasRefund uint64
|
||||
}
|
||||
|
||||
// NewGasBudgetReg creates a GasBudget with the given initial regular gas allowance.
|
||||
|
|
|
|||
Loading…
Reference in a new issue