github.com/ethereum/go-ethereum@v1.16.1/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 "errors" 21 "fmt" 22 "math/big" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/consensus/misc/eip4844" 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 MaxBlobCount int // Maximum number of blobs allowed per transaction 48 MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool 49 } 50 51 // ValidationFunction is an method type which the pools use to perform the tx-validations which do not 52 // require state access. Production code typically uses ValidateTransaction, whereas testing-code 53 // might choose to instead use something else, e.g. to always fail or avoid heavy cpu usage. 54 type ValidationFunction func(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error 55 56 // ValidateTransaction is a helper method to check whether a transaction is valid 57 // according to the consensus rules, but does not check state-dependent validation 58 // (balance, nonce, etc). 59 // 60 // This check is public to allow different transaction pools to check the basic 61 // rules without duplicating code and running the risk of missed updates. 62 func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error { 63 // Ensure transactions not implemented by the calling pool are rejected 64 if opts.Accept&(1<<tx.Type()) == 0 { 65 return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type()) 66 } 67 if blobCount := len(tx.BlobHashes()); blobCount > opts.MaxBlobCount { 68 return fmt.Errorf("%w: blob count %v, limit %v", ErrTxBlobLimitExceeded, blobCount, opts.MaxBlobCount) 69 } 70 // Before performing any expensive validations, sanity check that the tx is 71 // smaller than the maximum limit the pool can meaningfully handle 72 if tx.Size() > opts.MaxSize { 73 return fmt.Errorf("%w: transaction size %v, limit %v", ErrOversizedData, tx.Size(), opts.MaxSize) 74 } 75 // Ensure only transactions that have been enabled are accepted 76 rules := opts.Config.Rules(head.Number, head.Difficulty.Sign() == 0, head.Time) 77 if !rules.IsBerlin && tx.Type() != types.LegacyTxType { 78 return fmt.Errorf("%w: type %d rejected, pool not yet in Berlin", core.ErrTxTypeNotSupported, tx.Type()) 79 } 80 if !rules.IsLondon && tx.Type() == types.DynamicFeeTxType { 81 return fmt.Errorf("%w: type %d rejected, pool not yet in London", core.ErrTxTypeNotSupported, tx.Type()) 82 } 83 if !rules.IsCancun && tx.Type() == types.BlobTxType { 84 return fmt.Errorf("%w: type %d rejected, pool not yet in Cancun", core.ErrTxTypeNotSupported, tx.Type()) 85 } 86 if !rules.IsPrague && tx.Type() == types.SetCodeTxType { 87 return fmt.Errorf("%w: type %d rejected, pool not yet in Prague", core.ErrTxTypeNotSupported, tx.Type()) 88 } 89 // Check whether the init code size has been exceeded 90 if rules.IsShanghai && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { 91 return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) 92 } 93 // Transactions can't be negative. This may never happen using RLP decoded 94 // transactions but may occur for transactions created using the RPC. 95 if tx.Value().Sign() < 0 { 96 return ErrNegativeValue 97 } 98 // Ensure the transaction doesn't exceed the current block limit gas 99 if head.GasLimit < tx.Gas() { 100 return ErrGasLimit 101 } 102 // Sanity check for extremely large numbers (supported by RLP or RPC) 103 if tx.GasFeeCap().BitLen() > 256 { 104 return core.ErrFeeCapVeryHigh 105 } 106 if tx.GasTipCap().BitLen() > 256 { 107 return core.ErrTipVeryHigh 108 } 109 // Ensure gasFeeCap is greater than or equal to gasTipCap 110 if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 { 111 return core.ErrTipAboveFeeCap 112 } 113 // Make sure the transaction is signed properly 114 if _, err := types.Sender(signer, tx); err != nil { 115 return fmt.Errorf("%w: %v", ErrInvalidSender, err) 116 } 117 // Ensure the transaction has more gas than the bare minimum needed to cover 118 // the transaction metadata 119 intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsIstanbul, rules.IsShanghai) 120 if err != nil { 121 return err 122 } 123 if tx.Gas() < intrGas { 124 return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrIntrinsicGas, tx.Gas(), intrGas) 125 } 126 // Ensure the transaction can cover floor data gas. 127 if opts.Config.IsPrague(head.Number, head.Time) { 128 floorDataGas, err := core.FloorDataGas(tx.Data()) 129 if err != nil { 130 return err 131 } 132 if tx.Gas() < floorDataGas { 133 return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas) 134 } 135 } 136 // Ensure the gasprice is high enough to cover the requirement of the calling pool 137 if tx.GasTipCapIntCmp(opts.MinTip) < 0 { 138 return fmt.Errorf("%w: gas tip cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.GasTipCap(), opts.MinTip) 139 } 140 if tx.Type() == types.BlobTxType { 141 return validateBlobTx(tx, head, opts) 142 } 143 if tx.Type() == types.SetCodeTxType { 144 if len(tx.SetCodeAuthorizations()) == 0 { 145 return fmt.Errorf("set code tx must have at least one authorization tuple") 146 } 147 } 148 return nil 149 } 150 151 // validateBlobTx implements the blob-transaction specific validations. 152 func validateBlobTx(tx *types.Transaction, head *types.Header, opts *ValidationOptions) error { 153 sidecar := tx.BlobTxSidecar() 154 if sidecar == nil { 155 return errors.New("missing sidecar in blob transaction") 156 } 157 // Ensure the blob fee cap satisfies the minimum blob gas price 158 if tx.BlobGasFeeCapIntCmp(blobTxMinBlobGasPrice) < 0 { 159 return fmt.Errorf("%w: blob fee cap %v, minimum needed %v", ErrTxGasPriceTooLow, tx.BlobGasFeeCap(), blobTxMinBlobGasPrice) 160 } 161 // Ensure the number of items in the blob transaction and various side 162 // data match up before doing any expensive validations 163 hashes := tx.BlobHashes() 164 if len(hashes) == 0 { 165 return errors.New("blobless blob transaction") 166 } 167 maxBlobs := eip4844.MaxBlobsPerBlock(opts.Config, head.Time) 168 if len(hashes) > maxBlobs { 169 return fmt.Errorf("too many blobs in transaction: have %d, permitted %d", len(hashes), maxBlobs) 170 } 171 if len(sidecar.Blobs) != len(hashes) { 172 return fmt.Errorf("invalid number of %d blobs compared to %d blob hashes", len(sidecar.Blobs), len(hashes)) 173 } 174 if err := sidecar.ValidateBlobCommitmentHashes(hashes); err != nil { 175 return err 176 } 177 // Fork-specific sidecar checks, including proof verification. 178 if opts.Config.IsOsaka(head.Number, head.Time) { 179 return validateBlobSidecarOsaka(sidecar, hashes) 180 } 181 return validateBlobSidecarLegacy(sidecar, hashes) 182 } 183 184 func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { 185 if sidecar.Version != 0 { 186 return fmt.Errorf("invalid sidecar version pre-osaka: %v", sidecar.Version) 187 } 188 if len(sidecar.Proofs) != len(hashes) { 189 return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)) 190 } 191 for i := range sidecar.Blobs { 192 if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil { 193 return fmt.Errorf("invalid blob %d: %v", i, err) 194 } 195 } 196 return nil 197 } 198 199 func validateBlobSidecarOsaka(sidecar *types.BlobTxSidecar, hashes []common.Hash) error { 200 if sidecar.Version != 1 { 201 return fmt.Errorf("invalid sidecar version post-osaka: %v", sidecar.Version) 202 } 203 if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob { 204 return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob) 205 } 206 return kzg4844.VerifyCellProofs(sidecar.Blobs, sidecar.Commitments, sidecar.Proofs) 207 } 208 209 // ValidationOptionsWithState define certain differences between stateful transaction 210 // validation across the different pools without having to duplicate those checks. 211 type ValidationOptionsWithState struct { 212 State *state.StateDB // State database to check nonces and balances against 213 214 // FirstNonceGap is an optional callback to retrieve the first nonce gap in 215 // the list of pooled transactions of a specific account. If this method is 216 // set, nonce gaps will be checked and forbidden. If this method is not set, 217 // nonce gaps will be ignored and permitted. 218 FirstNonceGap func(addr common.Address) uint64 219 220 // UsedAndLeftSlots is an optional callback to retrieve the number of tx slots 221 // used and the number still permitted for an account. New transactions will 222 // be rejected once the number of remaining slots reaches zero. 223 UsedAndLeftSlots func(addr common.Address) (int, int) 224 225 // ExistingExpenditure is a mandatory callback to retrieve the cumulative 226 // cost of the already pooled transactions to check for overdrafts. 227 ExistingExpenditure func(addr common.Address) *big.Int 228 229 // ExistingCost is a mandatory callback to retrieve an already pooled 230 // transaction's cost with the given nonce to check for overdrafts. 231 ExistingCost func(addr common.Address, nonce uint64) *big.Int 232 } 233 234 // ValidateTransactionWithState is a helper method to check whether a transaction 235 // is valid according to the pool's internal state checks (balance, nonce, gaps). 236 // 237 // This check is public to allow different transaction pools to check the stateful 238 // rules without duplicating code and running the risk of missed updates. 239 func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error { 240 // Ensure the transaction adheres to nonce ordering 241 from, err := types.Sender(signer, tx) // already validated (and cached), but cleaner to check 242 if err != nil { 243 log.Error("Transaction sender recovery failed", "err", err) 244 return err 245 } 246 next := opts.State.GetNonce(from) 247 if next > tx.Nonce() { 248 return fmt.Errorf("%w: next nonce %v, tx nonce %v", core.ErrNonceTooLow, next, tx.Nonce()) 249 } 250 // Ensure the transaction doesn't produce a nonce gap in pools that do not 251 // support arbitrary orderings 252 if opts.FirstNonceGap != nil { 253 if gap := opts.FirstNonceGap(from); gap < tx.Nonce() { 254 return fmt.Errorf("%w: tx nonce %v, gapped nonce %v", core.ErrNonceTooHigh, tx.Nonce(), gap) 255 } 256 } 257 // Ensure the transactor has enough funds to cover the transaction costs 258 var ( 259 balance = opts.State.GetBalance(from).ToBig() 260 cost = tx.Cost() 261 ) 262 if balance.Cmp(cost) < 0 { 263 return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance)) 264 } 265 // Ensure the transactor has enough funds to cover for replacements or nonce 266 // expansions without overdrafts 267 spent := opts.ExistingExpenditure(from) 268 if prev := opts.ExistingCost(from, tx.Nonce()); prev != nil { 269 bump := new(big.Int).Sub(cost, prev) 270 need := new(big.Int).Add(spent, bump) 271 if balance.Cmp(need) < 0 { 272 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)) 273 } 274 } else { 275 need := new(big.Int).Add(spent, cost) 276 if balance.Cmp(need) < 0 { 277 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)) 278 } 279 // Transaction takes a new nonce value out of the pool. Ensure it doesn't 280 // overflow the number of permitted transactions from a single account 281 // (i.e. max cancellable via out-of-bound transaction). 282 if opts.UsedAndLeftSlots != nil { 283 if used, left := opts.UsedAndLeftSlots(from); left <= 0 { 284 return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used) 285 } 286 } 287 } 288 return nil 289 }