9 "github.com/golang/groupcache/lru"
11 "github.com/bytom/consensus"
12 "github.com/bytom/database/storage"
13 "github.com/bytom/protocol/bc"
14 "github.com/bytom/protocol/bc/types"
15 "github.com/bytom/protocol/state"
16 log "github.com/sirupsen/logrus"
25 maxCachedErrTxs = 1000
29 // ErrTransactionNotExist is the pre-defined error message
30 ErrTransactionNotExist = errors.New("transaction are not existed in the mempool")
31 // ErrPoolIsFull indicates the pool is full
32 ErrPoolIsFull = errors.New("transaction pool reach the max number")
35 // TxDesc store tx and related info for mining strategy
46 type TxPoolMsg struct {
51 // TxPool is use for store the unconfirmed transaction
55 pool map[bc.Hash]*TxDesc
56 utxo map[bc.Hash]bc.Hash
61 // NewTxPool init a new TxPool
62 func NewTxPool() *TxPool {
64 lastUpdated: time.Now().Unix(),
65 pool: make(map[bc.Hash]*TxDesc),
66 utxo: make(map[bc.Hash]bc.Hash),
67 errCache: lru.New(maxCachedErrTxs),
68 msgCh: make(chan *TxPoolMsg, maxMsgChSize),
72 // GetNewTxCh return a unconfirmed transaction feed channel
73 func (tp *TxPool) GetMsgCh() <-chan *TxPoolMsg {
77 // AddTransaction add a verified transaction to pool
78 func (tp *TxPool) AddTransaction(tx *types.Tx, statusFail bool, height, fee uint64) (*TxDesc, error) {
82 if len(tp.pool) >= maxNewTxNum {
83 return nil, ErrPoolIsFull
89 StatusFail: statusFail,
90 Weight: tx.TxData.SerializedSize,
93 FeePerKB: fee * 1000 / tx.TxHeader.SerializedSize,
96 tp.pool[tx.Tx.ID] = txD
97 atomic.StoreInt64(&tp.lastUpdated, time.Now().Unix())
99 for _, id := range tx.TxHeader.ResultIds {
100 output, err := tx.Output(*id)
102 // error due to it's a retirement, utxo doesn't care this output type so skip it
105 if !statusFail || *output.Source.Value.AssetId == *consensus.BTMAssetID {
106 tp.utxo[*id] = tx.Tx.ID
110 tp.msgCh <- &TxPoolMsg{TxDesc: txD, MsgType: MsgNewTx}
111 log.WithField("tx_id", tx.Tx.ID.String()).Debug("Add tx to mempool")
115 // AddErrCache add a failed transaction record to lru cache
116 func (tp *TxPool) AddErrCache(txHash *bc.Hash, err error) {
118 defer tp.mtx.Unlock()
120 tp.errCache.Add(txHash, err)
123 // GetErrCache return the error of the transaction
124 func (tp *TxPool) GetErrCache(txHash *bc.Hash) error {
126 defer tp.mtx.Unlock()
128 v, ok := tp.errCache.Get(txHash)
135 // RemoveTransaction remove a transaction from the pool
136 func (tp *TxPool) RemoveTransaction(txHash *bc.Hash) {
138 defer tp.mtx.Unlock()
140 txD, ok := tp.pool[*txHash]
145 for _, output := range txD.Tx.TxHeader.ResultIds {
146 delete(tp.utxo, *output)
148 delete(tp.pool, *txHash)
149 atomic.StoreInt64(&tp.lastUpdated, time.Now().Unix())
151 tp.msgCh <- &TxPoolMsg{TxDesc: txD, MsgType: MsgRemoveTx}
152 log.WithField("tx_id", txHash).Debug("remove tx from mempool")
155 // GetTransaction return the TxDesc by hash
156 func (tp *TxPool) GetTransaction(txHash *bc.Hash) (*TxDesc, error) {
158 defer tp.mtx.RUnlock()
160 if txD, ok := tp.pool[*txHash]; ok {
164 return nil, ErrTransactionNotExist
167 // GetTransactions return all the transactions in the pool
168 func (tp *TxPool) GetTransactions() []*TxDesc {
170 defer tp.mtx.RUnlock()
172 txDs := make([]*TxDesc, len(tp.pool))
174 for _, desc := range tp.pool {
181 // GetTransactionUTXO return unconfirmed utxo
182 func (tp *TxPool) GetTransactionUTXO(tx *bc.Tx) *state.UtxoViewpoint {
184 defer tp.mtx.RUnlock()
186 view := state.NewUtxoViewpoint()
187 for _, prevout := range tx.SpentOutputIDs {
188 if _, ok := tp.utxo[prevout]; ok {
189 view.Entries[prevout] = storage.NewUtxoEntry(false, 0, false)
195 // IsTransactionInPool check wheather a transaction in pool or not
196 func (tp *TxPool) IsTransactionInPool(txHash *bc.Hash) bool {
198 defer tp.mtx.RUnlock()
200 if _, ok := tp.pool[*txHash]; ok {
206 // IsTransactionInErrCache check wheather a transaction in errCache or not
207 func (tp *TxPool) IsTransactionInErrCache(txHash *bc.Hash) bool {
209 defer tp.mtx.RUnlock()
211 _, ok := tp.errCache.Get(txHash)
215 // HaveTransaction IsTransactionInErrCache check is transaction in errCache or pool
216 func (tp *TxPool) HaveTransaction(txHash *bc.Hash) bool {
217 return tp.IsTransactionInPool(txHash) || tp.IsTransactionInErrCache(txHash)
220 // Count return number of transcation in pool
221 func (tp *TxPool) Count() int {
223 defer tp.mtx.RUnlock()
225 count := len(tp.pool)