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