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. Oudated 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. Oudated 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}