github.com/ethereum/go-ethereum@v1.14.4-0.20240516095835-473ee8fc07a3/core/txpool/validation.go (about)

     1  // Copyright 2023 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package txpool
    18  
    19  import (
    20  	"crypto/sha256"
    21  	"errors"
    22  	"fmt"
    23  	"math/big"
    24  
    25  	"github.com/ethereum/go-ethereum/common"
    26  	"github.com/ethereum/go-ethereum/core"
    27  	"github.com/ethereum/go-ethereum/core/state"
    28  	"github.com/ethereum/go-ethereum/core/types"
    29  	"github.com/ethereum/go-ethereum/crypto/kzg4844"
    30  	"github.com/ethereum/go-ethereum/log"
    31  	"github.com/ethereum/go-ethereum/params"
    32  )
    33  
    34  var (
    35  	// blobTxMinBlobGasPrice is the big.Int version of the configured protocol
    36  	// parameter to avoid constructing a new big integer for every transaction.
    37  	blobTxMinBlobGasPrice = big.NewInt(params.BlobTxMinBlobGasprice)
    38  )
    39  
    40  // ValidationOptions define certain differences between transaction validation
    41  // across the different pools without having to duplicate those checks.
    42  type ValidationOptions struct {
    43  	Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules
    44  
    45  	Accept  uint8    // Bitmap of transaction types that should be accepted for the calling pool
    46  	MaxSize uint64   // Maximum size of a transaction that the caller can meaningfully handle
    47  	MinTip  *big.Int // Minimum gas tip needed to allow a transaction into the caller pool
    48  }
    49  
    50  // ValidateTransaction is a helper method to check whether a transaction is valid
    51  // according to the consensus rules, but does not check state-dependent validation
    52  // (balance, nonce, etc).
    53  //
    54  // This check is public to allow different transaction pools to check the basic
    55  // rules without duplicating code and running the risk of missed updates.
    56  func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error {
    57  	// Ensure transactions not implemented by the calling pool are rejected
    58  	if opts.Accept&(1<<tx.Type()) == 0 {
    59  		return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
    60  	}
    61  	// Before performing any expensive validations, sanity check that the tx is
    62  	// smaller than the maximum limit the pool can meaningfully handle
    63  	if tx.Size() > opts.MaxSize {
    64  		return fmt.Errorf("%w: transaction size %v, limit %v", ErrOversizedData, tx.Size(), opts.MaxSize)
    65  	}
    66  	// Ensure only transactions that have been enabled are accepted
    67  	if !opts.Config.IsBerlin(head.Number) && tx.Type() != types.LegacyTxType {
    68  		return fmt.Errorf("%w: type %d rejected, pool not yet in Berlin", core.ErrTxTypeNotSupported, tx.Type())
    69  	}
    70  	if !opts.Config.IsLondon(head.Number) && tx.Type() == types.DynamicFeeTxType {
    71  		return fmt.Errorf("%w: type %d rejected, pool not yet in London", core.ErrTxTypeNotSupported, tx.Type())
    72  	}
    73  	if !opts.Config.IsCancun(head.Number, head.Time) && tx.Type() == types.BlobTxType {
    74  		return fmt.Errorf("%w: type %d rejected, pool not yet in Cancun", core.ErrTxTypeNotSupported, tx.Type())
    75  	}
    76  	// Check whether the init code size has been exceeded
    77  	if opts.Config.IsShanghai(head.Number, head.Time) && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize {
    78  		return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize)
    79  	}
    80  	// Transactions can't be negative. This may never happen using RLP decoded
    81  	// transactions but may occur for transactions created using the RPC.
    82  	if tx.Value().Sign() < 0 {
    83  		return ErrNegativeValue
    84  	}
    85  	// Ensure the transaction doesn't exceed the current block limit gas
    86  	if head.GasLimit < tx.Gas() {
    87  		return ErrGasLimit
    88  	}
    89  	// Sanity check for extremely large numbers (supported by RLP or RPC)
    90  	if tx.GasFeeCap().BitLen() > 256 {
    91  		return core.ErrFeeCapVeryHigh
    92  	}
    93  	if tx.GasTipCap().BitLen() > 256 {
    94  		return core.ErrTipVeryHigh
    95  	}
    96  	// Ensure gasFeeCap is greater than or equal to gasTipCap
    97  	if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 {
    98  		return core.ErrTipAboveFeeCap
    99  	}
   100  	// Make sure the transaction is signed properly
   101  	if _, err := types.Sender(signer, tx); err != nil {
   102  		return ErrInvalidSender
   103  	}
   104  	// Ensure the transaction has more gas than the bare minimum needed to cover
   105  	// the transaction metadata
   106  	intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil, true, opts.Config.IsIstanbul(head.Number), opts.Config.IsShanghai(head.Number, head.Time))
   107  	if err != nil {
   108  		return err
   109  	}
   110  	if tx.Gas() < intrGas {
   111  		return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas)
   112  	}
   113  	// Ensure the gasprice is high enough to cover the requirement of the calling pool
   114  	if tx.GasTipCapIntCmp(opts.MinTip) < 0 {
   115  		return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrUnderpriced, tx.GasTipCap(), opts.MinTip)
   116  	}
   117  	if tx.Type() == types.BlobTxType {
   118  		// Ensure the blob fee cap satisfies the minimum blob gas price
   119  		if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 {
   120  			return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrUnderpriced, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice)
   121  		}
   122  		sidecar := tx.BlobTxSidecar()
   123  		if sidecar == nil {
   124  			return errors.New("missing sidecar in blob transaction")
   125  		}
   126  		// Ensure the number of items in the blob transaction and various side
   127  		// data match up before doing any expensive validations
   128  		hashes := tx.BlobHashes()
   129  		if len(hashes) == 0 {
   130  			return errors.New("blobless blob transaction")
   131  		}
   132  		if len(hashes) > params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob {
   133  			return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), params.MaxBlobGasPerBlock/params.BlobTxBlobGasPerBlob)
   134  		}
   135  		// Ensure commitments, proofs and hashes are valid
   136  		if err := validateBlobSidecar(hashes, sidecar); err != nil {
   137  			return err
   138  		}
   139  	}
   140  	return nil
   141  }
   142  
   143  func validateBlobSidecar(hashes []common.Hash, sidecar *types.BlobTxSidecar) error {
   144  	if len(sidecar.Blobs) != len(hashes) {
   145  		return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes))
   146  	}
   147  	if len(sidecar.Commitments) != len(hashes) {
   148  		return fmt.Errorf("invalid number of %d blob commitments compared to %d blob hashes", len(sidecar.Commitments), len(hashes))
   149  	}
   150  	if len(sidecar.Proofs) != len(hashes) {
   151  		return fmt.Errorf("invalid number of %d blob proofs compared to %d blob hashes", len(sidecar.Proofs), len(hashes))
   152  	}
   153  	// Blob quantities match up, validate that the provers match with the
   154  	// transaction hash before getting to the cryptography
   155  	hasher := sha256.New()
   156  	for i, vhash := range hashes {
   157  		computed := kzg4844.CalcBlobHashV1(hasher, &sidecar.Commitments[i])
   158  		if vhash != computed {
   159  			return fmt.Errorf("blob %d: computed hash %#x mismatches transaction one %#x", i, computed, vhash)
   160  		}
   161  	}
   162  	// Blob commitments match with the hashes in the transaction, verify the
   163  	// blobs themselves via KZG
   164  	for i := range sidecar.Blobs {
   165  		if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
   166  			return fmt.Errorf("invalid blob %d: %v", i, err)
   167  		}
   168  	}
   169  	return nil
   170  }
   171  
   172  // ValidationOptionsWithState define certain differences between stateful transaction
   173  // validation across the different pools without having to duplicate those checks.
   174  type ValidationOptionsWithState struct {
   175  	State *state.StateDB // State database to check nonces and balances against
   176  
   177  	// FirstNonceGap is an optional callback to retrieve the first nonce gap in
   178  	// the list of pooled transactions of a specific account. If this method is
   179  	// set, nonce gaps will be checked and forbidden. If this method is not set,
   180  	// nonce gaps will be ignored and permitted.
   181  	FirstNonceGap func(addr common.Address) uint64
   182  
   183  	// UsedAndLeftSlots is a mandatory callback to retrieve the number of tx slots
   184  	// used and the number still permitted for an account. New transactions will
   185  	// be rejected once the number of remaining slots reaches zero.
   186  	UsedAndLeftSlots func(addr common.Address) (int, int)
   187  
   188  	// ExistingExpenditure is a mandatory callback to retrieve the cumulative
   189  	// cost of the already pooled transactions to check for overdrafts.
   190  	ExistingExpenditure func(addr common.Address) *big.Int
   191  
   192  	// ExistingCost is a mandatory callback to retrieve an already pooled
   193  	// transaction's cost with the given nonce to check for overdrafts.
   194  	ExistingCost func(addr common.Address, nonce uint64) *big.Int
   195  }
   196  
   197  // ValidateTransactionWithState is a helper method to check whether a transaction
   198  // is valid according to the pool's internal state checks (balance, nonce, gaps).
   199  //
   200  // This check is public to allow different transaction pools to check the stateful
   201  // rules without duplicating code and running the risk of missed updates.
   202  func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error {
   203  	// Ensure the transaction adheres to nonce ordering
   204  	from, err := signer.Sender(tx) // already validated (and cached), but cleaner to check
   205  	if err != nil {
   206  		log.Error("Transaction sender recovery failed", "err", err)
   207  		return err
   208  	}
   209  	next := opts.State.GetNonce(from)
   210  	if next > tx.Nonce() {
   211  		return fmt.Errorf("%w: next nonce %v, tx nonce %v", core.ErrNonceTooLow, next, tx.Nonce())
   212  	}
   213  	// Ensure the transaction doesn't produce a nonce gap in pools that do not
   214  	// support arbitrary orderings
   215  	if opts.FirstNonceGap != nil {
   216  		if gap := opts.FirstNonceGap(from); gap < tx.Nonce() {
   217  			return fmt.Errorf("%w: tx nonce %v, gapped nonce %v", core.ErrNonceTooHigh, tx.Nonce(), gap)
   218  		}
   219  	}
   220  	// Ensure the transactor has enough funds to cover the transaction costs
   221  	var (
   222  		balance = opts.State.GetBalance(from).ToBig()
   223  		cost    = tx.Cost()
   224  	)
   225  	if balance.Cmp(cost) < 0 {
   226  		return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance))
   227  	}
   228  	// Ensure the transactor has enough funds to cover for replacements or nonce
   229  	// expansions without overdrafts
   230  	spent := opts.ExistingExpenditure(from)
   231  	if prev := opts.ExistingCost(from, tx.Nonce()); prev != nil {
   232  		bump := new(big.Int).Sub(cost, prev)
   233  		need := new(big.Int).Add(spent, bump)
   234  		if balance.Cmp(need) < 0 {
   235  			return fmt.Errorf("%w: balance %v, queued cost %v, tx bumped %v, overshot %v", core.ErrInsufficientFunds, balance, spent, bump, new(big.Int).Sub(need, balance))
   236  		}
   237  	} else {
   238  		need := new(big.Int).Add(spent, cost)
   239  		if balance.Cmp(need) < 0 {
   240  			return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, balance))
   241  		}
   242  		// Transaction takes a new nonce value out of the pool. Ensure it doesn't
   243  		// overflow the number of permitted transactions from a single account
   244  		// (i.e. max cancellable via out-of-bound transaction).
   245  		if used, left := opts.UsedAndLeftSlots(from); left <= 0 {
   246  			return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used)
   247  		}
   248  	}
   249  	return nil
   250  }