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