hermez-node/coordinator/purger_test.go
coordinator/purger_test.goRaw
1package coordinator
2
3import (
4 "io/ioutil"
5 "os"
6 "testing"
7 "time"
8
9 "github.com/hermeznetwork/hermez-node/common"
10 dbUtils "github.com/hermeznetwork/hermez-node/db"
11 "github.com/hermeznetwork/hermez-node/db/l2db"
12 "github.com/hermeznetwork/hermez-node/db/statedb"
13 "github.com/hermeznetwork/hermez-node/test"
14 "github.com/hermeznetwork/hermez-node/test/til"
15 "github.com/stretchr/testify/assert"
16 "github.com/stretchr/testify/require"
17)
18
19func newL2DB(t *testing.T) *l2db.L2DB {
20 pass := os.Getenv("POSTGRES_PASS")
21 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
22 require.NoError(t, err)
23 test.WipeDB(db)
24 return l2db.NewL2DB(db, db, 10, 100, 0.0, 1000.0, 24*time.Hour, nil)
25}
26
27func newStateDB(t *testing.T) *statedb.LocalStateDB {
28 syncDBPath, err := ioutil.TempDir("", "tmpSyncDB")
29 require.NoError(t, err)
30 deleteme = append(deleteme, syncDBPath)
31 syncStateDB, err := statedb.NewStateDB(statedb.Config{Path: syncDBPath, Keep: 128,
32 Type: statedb.TypeSynchronizer, NLevels: 48})
33 assert.NoError(t, err)
34 stateDBPath, err := ioutil.TempDir("", "tmpStateDB")
35 require.NoError(t, err)
36 deleteme = append(deleteme, stateDBPath)
37 stateDB, err := statedb.NewLocalStateDB(statedb.Config{Path: stateDBPath, Keep: 128,
38 Type: statedb.TypeTxSelector, NLevels: 0}, syncStateDB)
39 require.NoError(t, err)
40 return stateDB
41}
42
43func TestCanPurgeCanInvalidate(t *testing.T) {
44 cfg := PurgerCfg{
45 PurgeBatchDelay: 2,
46 PurgeBlockDelay: 6,
47 InvalidateBatchDelay: 4,
48 InvalidateBlockDelay: 8,
49 }
50 p := Purger{
51 cfg: cfg,
52 }
53 startBlockNum := int64(1000)
54 startBatchNum := int64(10)
55 blockNum := startBlockNum
56 batchNum := startBatchNum
57
58 assert.True(t, p.CanPurge(blockNum, batchNum))
59 p.lastPurgeBlock = startBlockNum
60 p.lastPurgeBatch = startBatchNum
61 assert.False(t, p.CanPurge(blockNum, batchNum))
62
63 blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
64 batchNum = startBatchNum + cfg.PurgeBatchDelay - 1
65 assert.False(t, p.CanPurge(blockNum, batchNum))
66 blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
67 batchNum = startBatchNum + cfg.PurgeBatchDelay
68 assert.True(t, p.CanPurge(blockNum, batchNum))
69 blockNum = startBlockNum + cfg.PurgeBlockDelay
70 batchNum = startBatchNum + cfg.PurgeBatchDelay - 1
71 assert.True(t, p.CanPurge(blockNum, batchNum))
72
73 assert.True(t, p.CanInvalidate(blockNum, batchNum))
74 p.lastInvalidateBlock = startBlockNum
75 p.lastInvalidateBatch = startBatchNum
76 assert.False(t, p.CanInvalidate(blockNum, batchNum))
77
78 blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
79 batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
80 assert.False(t, p.CanInvalidate(blockNum, batchNum))
81 blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
82 batchNum = startBatchNum + cfg.InvalidateBatchDelay
83 assert.True(t, p.CanInvalidate(blockNum, batchNum))
84 blockNum = startBlockNum + cfg.InvalidateBlockDelay
85 batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
86 assert.True(t, p.CanInvalidate(blockNum, batchNum))
87}
88
89func TestPurgeMaybeInvalidateMaybe(t *testing.T) {
90 cfg := PurgerCfg{
91 PurgeBatchDelay: 2,
92 PurgeBlockDelay: 6,
93 InvalidateBatchDelay: 4,
94 InvalidateBlockDelay: 8,
95 }
96 p := Purger{
97 cfg: cfg,
98 }
99 l2DB := newL2DB(t)
100 stateDB := newStateDB(t)
101
102 startBlockNum := int64(1000)
103 startBatchNum := int64(10)
104
105 p.lastPurgeBlock = startBlockNum
106 p.lastPurgeBatch = startBatchNum
107
108 blockNum := startBlockNum + cfg.PurgeBlockDelay - 1
109 batchNum := startBatchNum + cfg.PurgeBatchDelay - 1
110 ok, err := p.PurgeMaybe(l2DB, blockNum, batchNum)
111 require.NoError(t, err)
112 assert.False(t, ok)
113 // At this point the purger will purge. The second time it doesn't
114 // because it the first time it has updates the last time it did.
115 blockNum = startBlockNum + cfg.PurgeBlockDelay - 1
116 batchNum = startBatchNum + cfg.PurgeBatchDelay
117 ok, err = p.PurgeMaybe(l2DB, blockNum, batchNum)
118 require.NoError(t, err)
119 assert.True(t, ok)
120 ok, err = p.PurgeMaybe(l2DB, blockNum, batchNum)
121 require.NoError(t, err)
122 assert.False(t, ok)
123
124 p.lastInvalidateBlock = startBlockNum
125 p.lastInvalidateBatch = startBatchNum
126
127 blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
128 batchNum = startBatchNum + cfg.InvalidateBatchDelay - 1
129 ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
130 require.NoError(t, err)
131 assert.False(t, ok)
132 // At this point the purger will invaidate. The second time it doesn't
133 // because it the first time it has updates the last time it did.
134 blockNum = startBlockNum + cfg.InvalidateBlockDelay - 1
135 batchNum = startBatchNum + cfg.InvalidateBatchDelay
136 ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
137 require.NoError(t, err)
138 assert.True(t, ok)
139 ok, err = p.InvalidateMaybe(l2DB, stateDB, blockNum, batchNum)
140 require.NoError(t, err)
141 assert.False(t, ok)
142}
143
144func TestIdxsNonce(t *testing.T) {
145 inputIdxsNonce := []common.IdxNonce{
146 {Idx: 256, Nonce: 1},
147 {Idx: 256, Nonce: 2},
148 {Idx: 257, Nonce: 3},
149 {Idx: 258, Nonce: 5},
150 {Idx: 258, Nonce: 2},
151 }
152 expectedIdxsNonce := map[common.Idx]common.Nonce{
153 common.Idx(256): common.Nonce(2),
154 common.Idx(257): common.Nonce(3),
155 common.Idx(258): common.Nonce(5),
156 }
157
158 l2txs := make([]common.L2Tx, len(inputIdxsNonce))
159 for i, idxNonce := range inputIdxsNonce {
160 l2txs[i].FromIdx = idxNonce.Idx
161 l2txs[i].Nonce = idxNonce.Nonce
162 }
163 idxsNonce := idxsNonceFromL2Txs(l2txs)
164 assert.Equal(t, len(expectedIdxsNonce), len(idxsNonce))
165 for _, idxNonce := range idxsNonce {
166 nonce := expectedIdxsNonce[idxNonce.Idx]
167 assert.Equal(t, nonce, idxNonce.Nonce)
168 }
169
170 pooll2txs := make([]common.PoolL2Tx, len(inputIdxsNonce))
171 for i, idxNonce := range inputIdxsNonce {
172 pooll2txs[i].FromIdx = idxNonce.Idx
173 pooll2txs[i].Nonce = idxNonce.Nonce
174 }
175 idxsNonce = idxsNonceFromPoolL2Txs(pooll2txs)
176 assert.Equal(t, len(expectedIdxsNonce), len(idxsNonce))
177 for _, idxNonce := range idxsNonce {
178 nonce := expectedIdxsNonce[idxNonce.Idx]
179 assert.Equal(t, nonce, idxNonce.Nonce)
180 }
181}
182
183func TestPoolMarkInvalidOldNonces(t *testing.T) {
184 l2DB := newL2DB(t)
185 stateDB := newStateDB(t)
186
187 set0 := `
188 Type: Blockchain
189
190 CreateAccountDeposit(0) A: 1000 // Idx=256
191 CreateAccountDeposit(0) B: 1000 // Idx=257
192 CreateAccountDeposit(0) C: 1000 // Idx=258
193 CreateAccountDeposit(0) D: 1000 // Idx=259
194
195 > batchL1
196 > batchL1
197 > block
198 `
199 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
200 blocks, err := tc.GenerateBlocks(set0)
201 require.NoError(t, err)
202 tilCfgExtra := til.ConfigExtra{
203 CoordUser: "A",
204 }
205 // Call FillBlocksExtra to fill `Batch.CreatedAccounts`
206 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
207 require.NoError(t, err)
208 require.Equal(t, 4, len(blocks[0].Rollup.Batches[1].CreatedAccounts)) // sanity check
209
210 for _, acc := range blocks[0].Rollup.Batches[1].CreatedAccounts {
211 _, err := stateDB.CreateAccount(acc.Idx, &acc) //nolint:gosec
212 require.NoError(t, err)
213 }
214
215 setPool0 := `
216 Type: PoolL2
217 PoolTransfer(0) A-B: 10 (1)
218 PoolTransfer(0) A-C: 10 (1)
219 PoolTransfer(0) A-D: 10 (1)
220 PoolTransfer(0) B-A: 10 (1)
221 PoolTransfer(0) B-C: 10 (1)
222 PoolTransfer(0) C-A: 10 (1)
223 `
224 // We expect the following nonces
225 nonces0 := map[string]int64{"A": 3, "B": 2, "C": 1, "D": 0}
226 l2txs0, err := tc.GeneratePoolL2Txs(setPool0)
227 assert.Nil(t, err)
228 assert.Equal(t, 6, len(l2txs0))
229 for _, tx := range l2txs0 {
230 require.NoError(t, l2DB.AddTxTest(&tx)) //nolint:gosec
231 }
232
233 // Update the accounts in the StateDB, making the txs in the setPool0
234 // invalid
235 for name, user := range tc.Users {
236 for _, _acc := range user.Accounts {
237 require.Equal(t, common.Nonce(nonces0[name]), _acc.Nonce) // sanity check
238 acc, err := stateDB.GetAccount(_acc.Idx)
239 require.NoError(t, err)
240 require.Equal(t, common.Nonce(0), acc.Nonce) // sanity check
241 acc.Nonce = _acc.Nonce
242 _, err = stateDB.UpdateAccount(acc.Idx, acc)
243 require.NoError(t, err)
244 }
245 }
246
247 setPool1 := `
248 Type: PoolL2
249 PoolTransfer(0) A-B: 10 (1)
250 PoolTransfer(0) A-C: 10 (1)
251 PoolTransfer(0) A-D: 10 (1)
252 PoolTransfer(0) B-A: 10 (1)
253 PoolTransfer(0) B-C: 10 (1)
254 PoolTransfer(0) C-A: 10 (1)
255 `
256 // We expect the following nonces
257 nonces1 := map[string]int64{"A": 6, "B": 4, "C": 2, "D": 0}
258 l2txs1, err := tc.GeneratePoolL2Txs(setPool1)
259 require.NoError(t, err)
260 assert.Equal(t, 6, len(l2txs1))
261 for _, tx := range l2txs1 {
262 require.NoError(t, l2DB.AddTxTest(&tx)) //nolint:gosec
263 }
264
265 for name, user := range tc.Users {
266 for _, _acc := range user.Accounts {
267 require.Equal(t, common.Nonce(nonces1[name]), _acc.Nonce) // sanity check
268 acc, err := stateDB.GetAccount(_acc.Idx)
269 require.NoError(t, err)
270 require.Equal(t, common.Nonce(nonces0[name]), acc.Nonce) // sanity check
271 }
272 }
273
274 // Now we should have 12 txs in the pool, all marked as pending. Since
275 // we updated the stateDB with the nonces after setPool0, the first 6
276 // txs will be marked as invalid
277
278 pendingTxs, err := l2DB.GetPendingTxs()
279 require.NoError(t, err)
280 assert.Equal(t, 12, len(pendingTxs))
281
282 batchNum := common.BatchNum(1)
283 err = poolMarkInvalidOldNonces(l2DB, stateDB, batchNum)
284 require.NoError(t, err)
285
286 pendingTxs, err = l2DB.GetPendingTxs()
287 require.NoError(t, err)
288 assert.Equal(t, 6, len(pendingTxs))
289}