// Copyright 2023 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see . package txpool import ( "fmt" "math/big" "github.com/XinFinOrg/XDPoSChain/common" "github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core/state" "github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/log" "github.com/XinFinOrg/XDPoSChain/params" ) // ValidationOptions define certain differences between transaction validation // across the different pools without having to duplicate those checks. type ValidationOptions struct { Config *params.ChainConfig // Chain configuration to selectively validate based on current fork rules Accept uint8 // Bitmap of transaction types that should be accepted for the calling pool MaxSize uint64 // Maximum size of a transaction that the caller can meaningfully handle MinTip *big.Int // Minimum gas tip needed to allow a transaction into the caller pool NotSigner func(addr common.Address) bool } // ValidateTransaction is a helper method to check whether a transaction is valid // according to the consensus rules, but does not check state-dependent validation // (balance, nonce, etc). // // This check is public to allow different transaction pools to check the basic // rules without duplicating code and running the risk of missed updates. func ValidateTransaction(tx *types.Transaction, head *types.Header, signer types.Signer, opts *ValidationOptions) error { // Ensure transactions not implemented by the calling pool are rejected if opts.Accept&(1< opts.MaxSize { return fmt.Errorf("%w: transaction size %v, limit %v", ErrOversizedData, tx.Size(), opts.MaxSize) } // Ensure only transactions that have been enabled are accepted rules := opts.Config.Rules(head.Number) if !rules.IsEIP1559 && tx.Type() != types.LegacyTxType { return fmt.Errorf("%w: type %d rejected, pool not yet in EIP1559", core.ErrTxTypeNotSupported, tx.Type()) } if !rules.IsPrague && tx.Type() == types.SetCodeTxType { return fmt.Errorf("%w: type %d rejected, pool not yet in Prague", core.ErrTxTypeNotSupported, tx.Type()) } // Check whether the init code size has been exceeded if rules.IsEIP1559 && tx.To() == nil && len(tx.Data()) > params.MaxInitCodeSize { return fmt.Errorf("%w: code size %v, limit %v", core.ErrMaxInitCodeSizeExceeded, len(tx.Data()), params.MaxInitCodeSize) } // Transactions can't be negative. This may never happen using RLP decoded // transactions but may occur for transactions created using the RPC. if tx.Value().Sign() < 0 { return ErrNegativeValue } // Ensure the transaction doesn't exceed the current block limit gas if head.GasLimit < tx.Gas() { return ErrGasLimit } // Sanity check for extremely large numbers (supported by RLP or RPC) if tx.GasFeeCap().BitLen() > 256 { return core.ErrFeeCapVeryHigh } if tx.GasTipCap().BitLen() > 256 { return core.ErrTipVeryHigh } // Ensure gasFeeCap is greater than or equal to gasTipCap if tx.GasFeeCapIntCmp(tx.GasTipCap()) < 0 { return core.ErrTipAboveFeeCap } // Make sure the transaction is signed properly from, err := types.Sender(signer, tx) if err != nil { return ErrInvalidSender } // Limit nonce to 2^64-1 per EIP-2681 if tx.Nonce()+1 < tx.Nonce() { return core.ErrNonceMax } // Skip further validation for special transactions if tx.IsSpecialTransaction() { if opts.NotSigner(from) { return fmt.Errorf("%w: tip needed %v, tip permitted %v", ErrUnderpriced, opts.MinTip, tx.GasTipCap()) } return nil } // Check zero gas price. if tx.GasPrice().Sign() == 0 { return ErrZeroGasPrice } // Ensure the transaction has more gas than the bare minimum needed to // cover the transaction metadata intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, rules.IsEIP1559) if err != nil { return err } if tx.Gas() < intrGas { return fmt.Errorf("%w: needed %v, allowed %v", core.ErrIntrinsicGas, intrGas, tx.Gas()) } // Ensure the transaction can cover floor data gas. if rules.IsPrague { floorDataGas, err := core.FloorDataGas(tx.Data()) if err != nil { return err } if tx.Gas() < floorDataGas { return fmt.Errorf("%w: gas %v, minimum needed %v", core.ErrFloorDataGas, tx.Gas(), floorDataGas) } } // Ensure the gas price is high enough to cover the requirement of the calling pool if tx.GasTipCapIntCmp(opts.MinTip) < 0 { return fmt.Errorf("%w: tip needed %v, tip permitted %v", ErrUnderpriced, opts.MinTip, tx.GasTipCap()) } if tx.Type() == types.SetCodeTxType { if len(tx.SetCodeAuthorizations()) == 0 { return fmt.Errorf("set code tx must have at least one authorization tuple") } } return nil } // ValidationOptionsWithState define certain differences between stateful transaction // validation across the different pools without having to duplicate those checks. type ValidationOptionsWithState struct { State *state.StateDB // State database to check nonces and balances against // FirstNonceGap is an optional callback to retrieve the first nonce gap in // the list of pooled transactions of a specific account. If this method is // set, nonce gaps will be checked and forbidden. If this method is not set, // nonce gaps will be ignored and permitted. FirstNonceGap func(addr common.Address) uint64 // UsedAndLeftSlots is an optional callback to retrieve the number of tx slots // used and the number still permitted for an account. New transactions will // be rejected once the number of remaining slots reaches zero. UsedAndLeftSlots func(addr common.Address) (int, int) // ExistingExpenditure is a mandatory callback to retrieve the cumulative // cost of the already pooled transactions to check for overdrafts. ExistingExpenditure func(addr common.Address) *big.Int // ExistingCost is a mandatory callback to retrieve an already pooled // transaction's cost with the given nonce to check for overdrafts. ExistingCost func(addr common.Address, nonce uint64) *big.Int Trc21FeeCapacity map[common.Address]*big.Int PendingNonce func(addr common.Address) uint64 CurrentNumber func() *big.Int } // ValidateTransactionWithState is a helper method to check whether a transaction // is valid according to the pool's internal state checks (balance, nonce, gaps). // // This check is public to allow different transaction pools to check the stateful // rules without duplicating code and running the risk of missed updates. func ValidateTransactionWithState(tx *types.Transaction, signer types.Signer, opts *ValidationOptionsWithState) error { // Ensure the transaction adheres to nonce ordering from, err := signer.Sender(tx) // already validated (and cached), but cleaner to check if err != nil { log.Error("Transaction sender recovery failed", "err", err) return err } // Ensure the transaction nonce is in a valid range next := opts.State.GetNonce(from) if next > tx.Nonce() { return fmt.Errorf("%w: next nonce %v, tx nonce %v", core.ErrNonceTooLow, next, tx.Nonce()) } if opts.PendingNonce(from)+common.LimitThresholdNonceInQueue < tx.Nonce() { return core.ErrNonceTooHigh } // Ensure the transaction doesn't produce a nonce gap in pools that do not // support arbitrary orderings if opts.FirstNonceGap != nil { if gap := opts.FirstNonceGap(from); gap < tx.Nonce() { return fmt.Errorf("%w: tx nonce %v, gapped nonce %v", core.ErrNonceTooHigh, tx.Nonce(), gap) } } // Ensure the transactor has enough funds to cover the transaction costs var ( balance = opts.State.GetBalance(from) cost = tx.Cost() feeCapacity = big.NewInt(0) number = opts.CurrentNumber() to = tx.To() ) if to != nil { if value, ok := opts.Trc21FeeCapacity[*to]; ok { feeCapacity = value if !opts.State.ValidateTRC21Tx(from, *to, tx.Data()) { return core.ErrInsufficientFunds } cost = tx.TxCost(number) } } newBalance := new(big.Int).Add(balance, feeCapacity) if newBalance.Cmp(cost) < 0 { return fmt.Errorf("%w: balance %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, cost, new(big.Int).Sub(cost, balance)) } // Ensure the transactor has enough funds to cover for replacements or nonce // expansions without overdrafts spent := opts.ExistingExpenditure(from) if prev := opts.ExistingCost(from, tx.Nonce()); prev != nil { bump := new(big.Int).Sub(cost, prev) need := new(big.Int).Add(spent, bump) if newBalance.Cmp(need) < 0 { return fmt.Errorf("%w: balance %v, queued cost %v, tx bumped %v, overshot %v", core.ErrInsufficientFunds, balance, spent, bump, new(big.Int).Sub(need, newBalance)) } } else { need := new(big.Int).Add(spent, cost) if newBalance.Cmp(need) < 0 { return fmt.Errorf("%w: balance %v, queued cost %v, tx cost %v, overshot %v", core.ErrInsufficientFunds, balance, spent, cost, new(big.Int).Sub(need, newBalance)) } // Transaction takes a new nonce value out of the pool. Ensure it doesn't // overflow the number of permitted transactions from a single account // (i.e. max cancellable via out-of-bound transaction). if opts.UsedAndLeftSlots != nil { if used, left := opts.UsedAndLeftSlots(from); left <= 0 { return fmt.Errorf("%w: pooled %d txs", ErrAccountLimitExceeded, used) } } } // Ensure sender and receiver are not in denylist if number == nil || number.Cmp(new(big.Int).SetUint64(common.DenylistHFNumber)) >= 0 { // check if sender is in denylist if common.IsInDenylist(tx.From()) { return fmt.Errorf("reject transaction with sender in denylist: %v", tx.From().Hex()) } // check if receiver is in denylist if common.IsInDenylist(to) { return fmt.Errorf("reject transaction with receiver in denylist: %v", to.Hex()) } } // Validate gas price if !tx.IsSpecialTransaction() { minGasPrice := common.GetMinGasPrice(number) if tx.GasPrice().Cmp(minGasPrice) < 0 { return ErrUnderMinGasPrice } } return nil }