mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-06-19 21:31:37 +00:00
This commit is contained in:
parent
4f89e3fb24
commit
aa8c43caf3
7 changed files with 398 additions and 255 deletions
|
|
@ -1589,9 +1589,6 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
|
|||
log.Info("Using developer account", "address", developer.Address)
|
||||
|
||||
cfg.Genesis = core.DeveloperGenesisBlock(uint64(ctx.Int(DeveloperPeriodFlag.Name)), developer.Address)
|
||||
if !ctx.IsSet(MinerGasPriceFlag.Name) {
|
||||
cfg.GasPrice = big.NewInt(1)
|
||||
}
|
||||
}
|
||||
|
||||
// VM tracing config.
|
||||
|
|
|
|||
|
|
@ -93,10 +93,6 @@ var (
|
|||
// transaction. Future transactions should only be able to replace other future transactions.
|
||||
ErrFutureReplacePending = errors.New("future transaction tries to replace pending")
|
||||
|
||||
// ErrOverdraft is returned if a transaction would cause the sender's balance to go negative
|
||||
// thus invalidating a potential large number of transactions.
|
||||
ErrOverdraft = errors.New("transaction would cause overdraft")
|
||||
|
||||
ErrZeroGasPrice = errors.New("zero gas price")
|
||||
|
||||
ErrUnderMinGasPrice = errors.New("under min gas price")
|
||||
|
|
@ -198,8 +194,7 @@ type Config struct {
|
|||
Lifetime time.Duration // Maximum amount of time non-executable transaction are queued
|
||||
}
|
||||
|
||||
// DefaultConfig contains the default configurations for the transaction
|
||||
// pool.
|
||||
// DefaultConfig contains the default configurations for the transaction pool.
|
||||
var DefaultConfig = Config{
|
||||
Journal: "transactions.rlp",
|
||||
Rejournal: time.Hour,
|
||||
|
|
@ -215,7 +210,7 @@ var DefaultConfig = Config{
|
|||
Lifetime: 3 * time.Hour,
|
||||
}
|
||||
|
||||
var defaultMaxPrice = big.NewInt(1000 * params.GWei)
|
||||
var defaultMaxTip = big.NewInt(1000 * params.GWei)
|
||||
|
||||
// sanitize checks the provided user configurations and changes anything that's
|
||||
// unreasonable or unworkable.
|
||||
|
|
@ -263,18 +258,15 @@ type TxPool struct {
|
|||
config Config
|
||||
chainconfig *params.ChainConfig
|
||||
chain blockChain
|
||||
gasPrice *big.Int
|
||||
gasTip atomic.Pointer[big.Int]
|
||||
txFeed event.Feed
|
||||
scope event.SubscriptionScope
|
||||
signer types.Signer
|
||||
mu sync.RWMutex
|
||||
|
||||
eip2718 atomic.Bool // Fork indicator whether we are using EIP-2718 type transactions.
|
||||
eip1559 atomic.Bool // Fork indicator whether we are using EIP-1559 type transactions.
|
||||
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingNonces *noncer // Pending state tracking virtual nonces
|
||||
currentMaxGas atomic.Uint64 // Current gas limit for transaction caps
|
||||
currentHead atomic.Pointer[types.Header] // Current head of the blockchain
|
||||
currentState *state.StateDB // Current state in the blockchain head
|
||||
pendingNonces *noncer // Pending state tracking virtual nonces
|
||||
|
||||
locals *accountSet // Set of local transaction to exempt from eviction rules
|
||||
journal *journal // Journal of local transaction to back up to disk
|
||||
|
|
@ -305,9 +297,9 @@ type txpoolResetRequest struct {
|
|||
oldHead, newHead *types.Header
|
||||
}
|
||||
|
||||
// NewTxPool creates a new transaction pool to gather, sort and filter inbound
|
||||
// New creates a new transaction pool to gather, sort and filter inbound
|
||||
// transactions from the network.
|
||||
func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
|
||||
func New(config Config, chainconfig *params.ChainConfig, chain blockChain) *TxPool {
|
||||
// Sanitize the input to ensure no vulnerable gas prices are set
|
||||
config = (&config).sanitize()
|
||||
|
||||
|
|
@ -328,9 +320,9 @@ func NewTxPool(config Config, chainconfig *params.ChainConfig, chain blockChain)
|
|||
reorgDoneCh: make(chan chan struct{}),
|
||||
reorgShutdownCh: make(chan struct{}),
|
||||
initDoneCh: make(chan struct{}),
|
||||
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
|
||||
trc21FeeCapacity: map[common.Address]*big.Int{},
|
||||
}
|
||||
pool.gasTip.Store(new(big.Int).SetUint64(config.PriceLimit))
|
||||
pool.locals = newAccountSet(pool.signer)
|
||||
for _, addr := range config.Locals {
|
||||
log.Info("Setting new local account", "address", addr)
|
||||
|
|
@ -459,48 +451,40 @@ func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subsc
|
|||
return pool.scope.Track(pool.txFeed.Subscribe(ch))
|
||||
}
|
||||
|
||||
// GasPrice returns the current gas price enforced by the transaction pool.
|
||||
func (pool *TxPool) GasPrice() *big.Int {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
return new(big.Int).Set(pool.gasPrice)
|
||||
}
|
||||
|
||||
// SetGasPrice updates the minimum price required by the transaction pool for a
|
||||
// SetGasTip updates the minimum gas tip required by the transaction pool for a
|
||||
// new transaction, and drops all transactions below this threshold. Negative
|
||||
// gas prices and prices exceeding 1000 GWei are considered invalid and will be
|
||||
// rejected without updating the threshold.
|
||||
func (pool *TxPool) SetGasPrice(price *big.Int) error {
|
||||
func (pool *TxPool) SetGasTip(tip *big.Int) error {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
if price == nil {
|
||||
log.Warn("Reject nil gas price")
|
||||
return errors.New("reject nil gas price")
|
||||
if tip == nil {
|
||||
log.Warn("Reject nil gas tip")
|
||||
return errors.New("reject nil gas tip")
|
||||
}
|
||||
if price.Sign() < 0 {
|
||||
log.Warn("Reject invalid gas price", "price", price)
|
||||
return fmt.Errorf("reject negative gas price: %v", price)
|
||||
if tip.Sign() < 0 {
|
||||
log.Warn("Reject invalid gas tip", "tip", tip)
|
||||
return fmt.Errorf("reject negative gas tip: %v", tip)
|
||||
}
|
||||
if price.Cmp(defaultMaxPrice) > 0 {
|
||||
log.Warn("Reject invalid gas price", "price", price, "max", defaultMaxPrice)
|
||||
return fmt.Errorf("reject too high gas price: %v, maximum: %v", price, defaultMaxPrice)
|
||||
if tip.Cmp(defaultMaxTip) > 0 {
|
||||
log.Warn("Reject invalid gas tip", "tip", tip, "max", defaultMaxTip)
|
||||
return fmt.Errorf("reject too high gas tip: %v, maximum: %v", tip, defaultMaxTip)
|
||||
}
|
||||
|
||||
old := pool.gasPrice
|
||||
pool.gasPrice = price
|
||||
// if the min miner fee increased, remove transactions below the new threshold
|
||||
if price.Cmp(old) > 0 {
|
||||
old := pool.gasTip.Load()
|
||||
pool.gasTip.Store(new(big.Int).Set(tip))
|
||||
|
||||
// If the min miner fee increased, remove transactions below the new threshold
|
||||
if tip.Cmp(old) > 0 {
|
||||
// pool.priced is sorted by GasFeeCap, so we have to iterate through pool.all instead
|
||||
drop := pool.all.RemotesBelowTip(price)
|
||||
drop := pool.all.RemotesBelowTip(tip)
|
||||
for _, tx := range drop {
|
||||
pool.removeTx(tx.Hash(), false)
|
||||
}
|
||||
pool.priced.Removed(len(drop))
|
||||
}
|
||||
|
||||
log.Info("Transaction pool price threshold updated", "price", price)
|
||||
log.Info("Transaction pool tip threshold updated", "tip", tip)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -588,7 +572,7 @@ func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transacti
|
|||
// If the miner requests tip enforcement, cap the lists now
|
||||
if enforceTips && !pool.locals.contains(addr) {
|
||||
for i, tx := range txs {
|
||||
if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(pool.gasPrice, pool.priced.urgent.baseFee) < 0 {
|
||||
if !tx.IsSpecialTransaction() && tx.EffectiveGasTipIntCmp(pool.gasTip.Load(), pool.priced.urgent.baseFee) < 0 {
|
||||
txs = txs[:i]
|
||||
break
|
||||
}
|
||||
|
|
@ -630,155 +614,79 @@ func (pool *TxPool) local() map[common.Address]types.Transactions {
|
|||
// This check is meant as an early check which only needs to be performed once,
|
||||
// and does not require the pool mutex to be held.
|
||||
func (pool *TxPool) validateTxBasics(tx *types.Transaction, local bool) error {
|
||||
// Accept only legacy transactions until EIP-2718/2930 activates.
|
||||
if !pool.eip2718.Load() && tx.Type() != types.LegacyTxType {
|
||||
return core.ErrTxTypeNotSupported
|
||||
opts := &ValidationOptions{
|
||||
Config: pool.chainconfig,
|
||||
Accept: 0 |
|
||||
1<<types.LegacyTxType |
|
||||
1<<types.AccessListTxType |
|
||||
1<<types.DynamicFeeTxType,
|
||||
MaxSize: txMaxSize,
|
||||
MinTip: pool.gasTip.Load(),
|
||||
NotSigner: func(from common.Address) bool {
|
||||
return pool.IsSigner != nil && !pool.IsSigner(from)
|
||||
},
|
||||
}
|
||||
// Reject dynamic fee transactions until EIP-1559 activates.
|
||||
if !pool.eip1559.Load() && tx.Type() == types.DynamicFeeTxType {
|
||||
return core.ErrTxTypeNotSupported
|
||||
if local {
|
||||
opts.MinTip = new(big.Int)
|
||||
}
|
||||
// Reject transactions over defined size to prevent DOS attacks
|
||||
if tx.Size() > txMaxSize {
|
||||
return ErrOversizedData
|
||||
}
|
||||
// Check whether the init code size has been exceeded.
|
||||
if pool.eip1559.Load() && 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 if you create a transaction using the RPC.
|
||||
if tx.Value().Sign() < 0 {
|
||||
return ErrNegativeValue
|
||||
}
|
||||
// Ensure the transaction doesn't exceed the current block limit gas.
|
||||
if pool.currentMaxGas.Load() < tx.Gas() {
|
||||
return ErrGasLimit
|
||||
}
|
||||
// Sanity check for extremely large numbers
|
||||
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(pool.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
|
||||
}
|
||||
// Drop non-local transactions under our own minimal accepted gas price or tip
|
||||
if !local && tx.GasTipCapIntCmp(pool.gasPrice) < 0 {
|
||||
if !tx.IsSpecialTransaction() || (pool.IsSigner != nil && !pool.IsSigner(from)) {
|
||||
return ErrUnderpriced
|
||||
}
|
||||
}
|
||||
// Stop checking for special transactions
|
||||
if tx.IsSpecialTransaction() {
|
||||
return nil
|
||||
}
|
||||
// Ensure the transaction has more gas than the basic tx fee.
|
||||
intrGas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.SetCodeAuthorizations(), tx.To() == nil, true, pool.eip1559.Load())
|
||||
if err != nil {
|
||||
if err := ValidateTransaction(tx, pool.currentHead.Load(), pool.signer, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas {
|
||||
return core.ErrIntrinsicGas
|
||||
}
|
||||
// Check zero gas price.
|
||||
if tx.GasPrice().Sign() == 0 {
|
||||
return ErrZeroGasPrice
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateTx checks whether a transaction is valid according to the consensus
|
||||
// rules and adheres to some heuristic limits of the local node (price and size).
|
||||
func (pool *TxPool) validateTx(tx *types.Transaction, local bool) error {
|
||||
// Signature has been checked already, this cannot error.
|
||||
from, _ := types.Sender(pool.signer, tx)
|
||||
// Ensure the transaction adheres to nonce ordering
|
||||
if pool.currentState.GetNonce(from) > tx.Nonce() {
|
||||
return core.ErrNonceTooLow
|
||||
}
|
||||
if pool.pendingNonces.get(from)+common.LimitThresholdNonceInQueue < tx.Nonce() {
|
||||
return core.ErrNonceTooHigh
|
||||
}
|
||||
// Get current block number
|
||||
var number *big.Int = nil
|
||||
if pool.chain.CurrentHeader() != nil {
|
||||
number = pool.chain.CurrentHeader().Number
|
||||
}
|
||||
if number == nil || number.Uint64() >= common.BlackListHFNumber {
|
||||
// check if sender is in black list
|
||||
if common.IsInBlacklist(tx.From()) {
|
||||
return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
|
||||
}
|
||||
// check if receiver is in black list
|
||||
if common.IsInBlacklist(tx.To()) {
|
||||
return fmt.Errorf("reject transaction with receiver in black-list: %v", tx.To().Hex())
|
||||
}
|
||||
}
|
||||
// Transactor should have enough funds to cover the costs
|
||||
// cost == V + GP * GL
|
||||
balance := pool.currentState.GetBalance(from)
|
||||
cost := tx.Cost()
|
||||
feeCapacity := big.NewInt(0)
|
||||
if tx.To() != nil {
|
||||
if value, ok := pool.trc21FeeCapacity[*tx.To()]; ok {
|
||||
feeCapacity = value
|
||||
if !pool.currentState.ValidateTRC21Tx(from, *tx.To(), tx.Data()) {
|
||||
return core.ErrInsufficientFunds
|
||||
opts := &ValidationOptionsWithState{
|
||||
State: pool.currentState,
|
||||
|
||||
FirstNonceGap: nil, // Pool allows arbitrary arrival order, don't invalidate nonce gaps
|
||||
ExistingExpenditure: func(addr common.Address) *big.Int {
|
||||
if list := pool.pending[addr]; list != nil {
|
||||
return list.totalcost
|
||||
}
|
||||
cost = tx.TxCost(number)
|
||||
}
|
||||
return new(big.Int)
|
||||
},
|
||||
ExistingCost: func(addr common.Address, nonce uint64) *big.Int {
|
||||
if list := pool.pending[addr]; list != nil {
|
||||
if tx := list.txs.Get(nonce); tx != nil {
|
||||
return tx.Cost()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
Trc21FeeCapacity: pool.trc21FeeCapacity,
|
||||
|
||||
PendingNonce: func(addr common.Address) uint64 {
|
||||
return pool.pendingNonces.get(addr)
|
||||
},
|
||||
|
||||
CurrentNumber: func() *big.Int {
|
||||
header := pool.currentHead.Load()
|
||||
if header == nil {
|
||||
return nil
|
||||
}
|
||||
return header.Number
|
||||
},
|
||||
}
|
||||
newBalance := new(big.Int).Add(balance, feeCapacity)
|
||||
if newBalance.Cmp(cost) < 0 {
|
||||
return core.ErrInsufficientFunds
|
||||
if err := ValidateTransactionWithState(tx, pool.signer, opts); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Verify that replacing transactions will not result in overdraft
|
||||
list := pool.pending[from]
|
||||
if list != nil { // Sender already has pending txs
|
||||
sum := new(big.Int).Add(cost, list.totalcost)
|
||||
if repl := list.txs.Get(tx.Nonce()); repl != nil {
|
||||
// Deduct the cost of a transaction replaced by this
|
||||
sum.Sub(sum, repl.Cost())
|
||||
}
|
||||
if newBalance.Cmp(sum) < 0 {
|
||||
log.Trace("Replacing transactions would overdraft", "sender", from, "balance", pool.currentState.GetBalance(from), "required", sum)
|
||||
return ErrOverdraft
|
||||
}
|
||||
}
|
||||
|
||||
if !tx.IsSpecialTransaction() {
|
||||
// under min gas price
|
||||
minGasPrice := common.GetMinGasPrice(number)
|
||||
if tx.GasPrice().Cmp(minGasPrice) < 0 {
|
||||
return ErrUnderMinGasPrice
|
||||
}
|
||||
}
|
||||
|
||||
// validate minFee slot for XDCZ
|
||||
// Validate minFee slot for XDCZ
|
||||
if tx.IsXDCZApplyTransaction() {
|
||||
copyState := pool.currentState.Copy()
|
||||
copyState := opts.State.Copy()
|
||||
return core.ValidateXDCZApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
}
|
||||
|
||||
// validate balance slot, token decimal for XDCX
|
||||
// Validate balance slot, token decimal for XDCX
|
||||
if tx.IsXDCXApplyTransaction() {
|
||||
copyState := pool.currentState.Copy()
|
||||
copyState := opts.State.Copy()
|
||||
return core.ValidateXDCXApplyTransaction(pool.chain, nil, copyState, common.BytesToAddress(tx.Data()[4:]))
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1129,7 +1037,6 @@ func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
|
|||
// Exclude transactions with basic errors, e.g invalid signatures and
|
||||
// insufficient intrinsic gas as soon as possible and cache senders
|
||||
// in transactions before obtaining lock
|
||||
|
||||
if err := pool.validateTxBasics(tx, local); err != nil {
|
||||
errs[i] = err
|
||||
invalidTxMeter.Mark(1)
|
||||
|
|
@ -1517,20 +1424,15 @@ func (pool *TxPool) reset(oldHead, newHead *types.Header) {
|
|||
log.Error("Failed to reset txpool state", "err", err)
|
||||
return
|
||||
}
|
||||
pool.currentHead.Store(newHead)
|
||||
pool.currentState = statedb
|
||||
pool.trc21FeeCapacity = statedb.GetTRC21FeeCapacityFromStateWithCache(newHead.Root)
|
||||
pool.pendingNonces = newNoncer(statedb)
|
||||
pool.currentMaxGas.Store(newHead.GasLimit)
|
||||
|
||||
// Inject any transactions discarded due to reorgs
|
||||
log.Debug("Reinjecting stale transactions", "count", len(reinject))
|
||||
core.SenderCacher.Recover(pool.signer, reinject)
|
||||
pool.addTxsLocked(reinject, false)
|
||||
|
||||
// Update all fork indicator by next pending block number.
|
||||
next := new(big.Int).Add(newHead.Number, big.NewInt(1))
|
||||
pool.eip2718.Store(pool.chainconfig.IsEIP1559(next))
|
||||
pool.eip1559.Store(pool.chainconfig.IsEIP1559(next))
|
||||
}
|
||||
|
||||
// promoteExecutables moves transactions that have become processable from the
|
||||
|
|
@ -1546,6 +1448,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
|||
var promoted []*types.Transaction
|
||||
|
||||
// Iterate over all accounts and promote any executable transactions
|
||||
gasLimit := pool.currentHead.Load().GasLimit
|
||||
for _, addr := range accounts {
|
||||
list := pool.queue[addr]
|
||||
if list == nil {
|
||||
|
|
@ -1563,7 +1466,7 @@ func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Trans
|
|||
if pool.chain.CurrentHeader() != nil {
|
||||
number = pool.chain.CurrentHeader().Number
|
||||
}
|
||||
drops, _ := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas.Load(), pool.trc21FeeCapacity, number)
|
||||
drops, _ := list.Filter(pool.currentState.GetBalance(addr), gasLimit, pool.trc21FeeCapacity, number)
|
||||
for _, tx := range drops {
|
||||
hash := tx.Hash()
|
||||
pool.all.Remove(hash)
|
||||
|
|
@ -1749,6 +1652,7 @@ func (pool *TxPool) truncateQueue() {
|
|||
// to trigger a re-heap is this function
|
||||
func (pool *TxPool) demoteUnexecutables() {
|
||||
// Iterate over all accounts and demote any non-executable transactions
|
||||
gasLimit := pool.currentHead.Load().GasLimit
|
||||
for addr, list := range pool.pending {
|
||||
nonce := pool.currentState.GetNonce(addr)
|
||||
|
||||
|
|
@ -1764,7 +1668,7 @@ func (pool *TxPool) demoteUnexecutables() {
|
|||
if pool.chain.CurrentHeader() != nil {
|
||||
number = pool.chain.CurrentHeader().Number
|
||||
}
|
||||
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), pool.currentMaxGas.Load(), pool.trc21FeeCapacity, number)
|
||||
drops, invalids := list.Filter(pool.currentState.GetBalance(addr), gasLimit, pool.trc21FeeCapacity, number)
|
||||
for _, tx := range drops {
|
||||
hash := tx.Hash()
|
||||
log.Trace("Removed unpayable pending transaction", "hash", hash)
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ func TestTransactionFutureAttack(t *testing.T) {
|
|||
config := testTxPoolConfig
|
||||
config.GlobalQueue = 100
|
||||
config.GlobalSlots = 100
|
||||
pool := NewTxPool(config, eip1559Config, blockchain)
|
||||
pool := New(config, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
fillPool(t, pool)
|
||||
pending, _ := pool.Stats()
|
||||
|
|
@ -117,7 +117,7 @@ func TestTransactionFuture1559(t *testing.T) {
|
|||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
|
||||
pool := New(testTxPoolConfig, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts, fund them and make transactions
|
||||
|
|
@ -149,7 +149,7 @@ func TestTransactionZAttack(t *testing.T) {
|
|||
// Create the pool to test the pricing enforcement with
|
||||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
|
||||
pool := New(testTxPoolConfig, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
// Create a number of test accounts, fund them and make transactions
|
||||
fillPool(t, pool)
|
||||
|
|
@ -222,7 +222,7 @@ func BenchmarkFutureAttack(b *testing.B) {
|
|||
config := testTxPoolConfig
|
||||
config.GlobalQueue = 100
|
||||
config.GlobalSlots = 100
|
||||
pool := NewTxPool(config, eip1559Config, blockchain)
|
||||
pool := New(config, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
fillPool(b, pool)
|
||||
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ func setupPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey
|
|||
blockchain := newTestBlockChain(10000000, statedb, new(event.Feed))
|
||||
|
||||
key, _ := crypto.GenerateKey()
|
||||
pool := NewTxPool(testTxPoolConfig, config, blockchain)
|
||||
pool := New(testTxPoolConfig, config, blockchain)
|
||||
|
||||
// wait for the pool to initialize
|
||||
<-pool.initDoneCh
|
||||
|
|
@ -270,7 +270,7 @@ func TestStateChangeDuringReset(t *testing.T) {
|
|||
tx0 := pricedTransaction(0, 100000, big.NewInt(250000000), key)
|
||||
tx1 := pricedTransaction(1, 100000, big.NewInt(250000000), key)
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
nonce := pool.Nonce(address)
|
||||
|
|
@ -335,11 +335,15 @@ func TestInvalidTransactions(t *testing.T) {
|
|||
t.Errorf("want %v have %v", want, err)
|
||||
}
|
||||
|
||||
// Test underpriced: set pool gasTip first, then create transaction with lower gas price
|
||||
// MinGasPrice is 250000000 (0.25 Gwei), so use gas price 300000000 (0.3 Gwei)
|
||||
// which is higher than MinGasPrice but lower than pool's gasTip (1 Gwei)
|
||||
pool.gasTip.Store(big.NewInt(1000000000)) // Set pool gasTip to 1 Gwei (1000000000)
|
||||
tx = pricedTransaction(1, 100000, big.NewInt(300000000), key)
|
||||
pool.gasPrice = big.NewInt(1000000000)
|
||||
if err, want := pool.AddRemote(tx), ErrUnderpriced; !errors.Is(err, want) {
|
||||
t.Errorf("want %v have %v", want, err)
|
||||
}
|
||||
// Local transactions should be accepted even if underpriced
|
||||
if err := pool.AddLocal(tx); err != nil {
|
||||
t.Error("expected", nil, "got", err)
|
||||
}
|
||||
|
|
@ -761,7 +765,7 @@ func TestPostponing(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create two test accounts to produce different gap profiles with
|
||||
|
|
@ -979,7 +983,7 @@ func testQueueGlobalLimiting(t *testing.T, nolocals bool) {
|
|||
config.AccountQueue = 1
|
||||
config.GlobalQueue = config.AccountQueue*3 - 1 // reduce the queue limits to shorten test time (-1 to make it non divisible)
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts and fund them (last one will be the local)
|
||||
|
|
@ -1070,7 +1074,7 @@ func testQueueTimeLimiting(t *testing.T, nolocals bool) {
|
|||
config.Lifetime = time.Second
|
||||
config.NoLocals = nolocals
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create two test accounts to ensure remotes expire but locals do not
|
||||
|
|
@ -1256,7 +1260,7 @@ func TestPendingGlobalLimiting(t *testing.T) {
|
|||
config := testTxPoolConfig
|
||||
config.GlobalSlots = config.AccountSlots * 10
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -1308,7 +1312,7 @@ func TestAllowedTxSize(t *testing.T) {
|
|||
//
|
||||
// It is assumed the fields in the transaction (except of the data) are:
|
||||
// - nonce <= 32 bytes
|
||||
// - gasPrice <= 32 bytes
|
||||
// - gasTip <= 32 bytes
|
||||
// - gasLimit <= 32 bytes
|
||||
// - recipient == 20 bytes
|
||||
// - value <= 32 bytes
|
||||
|
|
@ -1316,22 +1320,21 @@ func TestAllowedTxSize(t *testing.T) {
|
|||
// All those fields are summed up to at most 213 bytes.
|
||||
baseSize := uint64(213)
|
||||
dataSize := txMaxSize - baseSize
|
||||
maxGas := pool.currentMaxGas.Load()
|
||||
// Try adding a transaction with maximal allowed size
|
||||
tx := pricedDataTransaction(0, pool.currentMaxGas.Load(), big.NewInt(300000000), key, dataSize)
|
||||
tx := pricedDataTransaction(0, pool.currentHead.Load().GasLimit, big.NewInt(300000000), key, dataSize)
|
||||
if err := pool.addRemoteSync(tx); err != nil {
|
||||
t.Fatalf("failed to add transaction of size %d, close to maximal: %v", int(tx.Size()), err)
|
||||
}
|
||||
// Try adding a transaction with random allowed size
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(1, maxGas, big.NewInt(300000000), key, uint64(rand.Intn(int(dataSize))))); err != nil {
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(1, pool.currentHead.Load().GasLimit, big.NewInt(300000000), key, uint64(rand.Intn(int(dataSize))))); err != nil {
|
||||
t.Fatalf("failed to add transaction of random allowed size: %v", err)
|
||||
}
|
||||
// Try adding a transaction of minimal not allowed size
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(300000000), key, txMaxSize)); err == nil {
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(2, pool.currentHead.Load().GasLimit, big.NewInt(300000000), key, txMaxSize)); err == nil {
|
||||
t.Fatalf("expected rejection on slightly oversize transaction")
|
||||
}
|
||||
// Try adding a transaction of random not allowed size
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(2, maxGas, big.NewInt(300000000), key, dataSize+1+uint64(rand.Intn(int(10*txMaxSize))))); err == nil {
|
||||
if err := pool.addRemoteSync(pricedDataTransaction(2, pool.currentHead.Load().GasLimit, big.NewInt(300000000), key, dataSize+1+uint64(rand.Intn(int(10*txMaxSize))))); err == nil {
|
||||
t.Fatalf("expected rejection on oversize transaction")
|
||||
}
|
||||
// Run some sanity checks on the pool internals
|
||||
|
|
@ -1361,7 +1364,7 @@ func TestCapClearsFromAll(t *testing.T) {
|
|||
config.AccountQueue = 2
|
||||
config.GlobalSlots = 8
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -1394,7 +1397,7 @@ func TestPendingMinimumAllowance(t *testing.T) {
|
|||
config := testTxPoolConfig
|
||||
config.AccountSlots = 5
|
||||
config.GlobalSlots = 1
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -1440,7 +1443,7 @@ func TestRepricing(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
|
|
@ -1489,7 +1492,7 @@ func TestRepricing(t *testing.T) {
|
|||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Reprice the pool and check that underpriced transactions get dropped
|
||||
pool.SetGasPrice(big.NewInt(500000000))
|
||||
pool.SetGasTip(big.NewInt(500000000))
|
||||
|
||||
pending, queued = pool.Stats()
|
||||
if pending != 2 {
|
||||
|
|
@ -1505,13 +1508,13 @@ func TestRepricing(t *testing.T) {
|
|||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Check that we can't add the old transactions back
|
||||
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(250000000), keys[0])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(1, 100000, big.NewInt(250000000), keys[0])); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(250000000), keys[2])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(2, 100000, big.NewInt(250000000), keys[2])); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := validateEvents(events, 0); err != nil {
|
||||
|
|
@ -1610,7 +1613,7 @@ func TestRepricingDynamicFee(t *testing.T) {
|
|||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Reprice the pool and check that underpriced transactions get dropped
|
||||
pool.SetGasPrice(big.NewInt(350000000))
|
||||
pool.SetGasTip(big.NewInt(350000000))
|
||||
|
||||
pending, queued = pool.Stats()
|
||||
if pending != 2 {
|
||||
|
|
@ -1627,15 +1630,15 @@ func TestRepricingDynamicFee(t *testing.T) {
|
|||
}
|
||||
// Check that we can't add the old transactions back
|
||||
tx := pricedTransaction(1, 100000, big.NewInt(300000000), keys[0])
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(tx); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
tx = dynamicFeeTx(0, 100000, big.NewInt(350000000), big.NewInt(300000000), keys[1])
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(tx); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
tx = dynamicFeeTx(2, 100000, big.NewInt(300000000), big.NewInt(300000000), keys[2])
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(tx); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced queued transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
if err := validateEvents(events, 0); err != nil {
|
||||
|
|
@ -1689,7 +1692,7 @@ func TestRepricingKeepsLocals(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
|
||||
pool := New(testTxPoolConfig, eip1559Config, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
|
|
@ -1741,13 +1744,13 @@ func TestRepricingKeepsLocals(t *testing.T) {
|
|||
validate()
|
||||
|
||||
// Reprice the pool and check that nothing is dropped
|
||||
pool.SetGasPrice(big.NewInt(500000000))
|
||||
pool.SetGasTip(big.NewInt(500000000))
|
||||
validate()
|
||||
|
||||
pool.SetGasPrice(big.NewInt(500000000))
|
||||
pool.SetGasPrice(big.NewInt(1000000000))
|
||||
pool.SetGasPrice(big.NewInt(2000000000))
|
||||
pool.SetGasPrice(big.NewInt(25000000000))
|
||||
pool.SetGasTip(big.NewInt(500000000))
|
||||
pool.SetGasTip(big.NewInt(1000000000))
|
||||
pool.SetGasTip(big.NewInt(2000000000))
|
||||
pool.SetGasTip(big.NewInt(25000000000))
|
||||
validate()
|
||||
}
|
||||
|
||||
|
|
@ -1768,7 +1771,7 @@ func TestPoolUnderpricing(t *testing.T) {
|
|||
config.GlobalSlots = 2
|
||||
config.GlobalQueue = 2
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
|
|
@ -1813,7 +1816,7 @@ func TestPoolUnderpricing(t *testing.T) {
|
|||
t.Fatalf("pool internal state corrupted: %v", err)
|
||||
}
|
||||
// Ensure that adding an underpriced transaction on block limit fails
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); err != ErrUnderpriced {
|
||||
if err := pool.AddRemote(pricedTransaction(0, 100000, big.NewInt(250000000), keys[1])); !errors.Is(err, ErrUnderpriced) {
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
// Replace a future transaction with a future transaction
|
||||
|
|
@ -1887,7 +1890,7 @@ func TestPoolStableUnderpricing(t *testing.T) {
|
|||
config.GlobalQueue = 0
|
||||
config.AccountSlots = config.GlobalSlots - 1
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
|
|
@ -1995,7 +1998,7 @@ func TestUnderpricingDynamicFee(t *testing.T) {
|
|||
|
||||
// Ensure that adding an underpriced transaction fails
|
||||
tx := dynamicFeeTx(0, 100000, big.NewInt(260000000), big.NewInt(250000000), keys[1])
|
||||
if err := pool.AddRemote(tx); err != ErrUnderpriced { // Pend K0:0, K0:1, K2:0; Que K1:1
|
||||
if err := pool.AddRemote(tx); !errors.Is(err, ErrUnderpriced) { // Pend K0:0, K0:1, K2:0; Que K1:1
|
||||
t.Fatalf("adding underpriced pending transaction error mismatch: have %v, want %v", err, ErrUnderpriced)
|
||||
}
|
||||
|
||||
|
|
@ -2115,7 +2118,7 @@ func TestDeduplication(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(rawdb.NewMemoryDatabase()))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create a test account to add transactions with
|
||||
|
|
@ -2182,7 +2185,7 @@ func TestReplacement(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Keep track of transaction events to ensure all executables get announced
|
||||
|
|
@ -2394,7 +2397,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
|||
config.Journal = journal
|
||||
config.Rejournal = time.Second
|
||||
|
||||
pool := NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool := New(config, params.TestChainConfig, blockchain)
|
||||
|
||||
// Create two test accounts to ensure remotes expire but locals do not
|
||||
local, _ := crypto.GenerateKey()
|
||||
|
|
@ -2431,7 +2434,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
|||
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
|
||||
blockchain = newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool = NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool = New(config, params.TestChainConfig, blockchain)
|
||||
|
||||
pending, queued = pool.Stats()
|
||||
if queued != 0 {
|
||||
|
|
@ -2457,7 +2460,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
|
|||
|
||||
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
|
||||
blockchain = newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
pool = NewTxPool(config, params.TestChainConfig, blockchain)
|
||||
pool = New(config, params.TestChainConfig, blockchain)
|
||||
|
||||
pending, queued = pool.Stats()
|
||||
if pending != 0 {
|
||||
|
|
@ -2488,7 +2491,7 @@ func TestStatusCheck(t *testing.T) {
|
|||
statedb, _ := state.New(types.EmptyRootHash, state.NewDatabase(db))
|
||||
blockchain := newTestBlockChain(1000000, statedb, new(event.Feed))
|
||||
|
||||
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
pool := New(testTxPoolConfig, params.TestChainConfig, blockchain)
|
||||
defer pool.Stop()
|
||||
|
||||
// Create the test accounts to check various transaction statuses with
|
||||
|
|
@ -2698,75 +2701,75 @@ func BenchmarkMultiAccountBatchInsert(b *testing.B) {
|
|||
func TestSetGasPrice(t *testing.T) {
|
||||
testCases := []struct {
|
||||
name string
|
||||
price *big.Int
|
||||
tip *big.Int
|
||||
wantErr error
|
||||
description string
|
||||
}{
|
||||
// Invalid cases - should be rejected
|
||||
{
|
||||
name: "nil gas price",
|
||||
price: nil,
|
||||
wantErr: errors.New("reject nil gas price"),
|
||||
name: "nil gas tip",
|
||||
tip: nil,
|
||||
wantErr: errors.New("reject nil gas tip"),
|
||||
description: "nil pointer should be rejected gracefully",
|
||||
},
|
||||
{
|
||||
name: "negative gas price",
|
||||
price: big.NewInt(-1),
|
||||
wantErr: fmt.Errorf("reject negative gas price: %v", big.NewInt(-1)),
|
||||
name: "negative gas tip",
|
||||
tip: big.NewInt(-1),
|
||||
wantErr: fmt.Errorf("reject negative gas tip: %v", big.NewInt(-1)),
|
||||
description: "negative value should be rejected",
|
||||
},
|
||||
{
|
||||
name: "exceeds maximum by 1",
|
||||
price: new(big.Int).Add(defaultMaxPrice, big.NewInt(1)),
|
||||
wantErr: fmt.Errorf("reject too high gas price: %v, maximum: %v", new(big.Int).Add(defaultMaxPrice, big.NewInt(1)), defaultMaxPrice),
|
||||
tip: new(big.Int).Add(defaultMaxTip, big.NewInt(1)),
|
||||
wantErr: fmt.Errorf("reject too high gas tip: %v, maximum: %v", new(big.Int).Add(defaultMaxTip, big.NewInt(1)), defaultMaxTip),
|
||||
description: "value exceeding 1000 GWei should be rejected",
|
||||
},
|
||||
{
|
||||
name: "exceeds maximum significantly",
|
||||
price: big.NewInt(10000 * params.GWei),
|
||||
wantErr: fmt.Errorf("reject too high gas price: %v, maximum: %v", big.NewInt(10000*params.GWei), defaultMaxPrice),
|
||||
tip: big.NewInt(10000 * params.GWei),
|
||||
wantErr: fmt.Errorf("reject too high gas tip: %v, maximum: %v", big.NewInt(10000*params.GWei), defaultMaxTip),
|
||||
description: "value far exceeding maximum should be rejected",
|
||||
},
|
||||
// Valid cases - should be accepted
|
||||
{
|
||||
name: "zero gas price",
|
||||
price: big.NewInt(0),
|
||||
name: "zero gas tip",
|
||||
tip: big.NewInt(0),
|
||||
wantErr: nil,
|
||||
description: "zero is valid as it's not negative",
|
||||
},
|
||||
{
|
||||
name: "minimum positive value",
|
||||
price: big.NewInt(1),
|
||||
tip: big.NewInt(1),
|
||||
wantErr: nil,
|
||||
description: "minimum positive value should be accepted",
|
||||
},
|
||||
{
|
||||
name: "1 GWei",
|
||||
price: big.NewInt(params.GWei),
|
||||
tip: big.NewInt(params.GWei),
|
||||
wantErr: nil,
|
||||
description: "1 GWei should be accepted",
|
||||
},
|
||||
{
|
||||
name: "100 GWei",
|
||||
price: big.NewInt(100 * params.GWei),
|
||||
tip: big.NewInt(100 * params.GWei),
|
||||
wantErr: nil,
|
||||
description: "100 GWei should be accepted",
|
||||
},
|
||||
{
|
||||
name: "500 GWei",
|
||||
price: big.NewInt(500 * params.GWei),
|
||||
tip: big.NewInt(500 * params.GWei),
|
||||
wantErr: nil,
|
||||
description: "500 GWei should be accepted",
|
||||
},
|
||||
{
|
||||
name: "just below maximum",
|
||||
price: new(big.Int).Sub(defaultMaxPrice, big.NewInt(1)),
|
||||
tip: new(big.Int).Sub(defaultMaxTip, big.NewInt(1)),
|
||||
wantErr: nil,
|
||||
description: "value just below maximum should be accepted",
|
||||
},
|
||||
{
|
||||
name: "exactly at maximum",
|
||||
price: defaultMaxPrice,
|
||||
tip: defaultMaxTip,
|
||||
wantErr: nil,
|
||||
description: "exactly 1000 GWei should be accepted",
|
||||
},
|
||||
|
|
@ -2778,9 +2781,9 @@ func TestSetGasPrice(t *testing.T) {
|
|||
pool, _ := setupPool()
|
||||
defer pool.Stop()
|
||||
|
||||
oldPrice := pool.GasPrice()
|
||||
haveErr := pool.SetGasPrice(tc.price)
|
||||
newPrice := pool.GasPrice()
|
||||
oldPrice := pool.gasTip.Load()
|
||||
haveErr := pool.SetGasTip(tc.tip)
|
||||
newPrice := pool.gasTip.Load()
|
||||
|
||||
if tc.wantErr != nil {
|
||||
// Invalid case: should return error and price should remain unchanged
|
||||
|
|
@ -2797,8 +2800,8 @@ func TestSetGasPrice(t *testing.T) {
|
|||
if haveErr != nil {
|
||||
t.Errorf("%s: Expected no error, got: %v", tc.description, haveErr)
|
||||
}
|
||||
if newPrice.Cmp(tc.price) != 0 {
|
||||
t.Errorf("%s: Expected gas price to be set to %v, got %v", tc.description, tc.price, newPrice)
|
||||
if newPrice.Cmp(tc.tip) != 0 {
|
||||
t.Errorf("%s: Expected gas price to be set to %v, got %v", tc.description, tc.tip, newPrice)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
|
|
|||
238
core/txpool/validation.go
Normal file
238
core/txpool/validation.go
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
// 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<<tx.Type()) == 0 {
|
||||
return fmt.Errorf("%w: tx type %v not supported by this pool", core.ErrTxTypeNotSupported, tx.Type())
|
||||
}
|
||||
// Before performing any expensive validations, sanity check that the tx is
|
||||
// smaller than the maximum limit the pool can meaningfully handle
|
||||
if tx.Size() > 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
|
||||
if !opts.Config.IsEIP1559(head.Number) && tx.Type() != types.LegacyTxType {
|
||||
return fmt.Errorf("%w: type %d rejected, pool not yet in EIP1559", core.ErrTxTypeNotSupported, tx.Type())
|
||||
}
|
||||
// Check whether the init code size has been exceeded
|
||||
if opts.Config.IsEIP1559(head.Number) && 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
|
||||
}
|
||||
isSpecial := tx.IsSpecialTransaction()
|
||||
// Ensure the gasprice is high enough to cover the requirement of
|
||||
// the calling pool and/or block producer
|
||||
if tx.GasTipCapIntCmp(opts.MinTip) < 0 {
|
||||
// For special transactions, only check if the sender is not a signer
|
||||
// For regular transactions, always check (to preserve old logic)
|
||||
if !isSpecial || opts.NotSigner(from) {
|
||||
return fmt.Errorf("%w: tip needed %v, tip permitted %v", ErrUnderpriced, opts.MinTip, tx.GasTipCap())
|
||||
}
|
||||
}
|
||||
// Skip further checks for special transactions
|
||||
if isSpecial {
|
||||
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, opts.Config.IsEIP1559(head.Number))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tx.Gas() < intrGas {
|
||||
return fmt.Errorf("%w: needed %v, allowed %v", core.ErrIntrinsicGas, intrGas, tx.Gas())
|
||||
}
|
||||
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
|
||||
|
||||
// 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))
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure sender and receiver are not in black list
|
||||
if number == nil || number.Cmp(new(big.Int).SetUint64(common.BlackListHFNumber)) >= 0 {
|
||||
// check if sender is in black list
|
||||
if common.IsInBlacklist(tx.From()) {
|
||||
return fmt.Errorf("reject transaction with sender in black-list: %v", tx.From().Hex())
|
||||
}
|
||||
// check if receiver is in black list
|
||||
if common.IsInBlacklist(to) {
|
||||
return fmt.Errorf("reject transaction with receiver in black-list: %v", to.Hex())
|
||||
}
|
||||
}
|
||||
|
||||
// Validate gas price
|
||||
if !tx.IsSpecialTransaction() {
|
||||
minGasPrice := common.GetMinGasPrice(number)
|
||||
if tx.GasPrice().Cmp(minGasPrice) < 0 {
|
||||
return ErrUnderMinGasPrice
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
@ -62,10 +62,11 @@ func (api *MinerAPI) Start(threads *int) error {
|
|||
if !api.e.IsStaking() {
|
||||
// Propagate the initial price point to the transaction pool
|
||||
api.e.lock.RLock()
|
||||
// api.e.gasPrice is from MinerGasPriceFlag
|
||||
price := api.e.gasPrice
|
||||
api.e.lock.RUnlock()
|
||||
|
||||
api.e.txPool.SetGasPrice(price)
|
||||
api.e.txPool.SetGasTip(price)
|
||||
return api.e.StartStaking(true)
|
||||
}
|
||||
return nil
|
||||
|
|
@ -97,7 +98,7 @@ func (api *MinerAPI) SetGasPrice(gasPrice hexutil.Big) bool {
|
|||
api.e.gasPrice = (*big.Int)(&gasPrice)
|
||||
api.e.lock.Unlock()
|
||||
|
||||
err := api.e.txPool.SetGasPrice((*big.Int)(&gasPrice))
|
||||
err := api.e.txPool.SetGasTip((*big.Int)(&gasPrice))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -265,7 +265,7 @@ func New(stack *node.Node, config *ethconfig.Config, XDCXServ *XDCx.XDCX, lendin
|
|||
if config.TxPool.Journal != "" {
|
||||
config.TxPool.Journal = stack.ResolvePath(config.TxPool.Journal)
|
||||
}
|
||||
eth.txPool = txpool.NewTxPool(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||
eth.txPool = txpool.New(config.TxPool, eth.chainConfig, eth.blockchain)
|
||||
eth.orderPool = txpool.NewOrderPool(eth.chainConfig, eth.blockchain)
|
||||
eth.lendingPool = txpool.NewLendingPool(eth.chainConfig, eth.blockchain)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue