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 }