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