add validates a transaction and inserts it into the non-executable queue for later pending promotion and execution. If the transaction is a replacement for an already pending or queued one, it overwrites the previous and returns this so outer code doesn't uselessly call promote. If a newly added tr
(tx *types.Transaction, local bool)
| 718 | // whitelisted, preventing any associated transaction from being dropped out of |
| 719 | // the pool due to pricing constraints. |
| 720 | func (pool *TxPool) add(tx *types.Transaction, local bool) (bool, error) { |
| 721 | // If IsFifoTxQueue is true and the txpool is full, just ignore the tx. |
| 722 | if pool.config.IsFifoTxQueue && uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { |
| 723 | log.Debug("txpool is full") |
| 724 | return false, fmt.Errorf("txpool is full") |
| 725 | } |
| 726 | |
| 727 | // If the transaction is already known, discard it |
| 728 | hash := tx.Hash() |
| 729 | if pool.all.Get(hash) != nil { |
| 730 | log.Debug("Discarding already known transaction", "hash", hash.Hex()) |
| 731 | return false, fmt.Errorf("known transaction: %x", hash) |
| 732 | } |
| 733 | // If the transaction fails basic validation, discard it |
| 734 | if err := pool.validateTx(tx, local); err != nil { |
| 735 | log.Debug("Discarding invalid transaction", "hash", hash.Hex(), "err", err) |
| 736 | invalidTxCounter.Inc(1) |
| 737 | return false, err |
| 738 | } |
| 739 | // If the transaction pool is full, discard underpriced transactions |
| 740 | log.Debug("txPoolLen", "len", pool.all.Count()) |
| 741 | if uint64(pool.all.Count()) >= pool.config.GlobalSlots+pool.config.GlobalQueue { |
| 742 | // If the new transaction is underpriced, don't accept it |
| 743 | if !local && pool.priced.Underpriced(tx, pool.locals) { |
| 744 | log.Debug("Discarding underpriced transaction", "hash", hash.Hex(), "price", tx.GasPrice()) |
| 745 | underpricedTxCounter.Inc(1) |
| 746 | return false, ErrUnderpriced |
| 747 | } |
| 748 | // New transaction is better than our worse ones, make room for it |
| 749 | discardNumber := pool.all.Count() - int(pool.config.GlobalSlots+pool.config.GlobalQueue-1) |
| 750 | log.Debug("discardNumber", "discardNumber", discardNumber) |
| 751 | drop := pool.priced.Discard(discardNumber, pool.locals) |
| 752 | for _, tx := range drop { |
| 753 | log.Debug("Discarding freshly underpriced transaction", "hash", tx.Hash().Hex(), "price", tx.GasPrice()) |
| 754 | underpricedTxCounter.Inc(1) |
| 755 | pool.removeTx(tx.Hash(), false) |
| 756 | } |
| 757 | } |
| 758 | // If the transaction is replacing an already pending one, do directly |
| 759 | from, _ := types.Sender(pool.signer, tx) // already validated |
| 760 | if list := pool.getPendingTxList(from); list != nil && list.Overlaps(tx) { |
| 761 | // Nonce already pending, check if required price bump is met |
| 762 | inserted, old := list.Add(tx, pool.config.PriceBump) |
| 763 | if !inserted { |
| 764 | pendingDiscardCounter.Inc(1) |
| 765 | return false, ErrReplaceUnderpriced |
| 766 | } |
| 767 | // New transaction is better, replace old one |
| 768 | if old != nil { |
| 769 | pool.all.Remove(old.Hash()) |
| 770 | pool.priced.Removed() |
| 771 | pendingReplaceCounter.Inc(1) |
| 772 | } |
| 773 | pool.all.Add(tx) |
| 774 | pool.priced.Put(tx) |
| 775 | pool.journalTx(from, tx) |
| 776 | |
| 777 | log.Debug("Pooled new executable transaction", "hash", hash.Hex(), "from", from, "to", tx.To()) |