9 "github.com/golang/groupcache/lru"
11 "github.com/bytom/blockchain/txdb/storage"
12 "github.com/bytom/consensus"
13 "github.com/bytom/protocol/bc"
14 "github.com/bytom/protocol/bc/legacy"
15 "github.com/bytom/protocol/state"
16 log "github.com/sirupsen/logrus"
20 maxCachedErrTxs = 1000
23 // ErrTransactionNotExist is the pre-defined error message
24 ErrTransactionNotExist = errors.New("transaction are not existed in the mempool")
25 ErrPoolIsFull = errors.New("transaction pool reach the max number")
28 // TxDesc store tx and related info for mining strategy
38 // TxPool is use for store the unconfirmed transaction
42 pool map[bc.Hash]*TxDesc
43 utxo map[bc.Hash]bc.Hash
45 newTxCh chan *legacy.Tx
48 // NewTxPool init a new TxPool
49 func NewTxPool() *TxPool {
51 lastUpdated: time.Now().Unix(),
52 pool: make(map[bc.Hash]*TxDesc),
53 utxo: make(map[bc.Hash]bc.Hash),
54 errCache: lru.New(maxCachedErrTxs),
55 newTxCh: make(chan *legacy.Tx, maxNewTxChSize),
59 // GetNewTxCh return a unconfirmed transaction feed channel
60 func (mp *TxPool) GetNewTxCh() chan *legacy.Tx {
64 // AddTransaction add a verified transaction to pool
65 func (mp *TxPool) AddTransaction(tx *legacy.Tx, gasOnlyTx bool, height, fee uint64) (*TxDesc, error) {
69 if len(mp.pool) >= maxNewTxNum {
70 return nil, ErrPoolIsFull
76 Weight: tx.TxData.SerializedSize,
79 FeePerKB: fee * 1000 / tx.TxHeader.SerializedSize,
82 mp.pool[tx.Tx.ID] = txD
83 atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix())
85 for _, id := range tx.TxHeader.ResultIds {
86 output, err := tx.Output(*id)
88 // error due to it's a retirement, utxo doesn't care this output type so skip it
91 if !gasOnlyTx || *output.Source.Value.AssetId == *consensus.BTMAssetID {
92 mp.utxo[*id] = tx.Tx.ID
97 log.WithField("tx_id", tx.Tx.ID).Info("Add tx to mempool")
101 // AddErrCache add a failed transaction record to lru cache
102 func (mp *TxPool) AddErrCache(txHash *bc.Hash, err error) {
104 defer mp.mtx.Unlock()
106 mp.errCache.Add(txHash, err)
109 // GetErrCache return the error of the transaction
110 func (mp *TxPool) GetErrCache(txHash *bc.Hash) error {
112 defer mp.mtx.Unlock()
114 v, ok := mp.errCache.Get(txHash)
121 // RemoveTransaction remove a transaction from the pool
122 func (mp *TxPool) RemoveTransaction(txHash *bc.Hash) {
124 defer mp.mtx.Unlock()
126 txD, ok := mp.pool[*txHash]
131 for _, output := range txD.Tx.TxHeader.ResultIds {
132 delete(mp.utxo, *output)
134 delete(mp.pool, *txHash)
135 atomic.StoreInt64(&mp.lastUpdated, time.Now().Unix())
137 log.WithField("tx_id", txHash).Info("remove tx from mempool")
140 // GetTransaction return the TxDesc by hash
141 func (mp *TxPool) GetTransaction(txHash *bc.Hash) (*TxDesc, error) {
143 defer mp.mtx.RUnlock()
145 if txD, ok := mp.pool[*txHash]; ok {
149 return nil, ErrTransactionNotExist
152 // GetTransactions return all the transactions in the pool
153 func (mp *TxPool) GetTransactions() []*TxDesc {
155 defer mp.mtx.RUnlock()
157 txDs := make([]*TxDesc, len(mp.pool))
159 for _, desc := range mp.pool {
166 // GetTransactionUTXO return unconfirmed utxo
167 func (mp *TxPool) GetTransactionUTXO(tx *bc.Tx) *state.UtxoViewpoint {
169 defer mp.mtx.RUnlock()
171 view := state.NewUtxoViewpoint()
172 for _, prevout := range tx.SpentOutputIDs {
173 if _, ok := mp.utxo[prevout]; ok {
174 view.Entries[prevout] = storage.NewUtxoEntry(false, 0, false)
180 // IsTransactionInPool check wheather a transaction in pool or not
181 func (mp *TxPool) IsTransactionInPool(txHash *bc.Hash) bool {
183 defer mp.mtx.RUnlock()
185 if _, ok := mp.pool[*txHash]; ok {
191 // IsTransactionInErrCache check wheather a transaction in errCache or not
192 func (mp *TxPool) IsTransactionInErrCache(txHash *bc.Hash) bool {
194 defer mp.mtx.RUnlock()
196 _, ok := mp.errCache.Get(txHash)
200 // HaveTransaction IsTransactionInErrCache check is transaction in errCache or pool
201 func (mp *TxPool) HaveTransaction(txHash *bc.Hash) bool {
202 return mp.IsTransactionInPool(txHash) || mp.IsTransactionInErrCache(txHash)
205 // Count return number of transcation in pool
206 func (mp *TxPool) Count() int {
208 defer mp.mtx.RUnlock()
210 count := len(mp.pool)