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