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"
20 maxCachedErrTxs = 1000
24 // ErrTransactionNotExist is the pre-defined error message
25 ErrTransactionNotExist = errors.New("transaction are not existed in the mempool")
26 // ErrPoolIsFull indicates the pool is full
27 ErrPoolIsFull = errors.New("transaction pool reach the max number")
30 // TxDesc store tx and related info for mining strategy
40 // TxPool is use for store the unconfirmed transaction
44 pool map[bc.Hash]*TxDesc
45 utxo map[bc.Hash]bc.Hash
47 newTxCh chan *types.Tx
50 // NewTxPool init a new TxPool
51 func NewTxPool() *TxPool {
53 lastUpdated: time.Now().Unix(),
54 pool: make(map[bc.Hash]*TxDesc),
55 utxo: make(map[bc.Hash]bc.Hash),
56 errCache: lru.New(maxCachedErrTxs),
57 newTxCh: make(chan *types.Tx, maxNewTxChSize),
61 // GetNewTxCh return a unconfirmed transaction feed channel
62 func (tp *TxPool) GetNewTxCh() chan *types.Tx {
66 // AddTransaction add a verified transaction to pool
67 func (tp *TxPool) AddTransaction(tx *types.Tx, gasOnlyTx bool, height, fee uint64) (*TxDesc, error) {
71 if len(tp.pool) >= maxNewTxNum {
72 return nil, ErrPoolIsFull
78 Weight: tx.TxData.SerializedSize,
81 FeePerKB: fee * 1000 / tx.TxHeader.SerializedSize,
84 tp.pool[tx.Tx.ID] = txD
85 atomic.StoreInt64(&tp.lastUpdated, time.Now().Unix())
87 for _, id := range tx.TxHeader.ResultIds {
88 output, err := tx.Output(*id)
90 // error due to it's a retirement, utxo doesn't care this output type so skip it
93 if !gasOnlyTx || *output.Source.Value.AssetId == *consensus.BTMAssetID {
94 tp.utxo[*id] = tx.Tx.ID
99 log.WithField("tx_id", tx.Tx.ID.String()).Debug("Add tx to mempool")
103 // AddErrCache add a failed transaction record to lru cache
104 func (tp *TxPool) AddErrCache(txHash *bc.Hash, err error) {
106 defer tp.mtx.Unlock()
108 tp.errCache.Add(txHash, err)
111 // GetErrCache return the error of the transaction
112 func (tp *TxPool) GetErrCache(txHash *bc.Hash) error {
114 defer tp.mtx.Unlock()
116 v, ok := tp.errCache.Get(txHash)
123 // RemoveTransaction remove a transaction from the pool
124 func (tp *TxPool) RemoveTransaction(txHash *bc.Hash) {
126 defer tp.mtx.Unlock()
128 txD, ok := tp.pool[*txHash]
133 for _, output := range txD.Tx.TxHeader.ResultIds {
134 delete(tp.utxo, *output)
136 delete(tp.pool, *txHash)
137 atomic.StoreInt64(&tp.lastUpdated, time.Now().Unix())
139 log.WithField("tx_id", txHash).Debug("remove tx from mempool")
142 // GetTransaction return the TxDesc by hash
143 func (tp *TxPool) GetTransaction(txHash *bc.Hash) (*TxDesc, error) {
145 defer tp.mtx.RUnlock()
147 if txD, ok := tp.pool[*txHash]; ok {
151 return nil, ErrTransactionNotExist
154 // GetTransactions return all the transactions in the pool
155 func (tp *TxPool) GetTransactions() []*TxDesc {
157 defer tp.mtx.RUnlock()
159 txDs := make([]*TxDesc, len(tp.pool))
161 for _, desc := range tp.pool {
168 // GetTransactionUTXO return unconfirmed utxo
169 func (tp *TxPool) GetTransactionUTXO(tx *bc.Tx) *state.UtxoViewpoint {
171 defer tp.mtx.RUnlock()
173 view := state.NewUtxoViewpoint()
174 for _, prevout := range tx.SpentOutputIDs {
175 if _, ok := tp.utxo[prevout]; ok {
176 view.Entries[prevout] = storage.NewUtxoEntry(false, 0, false)
182 // IsTransactionInPool check wheather a transaction in pool or not
183 func (tp *TxPool) IsTransactionInPool(txHash *bc.Hash) bool {
185 defer tp.mtx.RUnlock()
187 if _, ok := tp.pool[*txHash]; ok {
193 // IsTransactionInErrCache check wheather a transaction in errCache or not
194 func (tp *TxPool) IsTransactionInErrCache(txHash *bc.Hash) bool {
196 defer tp.mtx.RUnlock()
198 _, ok := tp.errCache.Get(txHash)
202 // HaveTransaction IsTransactionInErrCache check is transaction in errCache or pool
203 func (tp *TxPool) HaveTransaction(txHash *bc.Hash) bool {
204 return tp.IsTransactionInPool(txHash) || tp.IsTransactionInErrCache(txHash)
207 // Count return number of transcation in pool
208 func (tp *TxPool) Count() int {
210 defer tp.mtx.RUnlock()
212 count := len(tp.pool)