You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

717 lines
20 KiB

  1. package mempool
  2. import (
  3. "bytes"
  4. "container/list"
  5. "crypto/sha256"
  6. "fmt"
  7. "sync"
  8. "sync/atomic"
  9. "time"
  10. "github.com/pkg/errors"
  11. abci "github.com/tendermint/tendermint/abci/types"
  12. cfg "github.com/tendermint/tendermint/config"
  13. auto "github.com/tendermint/tendermint/libs/autofile"
  14. "github.com/tendermint/tendermint/libs/clist"
  15. cmn "github.com/tendermint/tendermint/libs/common"
  16. "github.com/tendermint/tendermint/libs/log"
  17. "github.com/tendermint/tendermint/proxy"
  18. "github.com/tendermint/tendermint/types"
  19. )
  20. //--------------------------------------------------------------------------------
  21. // CListMempool is an ordered in-memory pool for transactions before they are
  22. // proposed in a consensus round. Transaction validity is checked using the
  23. // CheckTx abci message before the transaction is added to the pool. The
  24. // mempool uses a concurrent list structure for storing transactions that can
  25. // be efficiently accessed by multiple concurrent readers.
  26. type CListMempool struct {
  27. config *cfg.MempoolConfig
  28. proxyMtx sync.Mutex
  29. proxyAppConn proxy.AppConnMempool
  30. txs *clist.CList // concurrent linked-list of good txs
  31. preCheck PreCheckFunc
  32. postCheck PostCheckFunc
  33. // Track whether we're rechecking txs.
  34. // These are not protected by a mutex and are expected to be mutated
  35. // in serial (ie. by abci responses which are called in serial).
  36. recheckCursor *clist.CElement // next expected response
  37. recheckEnd *clist.CElement // re-checking stops here
  38. // notify listeners (ie. consensus) when txs are available
  39. notifiedTxsAvailable bool
  40. txsAvailable chan struct{} // fires once for each height, when the mempool is not empty
  41. // Map for quick access to txs to record sender in CheckTx.
  42. // txsMap: txKey -> CElement
  43. txsMap sync.Map
  44. // Atomic integers
  45. height int64 // the last block Update()'d to
  46. rechecking int32 // for re-checking filtered txs on Update()
  47. txsBytes int64 // total size of mempool, in bytes
  48. // Keep a cache of already-seen txs.
  49. // This reduces the pressure on the proxyApp.
  50. cache txCache
  51. // A log of mempool txs
  52. wal *auto.AutoFile
  53. logger log.Logger
  54. metrics *Metrics
  55. }
  56. var _ Mempool = &CListMempool{}
  57. // Option sets an optional parameter on the mempool.
  58. type Option func(*CListMempool)
  59. // NewCListMempool returns a new mempool with the given configuration and connection to an application.
  60. func NewCListMempool(
  61. config *cfg.MempoolConfig,
  62. proxyAppConn proxy.AppConnMempool,
  63. height int64,
  64. options ...Option,
  65. ) *CListMempool {
  66. mempool := &CListMempool{
  67. config: config,
  68. proxyAppConn: proxyAppConn,
  69. txs: clist.New(),
  70. height: height,
  71. rechecking: 0,
  72. recheckCursor: nil,
  73. recheckEnd: nil,
  74. logger: log.NewNopLogger(),
  75. metrics: NopMetrics(),
  76. }
  77. if config.CacheSize > 0 {
  78. mempool.cache = newMapTxCache(config.CacheSize)
  79. } else {
  80. mempool.cache = nopTxCache{}
  81. }
  82. proxyAppConn.SetResponseCallback(mempool.globalCb)
  83. for _, option := range options {
  84. option(mempool)
  85. }
  86. return mempool
  87. }
  88. // NOTE: not thread safe - should only be called once, on startup
  89. func (mem *CListMempool) EnableTxsAvailable() {
  90. mem.txsAvailable = make(chan struct{}, 1)
  91. }
  92. // SetLogger sets the Logger.
  93. func (mem *CListMempool) SetLogger(l log.Logger) {
  94. mem.logger = l
  95. }
  96. // WithPreCheck sets a filter for the mempool to reject a tx if f(tx) returns
  97. // false. This is ran before CheckTx.
  98. func WithPreCheck(f PreCheckFunc) Option {
  99. return func(mem *CListMempool) { mem.preCheck = f }
  100. }
  101. // WithPostCheck sets a filter for the mempool to reject a tx if f(tx) returns
  102. // false. This is ran after CheckTx.
  103. func WithPostCheck(f PostCheckFunc) Option {
  104. return func(mem *CListMempool) { mem.postCheck = f }
  105. }
  106. // WithMetrics sets the metrics.
  107. func WithMetrics(metrics *Metrics) Option {
  108. return func(mem *CListMempool) { mem.metrics = metrics }
  109. }
  110. // InitWAL creates a directory for the WAL file and opens a file itself.
  111. //
  112. // *panics* if can't create directory or open file.
  113. // *not thread safe*
  114. func (mem *CListMempool) InitWAL() {
  115. walDir := mem.config.WalDir()
  116. err := cmn.EnsureDir(walDir, 0700)
  117. if err != nil {
  118. panic(errors.Wrap(err, "Error ensuring WAL dir"))
  119. }
  120. af, err := auto.OpenAutoFile(walDir + "/wal")
  121. if err != nil {
  122. panic(errors.Wrap(err, "Error opening WAL file"))
  123. }
  124. mem.wal = af
  125. }
  126. // CloseWAL closes and discards the underlying WAL file.
  127. // Any further writes will not be relayed to disk.
  128. func (mem *CListMempool) CloseWAL() {
  129. mem.proxyMtx.Lock()
  130. defer mem.proxyMtx.Unlock()
  131. if err := mem.wal.Close(); err != nil {
  132. mem.logger.Error("Error closing WAL", "err", err)
  133. }
  134. mem.wal = nil
  135. }
  136. func (mem *CListMempool) Lock() {
  137. mem.proxyMtx.Lock()
  138. }
  139. func (mem *CListMempool) Unlock() {
  140. mem.proxyMtx.Unlock()
  141. }
  142. func (mem *CListMempool) Size() int {
  143. return mem.txs.Len()
  144. }
  145. func (mem *CListMempool) TxsBytes() int64 {
  146. return atomic.LoadInt64(&mem.txsBytes)
  147. }
  148. func (mem *CListMempool) FlushAppConn() error {
  149. return mem.proxyAppConn.FlushSync()
  150. }
  151. func (mem *CListMempool) Flush() {
  152. mem.proxyMtx.Lock()
  153. defer mem.proxyMtx.Unlock()
  154. mem.cache.Reset()
  155. for e := mem.txs.Front(); e != nil; e = e.Next() {
  156. mem.txs.Remove(e)
  157. e.DetachPrev()
  158. }
  159. mem.txsMap = sync.Map{}
  160. _ = atomic.SwapInt64(&mem.txsBytes, 0)
  161. }
  162. // TxsFront returns the first transaction in the ordered list for peer
  163. // goroutines to call .NextWait() on.
  164. func (mem *CListMempool) TxsFront() *clist.CElement {
  165. return mem.txs.Front()
  166. }
  167. // TxsWaitChan returns a channel to wait on transactions. It will be closed
  168. // once the mempool is not empty (ie. the internal `mem.txs` has at least one
  169. // element)
  170. func (mem *CListMempool) TxsWaitChan() <-chan struct{} {
  171. return mem.txs.WaitChan()
  172. }
  173. // It blocks if we're waiting on Update() or Reap().
  174. // cb: A callback from the CheckTx command.
  175. // It gets called from another goroutine.
  176. // CONTRACT: Either cb will get called, or err returned.
  177. func (mem *CListMempool) CheckTx(tx types.Tx, cb func(*abci.Response)) (err error) {
  178. return mem.CheckTxWithInfo(tx, cb, TxInfo{SenderID: UnknownPeerID})
  179. }
  180. func (mem *CListMempool) CheckTxWithInfo(tx types.Tx, cb func(*abci.Response), txInfo TxInfo) (err error) {
  181. mem.proxyMtx.Lock()
  182. // use defer to unlock mutex because application (*local client*) might panic
  183. defer mem.proxyMtx.Unlock()
  184. var (
  185. memSize = mem.Size()
  186. txsBytes = mem.TxsBytes()
  187. )
  188. if memSize >= mem.config.Size ||
  189. int64(len(tx))+txsBytes > mem.config.MaxTxsBytes {
  190. return ErrMempoolIsFull{
  191. memSize, mem.config.Size,
  192. txsBytes, mem.config.MaxTxsBytes}
  193. }
  194. // The size of the corresponding amino-encoded TxMessage
  195. // can't be larger than the maxMsgSize, otherwise we can't
  196. // relay it to peers.
  197. if len(tx) > maxTxSize {
  198. return ErrTxTooLarge
  199. }
  200. if mem.preCheck != nil {
  201. if err := mem.preCheck(tx); err != nil {
  202. return ErrPreCheck{err}
  203. }
  204. }
  205. // CACHE
  206. if !mem.cache.Push(tx) {
  207. // Record a new sender for a tx we've already seen.
  208. // Note it's possible a tx is still in the cache but no longer in the mempool
  209. // (eg. after committing a block, txs are removed from mempool but not cache),
  210. // so we only record the sender for txs still in the mempool.
  211. if e, ok := mem.txsMap.Load(txKey(tx)); ok {
  212. memTx := e.(*clist.CElement).Value.(*mempoolTx)
  213. if _, loaded := memTx.senders.LoadOrStore(txInfo.SenderID, true); loaded {
  214. // TODO: consider punishing peer for dups,
  215. // its non-trivial since invalid txs can become valid,
  216. // but they can spam the same tx with little cost to them atm.
  217. }
  218. }
  219. return ErrTxInCache
  220. }
  221. // END CACHE
  222. // WAL
  223. if mem.wal != nil {
  224. // TODO: Notify administrators when WAL fails
  225. _, err := mem.wal.Write([]byte(tx))
  226. if err != nil {
  227. mem.logger.Error("Error writing to WAL", "err", err)
  228. }
  229. _, err = mem.wal.Write([]byte("\n"))
  230. if err != nil {
  231. mem.logger.Error("Error writing to WAL", "err", err)
  232. }
  233. }
  234. // END WAL
  235. // NOTE: proxyAppConn may error if tx buffer is full
  236. if err = mem.proxyAppConn.Error(); err != nil {
  237. return err
  238. }
  239. reqRes := mem.proxyAppConn.CheckTxAsync(tx)
  240. reqRes.SetCallback(mem.reqResCb(tx, txInfo.SenderID, cb))
  241. return nil
  242. }
  243. // Global callback that will be called after every ABCI response.
  244. // Having a single global callback avoids needing to set a callback for each request.
  245. // However, processing the checkTx response requires the peerID (so we can track which txs we heard from who),
  246. // and peerID is not included in the ABCI request, so we have to set request-specific callbacks that
  247. // include this information. If we're not in the midst of a recheck, this function will just return,
  248. // so the request specific callback can do the work.
  249. // When rechecking, we don't need the peerID, so the recheck callback happens here.
  250. func (mem *CListMempool) globalCb(req *abci.Request, res *abci.Response) {
  251. if mem.recheckCursor == nil {
  252. return
  253. }
  254. mem.metrics.RecheckTimes.Add(1)
  255. mem.resCbRecheck(req, res)
  256. // update metrics
  257. mem.metrics.Size.Set(float64(mem.Size()))
  258. }
  259. // Request specific callback that should be set on individual reqRes objects
  260. // to incorporate local information when processing the response.
  261. // This allows us to track the peer that sent us this tx, so we can avoid sending it back to them.
  262. // NOTE: alternatively, we could include this information in the ABCI request itself.
  263. //
  264. // External callers of CheckTx, like the RPC, can also pass an externalCb through here that is called
  265. // when all other response processing is complete.
  266. //
  267. // Used in CheckTxWithInfo to record PeerID who sent us the tx.
  268. func (mem *CListMempool) reqResCb(tx []byte, peerID uint16, externalCb func(*abci.Response)) func(res *abci.Response) {
  269. return func(res *abci.Response) {
  270. if mem.recheckCursor != nil {
  271. // this should never happen
  272. panic("recheck cursor is not nil in reqResCb")
  273. }
  274. mem.resCbFirstTime(tx, peerID, res)
  275. // update metrics
  276. mem.metrics.Size.Set(float64(mem.Size()))
  277. // passed in by the caller of CheckTx, eg. the RPC
  278. if externalCb != nil {
  279. externalCb(res)
  280. }
  281. }
  282. }
  283. // Called from:
  284. // - resCbFirstTime (lock not held) if tx is valid
  285. func (mem *CListMempool) addTx(memTx *mempoolTx) {
  286. e := mem.txs.PushBack(memTx)
  287. mem.txsMap.Store(txKey(memTx.tx), e)
  288. atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx)))
  289. mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx)))
  290. }
  291. // Called from:
  292. // - Update (lock held) if tx was committed
  293. // - resCbRecheck (lock not held) if tx was invalidated
  294. func (mem *CListMempool) removeTx(tx types.Tx, elem *clist.CElement, removeFromCache bool) {
  295. mem.txs.Remove(elem)
  296. elem.DetachPrev()
  297. mem.txsMap.Delete(txKey(tx))
  298. atomic.AddInt64(&mem.txsBytes, int64(-len(tx)))
  299. if removeFromCache {
  300. mem.cache.Remove(tx)
  301. }
  302. }
  303. // callback, which is called after the app checked the tx for the first time.
  304. //
  305. // The case where the app checks the tx for the second and subsequent times is
  306. // handled by the resCbRecheck callback.
  307. func (mem *CListMempool) resCbFirstTime(tx []byte, peerID uint16, res *abci.Response) {
  308. switch r := res.Value.(type) {
  309. case *abci.Response_CheckTx:
  310. var postCheckErr error
  311. if mem.postCheck != nil {
  312. postCheckErr = mem.postCheck(tx, r.CheckTx)
  313. }
  314. if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
  315. memTx := &mempoolTx{
  316. height: mem.height,
  317. gasWanted: r.CheckTx.GasWanted,
  318. tx: tx,
  319. }
  320. memTx.senders.Store(peerID, true)
  321. mem.addTx(memTx)
  322. mem.logger.Info("Added good transaction",
  323. "tx", txID(tx),
  324. "res", r,
  325. "height", memTx.height,
  326. "total", mem.Size(),
  327. )
  328. mem.notifyTxsAvailable()
  329. } else {
  330. // ignore bad transaction
  331. mem.logger.Info("Rejected bad transaction", "tx", txID(tx), "res", r, "err", postCheckErr)
  332. mem.metrics.FailedTxs.Add(1)
  333. // remove from cache (it might be good later)
  334. mem.cache.Remove(tx)
  335. }
  336. default:
  337. // ignore other messages
  338. }
  339. }
  340. // callback, which is called after the app rechecked the tx.
  341. //
  342. // The case where the app checks the tx for the first time is handled by the
  343. // resCbFirstTime callback.
  344. func (mem *CListMempool) resCbRecheck(req *abci.Request, res *abci.Response) {
  345. switch r := res.Value.(type) {
  346. case *abci.Response_CheckTx:
  347. tx := req.GetCheckTx().Tx
  348. memTx := mem.recheckCursor.Value.(*mempoolTx)
  349. if !bytes.Equal(tx, memTx.tx) {
  350. panic(fmt.Sprintf(
  351. "Unexpected tx response from proxy during recheck\nExpected %X, got %X",
  352. memTx.tx,
  353. tx))
  354. }
  355. var postCheckErr error
  356. if mem.postCheck != nil {
  357. postCheckErr = mem.postCheck(tx, r.CheckTx)
  358. }
  359. if (r.CheckTx.Code == abci.CodeTypeOK) && postCheckErr == nil {
  360. // Good, nothing to do.
  361. } else {
  362. // Tx became invalidated due to newly committed block.
  363. mem.logger.Info("Tx is no longer valid", "tx", txID(tx), "res", r, "err", postCheckErr)
  364. // NOTE: we remove tx from the cache because it might be good later
  365. mem.removeTx(tx, mem.recheckCursor, true)
  366. }
  367. if mem.recheckCursor == mem.recheckEnd {
  368. mem.recheckCursor = nil
  369. } else {
  370. mem.recheckCursor = mem.recheckCursor.Next()
  371. }
  372. if mem.recheckCursor == nil {
  373. // Done!
  374. atomic.StoreInt32(&mem.rechecking, 0)
  375. mem.logger.Info("Done rechecking txs")
  376. // incase the recheck removed all txs
  377. if mem.Size() > 0 {
  378. mem.notifyTxsAvailable()
  379. }
  380. }
  381. default:
  382. // ignore other messages
  383. }
  384. }
  385. func (mem *CListMempool) TxsAvailable() <-chan struct{} {
  386. return mem.txsAvailable
  387. }
  388. func (mem *CListMempool) notifyTxsAvailable() {
  389. if mem.Size() == 0 {
  390. panic("notified txs available but mempool is empty!")
  391. }
  392. if mem.txsAvailable != nil && !mem.notifiedTxsAvailable {
  393. // channel cap is 1, so this will send once
  394. mem.notifiedTxsAvailable = true
  395. select {
  396. case mem.txsAvailable <- struct{}{}:
  397. default:
  398. }
  399. }
  400. }
  401. func (mem *CListMempool) ReapMaxBytesMaxGas(maxBytes, maxGas int64) types.Txs {
  402. mem.proxyMtx.Lock()
  403. defer mem.proxyMtx.Unlock()
  404. for atomic.LoadInt32(&mem.rechecking) > 0 {
  405. // TODO: Something better?
  406. time.Sleep(time.Millisecond * 10)
  407. }
  408. var totalBytes int64
  409. var totalGas int64
  410. // TODO: we will get a performance boost if we have a good estimate of avg
  411. // size per tx, and set the initial capacity based off of that.
  412. // txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max/mem.avgTxSize))
  413. txs := make([]types.Tx, 0, mem.txs.Len())
  414. for e := mem.txs.Front(); e != nil; e = e.Next() {
  415. memTx := e.Value.(*mempoolTx)
  416. // Check total size requirement
  417. aminoOverhead := types.ComputeAminoOverhead(memTx.tx, 1)
  418. if maxBytes > -1 && totalBytes+int64(len(memTx.tx))+aminoOverhead > maxBytes {
  419. return txs
  420. }
  421. totalBytes += int64(len(memTx.tx)) + aminoOverhead
  422. // Check total gas requirement.
  423. // If maxGas is negative, skip this check.
  424. // Since newTotalGas < masGas, which
  425. // must be non-negative, it follows that this won't overflow.
  426. newTotalGas := totalGas + memTx.gasWanted
  427. if maxGas > -1 && newTotalGas > maxGas {
  428. return txs
  429. }
  430. totalGas = newTotalGas
  431. txs = append(txs, memTx.tx)
  432. }
  433. return txs
  434. }
  435. func (mem *CListMempool) ReapMaxTxs(max int) types.Txs {
  436. mem.proxyMtx.Lock()
  437. defer mem.proxyMtx.Unlock()
  438. if max < 0 {
  439. max = mem.txs.Len()
  440. }
  441. for atomic.LoadInt32(&mem.rechecking) > 0 {
  442. // TODO: Something better?
  443. time.Sleep(time.Millisecond * 10)
  444. }
  445. txs := make([]types.Tx, 0, cmn.MinInt(mem.txs.Len(), max))
  446. for e := mem.txs.Front(); e != nil && len(txs) <= max; e = e.Next() {
  447. memTx := e.Value.(*mempoolTx)
  448. txs = append(txs, memTx.tx)
  449. }
  450. return txs
  451. }
  452. func (mem *CListMempool) Update(
  453. height int64,
  454. txs types.Txs,
  455. preCheck PreCheckFunc,
  456. postCheck PostCheckFunc,
  457. ) error {
  458. // Set height
  459. mem.height = height
  460. mem.notifiedTxsAvailable = false
  461. if preCheck != nil {
  462. mem.preCheck = preCheck
  463. }
  464. if postCheck != nil {
  465. mem.postCheck = postCheck
  466. }
  467. // Add committed transactions to cache (if missing).
  468. for _, tx := range txs {
  469. _ = mem.cache.Push(tx)
  470. }
  471. // Remove committed transactions.
  472. txsLeft := mem.removeTxs(txs)
  473. // Either recheck non-committed txs to see if they became invalid
  474. // or just notify there're some txs left.
  475. if len(txsLeft) > 0 {
  476. if mem.config.Recheck {
  477. mem.logger.Info("Recheck txs", "numtxs", len(txsLeft), "height", height)
  478. mem.recheckTxs(txsLeft)
  479. // At this point, mem.txs are being rechecked.
  480. // mem.recheckCursor re-scans mem.txs and possibly removes some txs.
  481. // Before mem.Reap(), we should wait for mem.recheckCursor to be nil.
  482. } else {
  483. mem.notifyTxsAvailable()
  484. }
  485. }
  486. // Update metrics
  487. mem.metrics.Size.Set(float64(mem.Size()))
  488. return nil
  489. }
  490. func (mem *CListMempool) removeTxs(txs types.Txs) []types.Tx {
  491. // Build a map for faster lookups.
  492. txsMap := make(map[string]struct{}, len(txs))
  493. for _, tx := range txs {
  494. txsMap[string(tx)] = struct{}{}
  495. }
  496. txsLeft := make([]types.Tx, 0, mem.txs.Len())
  497. for e := mem.txs.Front(); e != nil; e = e.Next() {
  498. memTx := e.Value.(*mempoolTx)
  499. // Remove the tx if it's already in a block.
  500. if _, ok := txsMap[string(memTx.tx)]; ok {
  501. // NOTE: we don't remove committed txs from the cache.
  502. mem.removeTx(memTx.tx, e, false)
  503. continue
  504. }
  505. txsLeft = append(txsLeft, memTx.tx)
  506. }
  507. return txsLeft
  508. }
  509. // NOTE: pass in txs because mem.txs can mutate concurrently.
  510. func (mem *CListMempool) recheckTxs(txs []types.Tx) {
  511. if len(txs) == 0 {
  512. return
  513. }
  514. atomic.StoreInt32(&mem.rechecking, 1)
  515. mem.recheckCursor = mem.txs.Front()
  516. mem.recheckEnd = mem.txs.Back()
  517. // Push txs to proxyAppConn
  518. // NOTE: globalCb may be called concurrently.
  519. for _, tx := range txs {
  520. mem.proxyAppConn.CheckTxAsync(tx)
  521. }
  522. mem.proxyAppConn.FlushAsync()
  523. }
  524. //--------------------------------------------------------------------------------
  525. // mempoolTx is a transaction that successfully ran
  526. type mempoolTx struct {
  527. height int64 // height that this tx had been validated in
  528. gasWanted int64 // amount of gas this tx states it will require
  529. tx types.Tx //
  530. // ids of peers who've sent us this tx (as a map for quick lookups).
  531. // senders: PeerID -> bool
  532. senders sync.Map
  533. }
  534. // Height returns the height for this transaction
  535. func (memTx *mempoolTx) Height() int64 {
  536. return atomic.LoadInt64(&memTx.height)
  537. }
  538. //--------------------------------------------------------------------------------
  539. type txCache interface {
  540. Reset()
  541. Push(tx types.Tx) bool
  542. Remove(tx types.Tx)
  543. }
  544. // mapTxCache maintains a LRU cache of transactions. This only stores the hash
  545. // of the tx, due to memory concerns.
  546. type mapTxCache struct {
  547. mtx sync.Mutex
  548. size int
  549. map_ map[[sha256.Size]byte]*list.Element
  550. list *list.List
  551. }
  552. var _ txCache = (*mapTxCache)(nil)
  553. // newMapTxCache returns a new mapTxCache.
  554. func newMapTxCache(cacheSize int) *mapTxCache {
  555. return &mapTxCache{
  556. size: cacheSize,
  557. map_: make(map[[sha256.Size]byte]*list.Element, cacheSize),
  558. list: list.New(),
  559. }
  560. }
  561. // Reset resets the cache to an empty state.
  562. func (cache *mapTxCache) Reset() {
  563. cache.mtx.Lock()
  564. cache.map_ = make(map[[sha256.Size]byte]*list.Element, cache.size)
  565. cache.list.Init()
  566. cache.mtx.Unlock()
  567. }
  568. // Push adds the given tx to the cache and returns true. It returns
  569. // false if tx is already in the cache.
  570. func (cache *mapTxCache) Push(tx types.Tx) bool {
  571. cache.mtx.Lock()
  572. defer cache.mtx.Unlock()
  573. // Use the tx hash in the cache
  574. txHash := txKey(tx)
  575. if moved, exists := cache.map_[txHash]; exists {
  576. cache.list.MoveToBack(moved)
  577. return false
  578. }
  579. if cache.list.Len() >= cache.size {
  580. popped := cache.list.Front()
  581. poppedTxHash := popped.Value.([sha256.Size]byte)
  582. delete(cache.map_, poppedTxHash)
  583. if popped != nil {
  584. cache.list.Remove(popped)
  585. }
  586. }
  587. e := cache.list.PushBack(txHash)
  588. cache.map_[txHash] = e
  589. return true
  590. }
  591. // Remove removes the given tx from the cache.
  592. func (cache *mapTxCache) Remove(tx types.Tx) {
  593. cache.mtx.Lock()
  594. txHash := txKey(tx)
  595. popped := cache.map_[txHash]
  596. delete(cache.map_, txHash)
  597. if popped != nil {
  598. cache.list.Remove(popped)
  599. }
  600. cache.mtx.Unlock()
  601. }
  602. type nopTxCache struct{}
  603. var _ txCache = (*nopTxCache)(nil)
  604. func (nopTxCache) Reset() {}
  605. func (nopTxCache) Push(types.Tx) bool { return true }
  606. func (nopTxCache) Remove(types.Tx) {}
  607. //--------------------------------------------------------------------------------
  608. // txKey is the fixed length array sha256 hash used as the key in maps.
  609. func txKey(tx types.Tx) [sha256.Size]byte {
  610. return sha256.Sum256(tx)
  611. }
  612. // txID is the hex encoded hash of the bytes as a types.Tx.
  613. func txID(tx []byte) string {
  614. return fmt.Sprintf("%X", types.Tx(tx).Hash())
  615. }