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