github.com/Oyster-zx/tendermint@v0.34.24-fork/mempool/v1/mempool.go (about) 1 package v1 2 3 import ( 4 "fmt" 5 "runtime" 6 "sort" 7 "sync" 8 "sync/atomic" 9 "time" 10 11 "github.com/creachadair/taskgroup" 12 13 abci "github.com/tendermint/tendermint/abci/types" 14 "github.com/tendermint/tendermint/config" 15 "github.com/tendermint/tendermint/libs/clist" 16 "github.com/tendermint/tendermint/libs/log" 17 "github.com/tendermint/tendermint/mempool" 18 "github.com/tendermint/tendermint/proxy" 19 "github.com/tendermint/tendermint/types" 20 ) 21 22 var _ mempool.Mempool = (*TxMempool)(nil) 23 24 // TxMempoolOption sets an optional parameter on the TxMempool. 25 type TxMempoolOption func(*TxMempool) 26 27 // TxMempool implemements the Mempool interface and allows the application to 28 // set priority values on transactions in the CheckTx response. When selecting 29 // transactions to include in a block, higher-priority transactions are chosen 30 // first. When evicting transactions from the mempool for size constraints, 31 // lower-priority transactions are evicted sooner. 32 // 33 // Within the mempool, transactions are ordered by time of arrival, and are 34 // gossiped to the rest of the network based on that order (gossip order does 35 // not take priority into account). 36 type TxMempool struct { 37 // Immutable fields 38 logger log.Logger 39 config *config.MempoolConfig 40 proxyAppConn proxy.AppConnMempool 41 metrics *mempool.Metrics 42 cache mempool.TxCache // seen transactions 43 44 // Atomically-updated fields 45 txsBytes int64 // atomic: the total size of all transactions in the mempool, in bytes 46 47 // Synchronized fields, protected by mtx. 48 mtx *sync.RWMutex 49 notifiedTxsAvailable bool 50 txsAvailable chan struct{} // one value sent per height when mempool is not empty 51 preCheck mempool.PreCheckFunc 52 postCheck mempool.PostCheckFunc 53 height int64 // the latest height passed to Update 54 55 txs *clist.CList // valid transactions (passed CheckTx) 56 txByKey map[types.TxKey]*clist.CElement 57 txBySender map[string]*clist.CElement // for sender != "" 58 } 59 60 // NewTxMempool constructs a new, empty priority mempool at the specified 61 // initial height and using the given config and options. 62 func NewTxMempool( 63 logger log.Logger, 64 cfg *config.MempoolConfig, 65 proxyAppConn proxy.AppConnMempool, 66 height int64, 67 options ...TxMempoolOption, 68 ) *TxMempool { 69 70 txmp := &TxMempool{ 71 logger: logger, 72 config: cfg, 73 proxyAppConn: proxyAppConn, 74 metrics: mempool.NopMetrics(), 75 cache: mempool.NopTxCache{}, 76 txs: clist.New(), 77 mtx: new(sync.RWMutex), 78 height: height, 79 txByKey: make(map[types.TxKey]*clist.CElement), 80 txBySender: make(map[string]*clist.CElement), 81 } 82 if cfg.CacheSize > 0 { 83 txmp.cache = mempool.NewLRUTxCache(cfg.CacheSize) 84 } 85 86 for _, opt := range options { 87 opt(txmp) 88 } 89 90 return txmp 91 } 92 93 // WithPreCheck sets a filter for the mempool to reject a transaction if f(tx) 94 // returns an error. This is executed before CheckTx. It only applies to the 95 // first created block. After that, Update() overwrites the existing value. 96 func WithPreCheck(f mempool.PreCheckFunc) TxMempoolOption { 97 return func(txmp *TxMempool) { txmp.preCheck = f } 98 } 99 100 // WithPostCheck sets a filter for the mempool to reject a transaction if 101 // f(tx, resp) returns an error. This is executed after CheckTx. It only applies 102 // to the first created block. After that, Update overwrites the existing value. 103 func WithPostCheck(f mempool.PostCheckFunc) TxMempoolOption { 104 return func(txmp *TxMempool) { txmp.postCheck = f } 105 } 106 107 // WithMetrics sets the mempool's metrics collector. 108 func WithMetrics(metrics *mempool.Metrics) TxMempoolOption { 109 return func(txmp *TxMempool) { txmp.metrics = metrics } 110 } 111 112 // Lock obtains a write-lock on the mempool. A caller must be sure to explicitly 113 // release the lock when finished. 114 func (txmp *TxMempool) Lock() { txmp.mtx.Lock() } 115 116 // Unlock releases a write-lock on the mempool. 117 func (txmp *TxMempool) Unlock() { txmp.mtx.Unlock() } 118 119 // Size returns the number of valid transactions in the mempool. It is 120 // thread-safe. 121 func (txmp *TxMempool) Size() int { return txmp.txs.Len() } 122 123 // SizeBytes return the total sum in bytes of all the valid transactions in the 124 // mempool. It is thread-safe. 125 func (txmp *TxMempool) SizeBytes() int64 { return atomic.LoadInt64(&txmp.txsBytes) } 126 127 // FlushAppConn executes FlushSync on the mempool's proxyAppConn. 128 // 129 // The caller must hold an exclusive mempool lock (by calling txmp.Lock) before 130 // calling FlushAppConn. 131 func (txmp *TxMempool) FlushAppConn() error { 132 // N.B.: We have to issue the call outside the lock so that its callback can 133 // fire. It's safe to do this, the flush will block until complete. 134 // 135 // We could just not require the caller to hold the lock at all, but the 136 // semantics of the Mempool interface require the caller to hold it, and we 137 // can't change that without disrupting existing use. 138 txmp.mtx.Unlock() 139 defer txmp.mtx.Lock() 140 141 return txmp.proxyAppConn.FlushSync() 142 } 143 144 // EnableTxsAvailable enables the mempool to trigger events when transactions 145 // are available on a block by block basis. 146 func (txmp *TxMempool) EnableTxsAvailable() { 147 txmp.mtx.Lock() 148 defer txmp.mtx.Unlock() 149 150 txmp.txsAvailable = make(chan struct{}, 1) 151 } 152 153 // TxsAvailable returns a channel which fires once for every height, and only 154 // when transactions are available in the mempool. It is thread-safe. 155 func (txmp *TxMempool) TxsAvailable() <-chan struct{} { return txmp.txsAvailable } 156 157 // CheckTx adds the given transaction to the mempool if it fits and passes the 158 // application's ABCI CheckTx method. 159 // 160 // CheckTx reports an error without adding tx if: 161 // 162 // - The size of tx exceeds the configured maximum transaction size. 163 // - The pre-check hook is defined and reports an error for tx. 164 // - The transaction already exists in the cache. 165 // - The proxy connection to the application fails. 166 // 167 // If tx passes all of the above conditions, it is passed (asynchronously) to 168 // the application's ABCI CheckTx method and this CheckTx method returns nil. 169 // If cb != nil, it is called when the ABCI request completes to report the 170 // application response. 171 // 172 // If the application accepts the transaction and the mempool is full, the 173 // mempool evicts one or more of the lowest-priority transaction whose priority 174 // is (strictly) lower than the priority of tx and whose size together exceeds 175 // the size of tx, and adds tx instead. If no such transactions exist, tx is 176 // discarded. 177 func (txmp *TxMempool) CheckTx(tx types.Tx, cb func(*abci.Response), txInfo mempool.TxInfo) error { 178 179 // During the initial phase of CheckTx, we do not need to modify any state. 180 // A transaction will not actually be added to the mempool until it survives 181 // a call to the ABCI CheckTx method and size constraint checks. 182 height, err := func() (int64, error) { 183 txmp.mtx.RLock() 184 defer txmp.mtx.RUnlock() 185 186 // Reject transactions in excess of the configured maximum transaction size. 187 if len(tx) > txmp.config.MaxTxBytes { 188 return 0, mempool.ErrTxTooLarge{Max: txmp.config.MaxTxBytes, Actual: len(tx)} 189 } 190 191 // If a precheck hook is defined, call it before invoking the application. 192 if txmp.preCheck != nil { 193 if err := txmp.preCheck(tx); err != nil { 194 return 0, mempool.ErrPreCheck{Reason: err} 195 } 196 } 197 198 // Early exit if the proxy connection has an error. 199 if err := txmp.proxyAppConn.Error(); err != nil { 200 return 0, err 201 } 202 203 txKey := tx.Key() 204 205 // Check for the transaction in the cache. 206 if !txmp.cache.Push(tx) { 207 // If the cached transaction is also in the pool, record its sender. 208 if elt, ok := txmp.txByKey[txKey]; ok { 209 w := elt.Value.(*WrappedTx) 210 w.SetPeer(txInfo.SenderID) 211 } 212 return 0, mempool.ErrTxInCache 213 } 214 return txmp.height, nil 215 }() 216 if err != nil { 217 return err 218 } 219 220 // Invoke an ABCI CheckTx for this transaction. 221 rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{Tx: tx}) 222 if err != nil { 223 txmp.cache.Remove(tx) 224 return err 225 } 226 wtx := &WrappedTx{ 227 tx: tx, 228 hash: tx.Key(), 229 timestamp: time.Now().UTC(), 230 height: height, 231 } 232 wtx.SetPeer(txInfo.SenderID) 233 txmp.addNewTransaction(wtx, rsp) 234 if cb != nil { 235 cb(&abci.Response{Value: &abci.Response_CheckTx{CheckTx: rsp}}) 236 } 237 return nil 238 } 239 240 // RemoveTxByKey removes the transaction with the specified key from the 241 // mempool. It reports an error if no such transaction exists. This operation 242 // does not remove the transaction from the cache. 243 func (txmp *TxMempool) RemoveTxByKey(txKey types.TxKey) error { 244 txmp.mtx.Lock() 245 defer txmp.mtx.Unlock() 246 return txmp.removeTxByKey(txKey) 247 } 248 249 // removeTxByKey removes the specified transaction key from the mempool. 250 // The caller must hold txmp.mtx excluxively. 251 func (txmp *TxMempool) removeTxByKey(key types.TxKey) error { 252 if elt, ok := txmp.txByKey[key]; ok { 253 w := elt.Value.(*WrappedTx) 254 delete(txmp.txByKey, key) 255 delete(txmp.txBySender, w.sender) 256 txmp.txs.Remove(elt) 257 elt.DetachPrev() 258 elt.DetachNext() 259 atomic.AddInt64(&txmp.txsBytes, -w.Size()) 260 return nil 261 } 262 return fmt.Errorf("transaction %x not found", key) 263 } 264 265 // removeTxByElement removes the specified transaction element from the mempool. 266 // The caller must hold txmp.mtx exclusively. 267 func (txmp *TxMempool) removeTxByElement(elt *clist.CElement) { 268 w := elt.Value.(*WrappedTx) 269 delete(txmp.txByKey, w.tx.Key()) 270 delete(txmp.txBySender, w.sender) 271 txmp.txs.Remove(elt) 272 elt.DetachPrev() 273 elt.DetachNext() 274 atomic.AddInt64(&txmp.txsBytes, -w.Size()) 275 } 276 277 // Flush purges the contents of the mempool and the cache, leaving both empty. 278 // The current height is not modified by this operation. 279 func (txmp *TxMempool) Flush() { 280 txmp.mtx.Lock() 281 defer txmp.mtx.Unlock() 282 283 // Remove all the transactions in the list explicitly, so that the sizes 284 // and indexes get updated properly. 285 cur := txmp.txs.Front() 286 for cur != nil { 287 next := cur.Next() 288 txmp.removeTxByElement(cur) 289 cur = next 290 } 291 txmp.cache.Reset() 292 } 293 294 // allEntriesSorted returns a slice of all the transactions currently in the 295 // mempool, sorted in nonincreasing order by priority with ties broken by 296 // increasing order of arrival time. 297 func (txmp *TxMempool) allEntriesSorted() []*WrappedTx { 298 txmp.mtx.RLock() 299 defer txmp.mtx.RUnlock() 300 301 all := make([]*WrappedTx, 0, len(txmp.txByKey)) 302 for _, tx := range txmp.txByKey { 303 all = append(all, tx.Value.(*WrappedTx)) 304 } 305 sort.Slice(all, func(i, j int) bool { 306 if all[i].priority == all[j].priority { 307 return all[i].timestamp.Before(all[j].timestamp) 308 } 309 return all[i].priority > all[j].priority // N.B. higher priorities first 310 }) 311 return all 312 } 313 314 // ReapMaxBytesMaxGas returns a slice of valid transactions that fit within the 315 // size and gas constraints. The results are ordered by nonincreasing priority, 316 // with ties broken by increasing order of arrival. Reaping transactions does 317 // not remove them from the mempool. 318 // 319 // If maxBytes < 0, no limit is set on the total size in bytes. 320 // If maxGas < 0, no limit is set on the total gas cost. 321 // 322 // If the mempool is empty or has no transactions fitting within the given 323 // constraints, the result will also be empty. 324 func (txmp *TxMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs { 325 var totalGas, totalBytes int64 326 327 var keep []types.Tx //nolint:prealloc 328 for _, w := range txmp.allEntriesSorted() { 329 // N.B. When computing byte size, we need to include the overhead for 330 // encoding as protobuf to send to the application. 331 totalGas += w.gasWanted 332 totalBytes += types.ComputeProtoSizeForTxs([]types.Tx{w.tx}) 333 if (maxGas >= 0 && totalGas > maxGas) || (maxBytes >= 0 && totalBytes > maxBytes) { 334 break 335 } 336 keep = append(keep, w.tx) 337 } 338 return keep 339 } 340 341 // TxsWaitChan returns a channel that is closed when there is at least one 342 // transaction available to be gossiped. 343 func (txmp *TxMempool) TxsWaitChan() <-chan struct{} { return txmp.txs.WaitChan() } 344 345 // TxsFront returns the frontmost element of the pending transaction list. 346 // It will be nil if the mempool is empty. 347 func (txmp *TxMempool) TxsFront() *clist.CElement { return txmp.txs.Front() } 348 349 // ReapMaxTxs returns up to max transactions from the mempool. The results are 350 // ordered by nonincreasing priority with ties broken by increasing order of 351 // arrival. Reaping transactions does not remove them from the mempool. 352 // 353 // If max < 0, all transactions in the mempool are reaped. 354 // 355 // The result may have fewer than max elements (possibly zero) if the mempool 356 // does not have that many transactions available. 357 func (txmp *TxMempool) ReapMaxTxs(max int) types.Txs { 358 var keep []types.Tx //nolint:prealloc 359 360 for _, w := range txmp.allEntriesSorted() { 361 if max >= 0 && len(keep) >= max { 362 break 363 } 364 keep = append(keep, w.tx) 365 } 366 return keep 367 } 368 369 // Update removes all the given transactions from the mempool and the cache, 370 // and updates the current block height. The blockTxs and deliverTxResponses 371 // must have the same length with each response corresponding to the tx at the 372 // same offset. 373 // 374 // If the configuration enables recheck, Update sends each remaining 375 // transaction after removing blockTxs to the ABCI CheckTx method. Any 376 // transactions marked as invalid during recheck are also removed. 377 // 378 // The caller must hold an exclusive mempool lock (by calling txmp.Lock) before 379 // calling Update. 380 func (txmp *TxMempool) Update( 381 blockHeight int64, 382 blockTxs types.Txs, 383 deliverTxResponses []*abci.ResponseDeliverTx, 384 newPreFn mempool.PreCheckFunc, 385 newPostFn mempool.PostCheckFunc, 386 ) error { 387 // Safety check: Transactions and responses must match in number. 388 if len(blockTxs) != len(deliverTxResponses) { 389 panic(fmt.Sprintf("mempool: got %d transactions but %d DeliverTx responses", 390 len(blockTxs), len(deliverTxResponses))) 391 } 392 393 txmp.height = blockHeight 394 txmp.notifiedTxsAvailable = false 395 396 if newPreFn != nil { 397 txmp.preCheck = newPreFn 398 } 399 if newPostFn != nil { 400 txmp.postCheck = newPostFn 401 } 402 403 for i, tx := range blockTxs { 404 // Add successful committed transactions to the cache (if they are not 405 // already present). Transactions that failed to commit are removed from 406 // the cache unless the operator has explicitly requested we keep them. 407 if deliverTxResponses[i].Code == abci.CodeTypeOK { 408 _ = txmp.cache.Push(tx) 409 } else if !txmp.config.KeepInvalidTxsInCache { 410 txmp.cache.Remove(tx) 411 } 412 413 // Regardless of success, remove the transaction from the mempool. 414 _ = txmp.removeTxByKey(tx.Key()) 415 } 416 417 txmp.purgeExpiredTxs(blockHeight) 418 419 // If there any uncommitted transactions left in the mempool, we either 420 // initiate re-CheckTx per remaining transaction or notify that remaining 421 // transactions are left. 422 size := txmp.Size() 423 txmp.metrics.Size.Set(float64(size)) 424 if size > 0 { 425 if txmp.config.Recheck { 426 txmp.recheckTransactions() 427 } else { 428 txmp.notifyTxsAvailable() 429 } 430 } 431 return nil 432 } 433 434 // addNewTransaction handles the ABCI CheckTx response for the first time a 435 // transaction is added to the mempool. A recheck after a block is committed 436 // goes to handleRecheckResult. 437 // 438 // If either the application rejected the transaction or a post-check hook is 439 // defined and rejects the transaction, it is discarded. 440 // 441 // Otherwise, if the mempool is full, check for lower-priority transactions 442 // that can be evicted to make room for the new one. If no such transactions 443 // exist, this transaction is logged and dropped; otherwise the selected 444 // transactions are evicted. 445 // 446 // Finally, the new transaction is added and size stats updated. 447 func (txmp *TxMempool) addNewTransaction(wtx *WrappedTx, checkTxRes *abci.ResponseCheckTx) { 448 txmp.mtx.Lock() 449 defer txmp.mtx.Unlock() 450 451 var err error 452 if txmp.postCheck != nil { 453 err = txmp.postCheck(wtx.tx, checkTxRes) 454 } 455 456 if err != nil || checkTxRes.Code != abci.CodeTypeOK { 457 txmp.logger.Info( 458 "rejected bad transaction", 459 "priority", wtx.Priority(), 460 "tx", fmt.Sprintf("%X", wtx.tx.Hash()), 461 "peer_id", wtx.peers, 462 "code", checkTxRes.Code, 463 "post_check_err", err, 464 ) 465 466 txmp.metrics.FailedTxs.Add(1) 467 468 // Remove the invalid transaction from the cache, unless the operator has 469 // instructed us to keep invalid transactions. 470 if !txmp.config.KeepInvalidTxsInCache { 471 txmp.cache.Remove(wtx.tx) 472 } 473 474 // If there was a post-check error, record its text in the result for 475 // debugging purposes. 476 if err != nil { 477 checkTxRes.MempoolError = err.Error() 478 } 479 return 480 } 481 482 priority := checkTxRes.Priority 483 sender := checkTxRes.Sender 484 485 // Disallow multiple concurrent transactions from the same sender assigned 486 // by the ABCI application. As a special case, an empty sender is not 487 // restricted. 488 if sender != "" { 489 elt, ok := txmp.txBySender[sender] 490 if ok { 491 w := elt.Value.(*WrappedTx) 492 txmp.logger.Debug( 493 "rejected valid incoming transaction; tx already exists for sender", 494 "tx", fmt.Sprintf("%X", w.tx.Hash()), 495 "sender", sender, 496 ) 497 checkTxRes.MempoolError = 498 fmt.Sprintf("rejected valid incoming transaction; tx already exists for sender %q (%X)", 499 sender, w.tx.Hash()) 500 txmp.metrics.RejectedTxs.Add(1) 501 return 502 } 503 } 504 505 // At this point the application has ruled the transaction valid, but the 506 // mempool might be full. If so, find the lowest-priority items with lower 507 // priority than the application assigned to this new one, and evict as many 508 // of them as necessary to make room for tx. If no such items exist, we 509 // discard tx. 510 511 if err := txmp.canAddTx(wtx); err != nil { 512 var victims []*clist.CElement // eligible transactions for eviction 513 var victimBytes int64 // total size of victims 514 for cur := txmp.txs.Front(); cur != nil; cur = cur.Next() { 515 cw := cur.Value.(*WrappedTx) 516 if cw.priority < priority { 517 victims = append(victims, cur) 518 victimBytes += cw.Size() 519 } 520 } 521 522 // If there are no suitable eviction candidates, or the total size of 523 // those candidates is not enough to make room for the new transaction, 524 // drop the new one. 525 if len(victims) == 0 || victimBytes < wtx.Size() { 526 txmp.cache.Remove(wtx.tx) 527 txmp.logger.Error( 528 "rejected valid incoming transaction; mempool is full", 529 "tx", fmt.Sprintf("%X", wtx.tx.Hash()), 530 "err", err.Error(), 531 ) 532 checkTxRes.MempoolError = 533 fmt.Sprintf("rejected valid incoming transaction; mempool is full (%X)", 534 wtx.tx.Hash()) 535 txmp.metrics.RejectedTxs.Add(1) 536 return 537 } 538 539 txmp.logger.Debug("evicting lower-priority transactions", 540 "new_tx", fmt.Sprintf("%X", wtx.tx.Hash()), 541 "new_priority", priority, 542 ) 543 544 // Sort lowest priority items first so they will be evicted first. Break 545 // ties in favor of newer items (to maintain FIFO semantics in a group). 546 sort.Slice(victims, func(i, j int) bool { 547 iw := victims[i].Value.(*WrappedTx) 548 jw := victims[j].Value.(*WrappedTx) 549 if iw.Priority() == jw.Priority() { 550 return iw.timestamp.After(jw.timestamp) 551 } 552 return iw.Priority() < jw.Priority() 553 }) 554 555 // Evict as many of the victims as necessary to make room. 556 var evictedBytes int64 557 for _, vic := range victims { 558 w := vic.Value.(*WrappedTx) 559 560 txmp.logger.Debug( 561 "evicted valid existing transaction; mempool full", 562 "old_tx", fmt.Sprintf("%X", w.tx.Hash()), 563 "old_priority", w.priority, 564 ) 565 txmp.removeTxByElement(vic) 566 txmp.cache.Remove(w.tx) 567 txmp.metrics.EvictedTxs.Add(1) 568 569 // We may not need to evict all the eligible transactions. Bail out 570 // early if we have made enough room. 571 evictedBytes += w.Size() 572 if evictedBytes >= wtx.Size() { 573 break 574 } 575 } 576 } 577 578 wtx.SetGasWanted(checkTxRes.GasWanted) 579 wtx.SetPriority(priority) 580 wtx.SetSender(sender) 581 txmp.insertTx(wtx) 582 583 txmp.metrics.TxSizeBytes.Observe(float64(wtx.Size())) 584 txmp.metrics.Size.Set(float64(txmp.Size())) 585 txmp.logger.Debug( 586 "inserted new valid transaction", 587 "priority", wtx.Priority(), 588 "tx", fmt.Sprintf("%X", wtx.tx.Hash()), 589 "height", txmp.height, 590 "num_txs", txmp.Size(), 591 ) 592 txmp.notifyTxsAvailable() 593 } 594 595 func (txmp *TxMempool) insertTx(wtx *WrappedTx) { 596 elt := txmp.txs.PushBack(wtx) 597 txmp.txByKey[wtx.tx.Key()] = elt 598 if s := wtx.Sender(); s != "" { 599 txmp.txBySender[s] = elt 600 } 601 602 atomic.AddInt64(&txmp.txsBytes, wtx.Size()) 603 } 604 605 // handleRecheckResult handles the responses from ABCI CheckTx calls issued 606 // during the recheck phase of a block Update. It removes any transactions 607 // invalidated by the application. 608 // 609 // This method is NOT executed for the initial CheckTx on a new transaction; 610 // that case is handled by addNewTransaction instead. 611 func (txmp *TxMempool) handleRecheckResult(tx types.Tx, checkTxRes *abci.ResponseCheckTx) { 612 txmp.metrics.RecheckTimes.Add(1) 613 txmp.mtx.Lock() 614 defer txmp.mtx.Unlock() 615 616 // Find the transaction reported by the ABCI callback. It is possible the 617 // transaction was evicted during the recheck, in which case the transaction 618 // will be gone. 619 elt, ok := txmp.txByKey[tx.Key()] 620 if !ok { 621 return 622 } 623 wtx := elt.Value.(*WrappedTx) 624 625 // If a postcheck hook is defined, call it before checking the result. 626 var err error 627 if txmp.postCheck != nil { 628 err = txmp.postCheck(tx, checkTxRes) 629 } 630 631 if checkTxRes.Code == abci.CodeTypeOK && err == nil { 632 wtx.SetPriority(checkTxRes.Priority) 633 return // N.B. Size of mempool did not change 634 } 635 636 txmp.logger.Debug( 637 "existing transaction no longer valid; failed re-CheckTx callback", 638 "priority", wtx.Priority(), 639 "tx", fmt.Sprintf("%X", wtx.tx.Hash()), 640 "err", err, 641 "code", checkTxRes.Code, 642 ) 643 txmp.removeTxByElement(elt) 644 txmp.metrics.FailedTxs.Add(1) 645 if !txmp.config.KeepInvalidTxsInCache { 646 txmp.cache.Remove(wtx.tx) 647 } 648 txmp.metrics.Size.Set(float64(txmp.Size())) 649 } 650 651 // recheckTransactions initiates re-CheckTx ABCI calls for all the transactions 652 // currently in the mempool. It reports the number of recheck calls that were 653 // successfully initiated. 654 // 655 // Precondition: The mempool is not empty. 656 // The caller must hold txmp.mtx exclusively. 657 func (txmp *TxMempool) recheckTransactions() { 658 if txmp.Size() == 0 { 659 panic("mempool: cannot run recheck on an empty mempool") 660 } 661 txmp.logger.Debug( 662 "executing re-CheckTx for all remaining transactions", 663 "num_txs", txmp.Size(), 664 "height", txmp.height, 665 ) 666 667 // Collect transactions currently in the mempool requiring recheck. 668 wtxs := make([]*WrappedTx, 0, txmp.txs.Len()) 669 for e := txmp.txs.Front(); e != nil; e = e.Next() { 670 wtxs = append(wtxs, e.Value.(*WrappedTx)) 671 } 672 673 // Issue CheckTx calls for each remaining transaction, and when all the 674 // rechecks are complete signal watchers that transactions may be available. 675 go func() { 676 g, start := taskgroup.New(nil).Limit(2 * runtime.NumCPU()) 677 678 for _, wtx := range wtxs { 679 wtx := wtx 680 start(func() error { 681 // The response for this CheckTx is handled by the default recheckTxCallback. 682 rsp, err := txmp.proxyAppConn.CheckTxSync(abci.RequestCheckTx{ 683 Tx: wtx.tx, 684 Type: abci.CheckTxType_Recheck, 685 }) 686 if err != nil { 687 txmp.logger.Error("failed to execute CheckTx during recheck", 688 "err", err, "hash", fmt.Sprintf("%x", wtx.tx.Hash())) 689 } else { 690 txmp.handleRecheckResult(wtx.tx, rsp) 691 } 692 return nil 693 }) 694 } 695 _ = txmp.proxyAppConn.FlushAsync() 696 697 // When recheck is complete, trigger a notification for more transactions. 698 _ = g.Wait() 699 txmp.mtx.Lock() 700 defer txmp.mtx.Unlock() 701 txmp.notifyTxsAvailable() 702 }() 703 } 704 705 // canAddTx returns an error if we cannot insert the provided *WrappedTx into 706 // the mempool due to mempool configured constraints. Otherwise, nil is 707 // returned and the transaction can be inserted into the mempool. 708 func (txmp *TxMempool) canAddTx(wtx *WrappedTx) error { 709 numTxs := txmp.Size() 710 txBytes := txmp.SizeBytes() 711 712 if numTxs >= txmp.config.Size || wtx.Size()+txBytes > txmp.config.MaxTxsBytes { 713 return mempool.ErrMempoolIsFull{ 714 NumTxs: numTxs, 715 MaxTxs: txmp.config.Size, 716 TxsBytes: txBytes, 717 MaxTxsBytes: txmp.config.MaxTxsBytes, 718 } 719 } 720 721 return nil 722 } 723 724 // purgeExpiredTxs removes all transactions from the mempool that have exceeded 725 // their respective height or time-based limits as of the given blockHeight. 726 // Transactions removed by this operation are not removed from the cache. 727 // 728 // The caller must hold txmp.mtx exclusively. 729 func (txmp *TxMempool) purgeExpiredTxs(blockHeight int64) { 730 if txmp.config.TTLNumBlocks == 0 && txmp.config.TTLDuration == 0 { 731 return // nothing to do 732 } 733 734 now := time.Now() 735 cur := txmp.txs.Front() 736 for cur != nil { 737 // N.B. Grab the next element first, since if we remove cur its successor 738 // will be invalidated. 739 next := cur.Next() 740 741 w := cur.Value.(*WrappedTx) 742 if txmp.config.TTLNumBlocks > 0 && (blockHeight-w.height) > txmp.config.TTLNumBlocks { 743 txmp.removeTxByElement(cur) 744 txmp.cache.Remove(w.tx) 745 txmp.metrics.EvictedTxs.Add(1) 746 } else if txmp.config.TTLDuration > 0 && now.Sub(w.timestamp) > txmp.config.TTLDuration { 747 txmp.removeTxByElement(cur) 748 txmp.cache.Remove(w.tx) 749 txmp.metrics.EvictedTxs.Add(1) 750 } 751 cur = next 752 } 753 } 754 755 func (txmp *TxMempool) notifyTxsAvailable() { 756 if txmp.Size() == 0 { 757 return // nothing to do 758 } 759 760 if txmp.txsAvailable != nil && !txmp.notifiedTxsAvailable { 761 // channel cap is 1, so this will send once 762 txmp.notifiedTxsAvailable = true 763 764 select { 765 case txmp.txsAvailable <- struct{}{}: 766 default: 767 } 768 } 769 }