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.

145 lines
4.5 KiB

  1. package coordinator
  2. import (
  3. "fmt"
  4. "github.com/hermeznetwork/hermez-node/common"
  5. "github.com/hermeznetwork/hermez-node/db/l2db"
  6. "github.com/hermeznetwork/hermez-node/db/statedb"
  7. "github.com/hermeznetwork/hermez-node/log"
  8. "github.com/hermeznetwork/tracerr"
  9. "github.com/iden3/go-merkletree/db"
  10. )
  11. // PurgerCfg is the purger configuration
  12. type PurgerCfg struct {
  13. // PurgeBatchDelay is the delay between batches to purge outdated transactions
  14. PurgeBatchDelay int64
  15. // InvalidateBatchDelay is the delay between batches to mark invalid transactions
  16. InvalidateBatchDelay int64
  17. // PurgeBlockDelay is the delay between blocks to purge outdated transactions
  18. PurgeBlockDelay int64
  19. // InvalidateBlockDelay is the delay between blocks to mark invalid transactions
  20. InvalidateBlockDelay int64
  21. }
  22. // Purger manages cleanup of transactions in the pool
  23. type Purger struct {
  24. cfg PurgerCfg
  25. lastPurgeBlock int64
  26. lastPurgeBatch int64
  27. lastInvalidateBlock int64
  28. lastInvalidateBatch int64
  29. }
  30. // CanPurge returns true if it's a good time to purge according to the
  31. // configuration
  32. func (p *Purger) CanPurge(blockNum, batchNum int64) bool {
  33. if blockNum >= p.lastPurgeBlock+p.cfg.PurgeBlockDelay {
  34. return true
  35. }
  36. if batchNum >= p.lastPurgeBatch+p.cfg.PurgeBatchDelay {
  37. return true
  38. }
  39. return false
  40. }
  41. // CanInvalidate returns true if it's a good time to invalidate according to
  42. // the configuration
  43. func (p *Purger) CanInvalidate(blockNum, batchNum int64) bool {
  44. if blockNum >= p.lastInvalidateBlock+p.cfg.InvalidateBlockDelay {
  45. return true
  46. }
  47. if batchNum >= p.lastInvalidateBatch+p.cfg.InvalidateBatchDelay {
  48. return true
  49. }
  50. return false
  51. }
  52. // PurgeMaybe purges txs if it's a good time to do so
  53. func (p *Purger) PurgeMaybe(l2DB *l2db.L2DB, blockNum, batchNum int64) (bool, error) {
  54. if !p.CanPurge(blockNum, batchNum) {
  55. return false, nil
  56. }
  57. p.lastPurgeBlock = blockNum
  58. p.lastPurgeBatch = batchNum
  59. log.Debugw("Purger: purging l2txs in pool", "block", blockNum, "batch", batchNum)
  60. err := l2DB.Purge(common.BatchNum(batchNum))
  61. return true, tracerr.Wrap(err)
  62. }
  63. // InvalidateMaybe invalidates txs if it's a good time to do so
  64. func (p *Purger) InvalidateMaybe(l2DB *l2db.L2DB, stateDB *statedb.LocalStateDB,
  65. blockNum, batchNum int64) (bool, error) {
  66. if !p.CanInvalidate(blockNum, batchNum) {
  67. return false, nil
  68. }
  69. p.lastInvalidateBlock = blockNum
  70. p.lastInvalidateBatch = batchNum
  71. log.Debugw("Purger: invalidating l2txs in pool", "block", blockNum, "batch", batchNum)
  72. err := poolMarkInvalidOldNonces(l2DB, stateDB, common.BatchNum(batchNum))
  73. return true, tracerr.Wrap(err)
  74. }
  75. //nolint:unused,deadcode
  76. func idxsNonceFromL2Txs(txs []common.L2Tx) []common.IdxNonce {
  77. idxNonceMap := map[common.Idx]common.Nonce{}
  78. for _, tx := range txs {
  79. if nonce, ok := idxNonceMap[tx.FromIdx]; !ok {
  80. idxNonceMap[tx.FromIdx] = tx.Nonce
  81. } else if tx.Nonce > nonce {
  82. idxNonceMap[tx.FromIdx] = tx.Nonce
  83. }
  84. }
  85. idxsNonce := make([]common.IdxNonce, 0, len(idxNonceMap))
  86. for idx, nonce := range idxNonceMap {
  87. idxsNonce = append(idxsNonce, common.IdxNonce{Idx: idx, Nonce: nonce})
  88. }
  89. return idxsNonce
  90. }
  91. func idxsNonceFromPoolL2Txs(txs []common.PoolL2Tx) []common.IdxNonce {
  92. idxNonceMap := map[common.Idx]common.Nonce{}
  93. for _, tx := range txs {
  94. if nonce, ok := idxNonceMap[tx.FromIdx]; !ok {
  95. idxNonceMap[tx.FromIdx] = tx.Nonce
  96. } else if tx.Nonce > nonce {
  97. idxNonceMap[tx.FromIdx] = tx.Nonce
  98. }
  99. }
  100. idxsNonce := make([]common.IdxNonce, 0, len(idxNonceMap))
  101. for idx, nonce := range idxNonceMap {
  102. idxsNonce = append(idxsNonce, common.IdxNonce{Idx: idx, Nonce: nonce})
  103. }
  104. return idxsNonce
  105. }
  106. // poolMarkInvalidOldNonces marks as invalid txs in the pool that contain
  107. // nonces equal or older to the nonce of the corresponding sender account
  108. func poolMarkInvalidOldNonces(l2DB *l2db.L2DB, stateDB *statedb.LocalStateDB,
  109. batchNum common.BatchNum) error {
  110. idxs, err := l2DB.GetPendingUniqueFromIdxs()
  111. if err != nil {
  112. return tracerr.Wrap(err)
  113. }
  114. idxsNonce := make([]common.IdxNonce, len(idxs))
  115. lastIdx, err := stateDB.GetCurrentIdx()
  116. if err != nil {
  117. return tracerr.Wrap(err)
  118. }
  119. for i, idx := range idxs {
  120. acc, err := stateDB.GetAccount(idx)
  121. if err != nil {
  122. if tracerr.Unwrap(err) != db.ErrNotFound {
  123. return tracerr.Wrap(err)
  124. } else if idx <= lastIdx {
  125. return tracerr.Wrap(fmt.Errorf("account with idx %v not found: %w", idx, err))
  126. } else {
  127. return tracerr.Wrap(fmt.Errorf("unexpected stateDB error with idx %v: %w", idx, err))
  128. }
  129. }
  130. idxsNonce[i].Idx = idx
  131. idxsNonce[i].Nonce = acc.Nonce
  132. }
  133. return l2DB.InvalidateOldNonces(idxsNonce, batchNum)
  134. }