hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
1package historydb
2
3import (
4 "database/sql"
5 "math"
6 "math/big"
7 "os"
8 "testing"
9 "time"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 "github.com/hermeznetwork/hermez-node/common"
13 dbUtils "github.com/hermeznetwork/hermez-node/db"
14 "github.com/hermeznetwork/hermez-node/log"
15 "github.com/hermeznetwork/hermez-node/test"
16 "github.com/hermeznetwork/hermez-node/test/til"
17 "github.com/hermeznetwork/tracerr"
18 "github.com/stretchr/testify/assert"
19 "github.com/stretchr/testify/require"
20)
21
22var historyDB *HistoryDB
23
24// In order to run the test you need to run a Posgres DB with
25// a database named "history" that is accessible by
26// user: "hermez"
27// pass: set it using the env var POSTGRES_PASS
28// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;"
29// After running the test you can stop the container by running: sudo docker kill hermez-db-test
30// If you already did that for the L2DB you don't have to do it again
31
32func TestMain(m *testing.M) {
33 // init DB
34 pass := os.Getenv("POSTGRES_PASS")
35 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
36 if err != nil {
37 panic(err)
38 }
39 historyDB = NewHistoryDB(db)
40 if err != nil {
41 panic(err)
42 }
43 // Run tests
44 result := m.Run()
45 // Close DB
46 if err := db.Close(); err != nil {
47 log.Error("Error closing the history DB:", err)
48 }
49 os.Exit(result)
50}
51
52func TestBlocks(t *testing.T) {
53 var fromBlock, toBlock int64
54 fromBlock = 0
55 toBlock = 7
56 // Reset DB
57 test.WipeDB(historyDB.DB())
58 // Generate blocks using til
59 set1 := `
60 Type: Blockchain
61 // block 0 is stored as default in the DB
62 // block 1 does not exist
63 > block // blockNum=2
64 > block // blockNum=3
65 > block // blockNum=4
66 > block // blockNum=5
67 > block // blockNum=6
68 `
69 tc := til.NewContext(1)
70 blocks, err := tc.GenerateBlocks(set1)
71 require.NoError(t, err)
72 // Save timestamp of a block with UTC and change it without UTC
73 timestamp := time.Now().Add(time.Second * 13)
74 blocks[fromBlock].Block.Timestamp = timestamp
75 // Insert blocks into DB
76 for i := 0; i < len(blocks); i++ {
77 err := historyDB.AddBlock(&blocks[i].Block)
78 assert.NoError(t, err)
79 }
80 // Add block 0 to the generated blocks
81 blocks = append(
82 []common.BlockData{{Block: test.Block0}}, //nolint:gofmt
83 blocks...,
84 )
85 // Get all blocks from DB
86 fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
87 assert.Equal(t, len(blocks), len(fetchedBlocks))
88 // Compare generated vs getted blocks
89 assert.NoError(t, err)
90 for i := range fetchedBlocks {
91 assertEqualBlock(t, &blocks[i].Block, &fetchedBlocks[i])
92 }
93 // Compare saved timestamp vs getted
94 nameZoneUTC, offsetUTC := timestamp.UTC().Zone()
95 zoneFetchedBlock, offsetFetchedBlock := fetchedBlocks[fromBlock].Timestamp.Zone()
96 assert.Equal(t, nameZoneUTC, zoneFetchedBlock)
97 assert.Equal(t, offsetUTC, offsetFetchedBlock)
98 // Get blocks from the DB one by one
99 for i := int64(2); i < toBlock; i++ { // avoid block 0 for simplicity
100 fetchedBlock, err := historyDB.GetBlock(i)
101 assert.NoError(t, err)
102 assertEqualBlock(t, &blocks[i-1].Block, fetchedBlock)
103 }
104 // Get last block
105 lastBlock, err := historyDB.GetLastBlock()
106 assert.NoError(t, err)
107 assertEqualBlock(t, &blocks[len(blocks)-1].Block, lastBlock)
108}
109
110func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
111 assert.Equal(t, expected.Num, actual.Num)
112 assert.Equal(t, expected.Hash, actual.Hash)
113 assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
114}
115
116func TestBatches(t *testing.T) {
117 // Reset DB
118 test.WipeDB(historyDB.DB())
119 // Generate batches using til (and blocks for foreign key)
120 set := `
121 Type: Blockchain
122
123 AddToken(1) // Will have value in USD
124 AddToken(2) // Will NOT have value in USD
125 CreateAccountDeposit(1) A: 2000
126 CreateAccountDeposit(2) A: 2000
127 CreateAccountDeposit(1) B: 1000
128 CreateAccountDeposit(2) B: 1000
129 > batchL1
130 > batchL1
131 Transfer(1) A-B: 100 (5)
132 Transfer(2) B-A: 100 (199)
133 > batch // batchNum=2, L2 only batch, forges transfers (mixed case of with(out) USD value)
134 > block
135 Transfer(1) A-B: 100 (5)
136 > batch // batchNum=3, L2 only batch, forges transfer (with USD value)
137 Transfer(2) B-A: 100 (199)
138 > batch // batchNum=4, L2 only batch, forges transfer (without USD value)
139 > block
140 `
141 tc := til.NewContext(common.RollupConstMaxL1UserTx)
142 tilCfgExtra := til.ConfigExtra{
143 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
144 CoordUser: "A",
145 }
146 blocks, err := tc.GenerateBlocks(set)
147 require.NoError(t, err)
148 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
149 require.NoError(t, err)
150 // Insert to DB
151 batches := []common.Batch{}
152 tokensValue := make(map[common.TokenID]float64)
153 lastL1TxsNum := new(int64)
154 lastL1BatchBlockNum := int64(0)
155 for _, block := range blocks {
156 // Insert block
157 assert.NoError(t, historyDB.AddBlock(&block.Block))
158 // Insert tokens
159 for i, token := range block.Rollup.AddedTokens {
160 assert.NoError(t, historyDB.AddToken(&token)) //nolint:gosec
161 if i%2 != 0 {
162 // Set value to the token
163 value := (float64(i) + 5) * 5.389329
164 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
165 tokensValue[token.TokenID] = value / math.Pow(10, float64(token.Decimals))
166 }
167 }
168 // Combine all generated batches into single array
169 for _, batch := range block.Rollup.Batches {
170 batches = append(batches, batch.Batch)
171 forgeTxsNum := batch.Batch.ForgeL1TxsNum
172 if forgeTxsNum != nil && (lastL1TxsNum == nil || *lastL1TxsNum < *forgeTxsNum) {
173 *lastL1TxsNum = *forgeTxsNum
174 lastL1BatchBlockNum = batch.Batch.EthBlockNum
175 }
176 }
177 }
178 // Insert batches
179 assert.NoError(t, historyDB.AddBatches(batches))
180 // Set expected total fee
181 for _, batch := range batches {
182 total := .0
183 for tokenID, amount := range batch.CollectedFees {
184 af := new(big.Float).SetInt(amount)
185 amountFloat, _ := af.Float64()
186 total += tokensValue[tokenID] * amountFloat
187 }
188 batch.TotalFeesUSD = &total
189 }
190 // Get batches from the DB
191 fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(len(batches)+1))
192 assert.NoError(t, err)
193 assert.Equal(t, len(batches), len(fetchedBatches))
194 for i, fetchedBatch := range fetchedBatches {
195 assert.Equal(t, batches[i], fetchedBatch)
196 }
197 // Test GetLastBatchNum
198 fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
199 assert.NoError(t, err)
200 assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
201 // Test GetLastL1TxsNum
202 fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
203 assert.NoError(t, err)
204 assert.Equal(t, lastL1TxsNum, fetchedLastL1TxsNum)
205 // Test GetLastL1BatchBlockNum
206 fetchedLastL1BatchBlockNum, err := historyDB.GetLastL1BatchBlockNum()
207 assert.NoError(t, err)
208 assert.Equal(t, lastL1BatchBlockNum, fetchedLastL1BatchBlockNum)
209}
210
211func TestBids(t *testing.T) {
212 const fromBlock int64 = 1
213 const toBlock int64 = 5
214 // Prepare blocks in the DB
215 blocks := setTestBlocks(fromBlock, toBlock)
216 // Generate fake coordinators
217 const nCoords = 5
218 coords := test.GenCoordinators(nCoords, blocks)
219 err := historyDB.AddCoordinators(coords)
220 assert.NoError(t, err)
221 // Generate fake bids
222 const nBids = 20
223 bids := test.GenBids(nBids, blocks, coords)
224 err = historyDB.AddBids(bids)
225 assert.NoError(t, err)
226 // Fetch bids
227 fetchedBids, err := historyDB.GetAllBids()
228 assert.NoError(t, err)
229 // Compare fetched bids vs generated bids
230 for i, bid := range fetchedBids {
231 assert.Equal(t, bids[i], bid)
232 }
233}
234
235func TestTokens(t *testing.T) {
236 const fromBlock int64 = 1
237 const toBlock int64 = 5
238 // Prepare blocks in the DB
239 blocks := setTestBlocks(fromBlock, toBlock)
240 // Generate fake tokens
241 const nTokens = 5
242 tokens, ethToken := test.GenTokens(nTokens, blocks)
243 err := historyDB.AddTokens(tokens)
244 assert.NoError(t, err)
245 tokens = append([]common.Token{ethToken}, tokens...)
246 limit := uint(10)
247 // Fetch tokens
248 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
249 assert.NoError(t, err)
250 // Compare fetched tokens vs generated tokens
251 // All the tokens should have USDUpdate setted by the DB trigger
252 for i, token := range fetchedTokens {
253 assert.Equal(t, tokens[i].TokenID, token.TokenID)
254 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
255 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
256 assert.Equal(t, tokens[i].Name, token.Name)
257 assert.Equal(t, tokens[i].Symbol, token.Symbol)
258 assert.Nil(t, token.USD)
259 assert.Nil(t, token.USDUpdate)
260 }
261
262 // Update token value
263 for i, token := range tokens {
264 value := 1.01 * float64(i)
265 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
266 }
267 // Fetch tokens
268 fetchedTokens, _, err = historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
269 assert.NoError(t, err)
270 // Compare fetched tokens vs generated tokens
271 // All the tokens should have USDUpdate setted by the DB trigger
272 for i, token := range fetchedTokens {
273 value := 1.01 * float64(i)
274 assert.Equal(t, value, *token.USD)
275 nameZone, offset := token.USDUpdate.Zone()
276 assert.Equal(t, "UTC", nameZone)
277 assert.Equal(t, 0, offset)
278 }
279}
280
281func TestAccounts(t *testing.T) {
282 const fromBlock int64 = 1
283 const toBlock int64 = 5
284 // Prepare blocks in the DB
285 blocks := setTestBlocks(fromBlock, toBlock)
286 // Generate fake tokens
287 const nTokens = 5
288 tokens, ethToken := test.GenTokens(nTokens, blocks)
289 err := historyDB.AddTokens(tokens)
290 assert.NoError(t, err)
291 tokens = append([]common.Token{ethToken}, tokens...)
292 // Generate fake batches
293 const nBatches = 10
294 batches := test.GenBatches(nBatches, blocks)
295 err = historyDB.AddBatches(batches)
296 assert.NoError(t, err)
297 // Generate fake accounts
298 const nAccounts = 3
299 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
300 err = historyDB.AddAccounts(accs)
301 assert.NoError(t, err)
302 // Fetch accounts
303 fetchedAccs, err := historyDB.GetAllAccounts()
304 assert.NoError(t, err)
305 // Compare fetched accounts vs generated accounts
306 for i, acc := range fetchedAccs {
307 accs[i].Balance = nil
308 assert.Equal(t, accs[i], acc)
309 }
310}
311
312func TestTxs(t *testing.T) {
313 // Reset DB
314 test.WipeDB(historyDB.DB())
315
316 set := `
317 Type: Blockchain
318
319 AddToken(1)
320 AddToken(2)
321 CreateAccountDeposit(1) A: 10
322 CreateAccountDeposit(1) B: 10
323 > batchL1
324 > batchL1
325 > block
326
327 CreateAccountDepositTransfer(1) C-A: 20, 10
328 CreateAccountCoordinator(1) User0
329 > batchL1
330 > batchL1
331 > block
332
333 Deposit(1) B: 10
334 Deposit(1) C: 10
335 Transfer(1) C-A : 10 (1)
336 Transfer(1) B-C : 10 (1)
337 Transfer(1) A-B : 10 (1)
338 Exit(1) A: 10 (1)
339 > batch
340 > block
341
342 DepositTransfer(1) A-B: 10, 10
343 > batchL1
344 > block
345
346 ForceTransfer(1) A-B: 10
347 ForceExit(1) A: 5
348 > batchL1
349 > batchL1
350 > block
351
352 CreateAccountDeposit(2) D: 10
353 > batchL1
354 > block
355
356 CreateAccountDeposit(2) E: 10
357 > batchL1
358 > batchL1
359 > block
360
361 `
362 tc := til.NewContext(common.RollupConstMaxL1UserTx)
363 tilCfgExtra := til.ConfigExtra{
364 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
365 CoordUser: "A",
366 }
367 blocks, err := tc.GenerateBlocks(set)
368 require.NoError(t, err)
369 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
370 require.NoError(t, err)
371
372 // Sanity check
373 require.Equal(t, 7, len(blocks))
374 require.Equal(t, 2, len(blocks[0].Rollup.L1UserTxs))
375 require.Equal(t, 1, len(blocks[1].Rollup.L1UserTxs))
376 require.Equal(t, 2, len(blocks[2].Rollup.L1UserTxs))
377 require.Equal(t, 1, len(blocks[3].Rollup.L1UserTxs))
378 require.Equal(t, 2, len(blocks[4].Rollup.L1UserTxs))
379 require.Equal(t, 1, len(blocks[5].Rollup.L1UserTxs))
380 require.Equal(t, 1, len(blocks[6].Rollup.L1UserTxs))
381
382 var null *common.BatchNum = nil
383 var txID common.TxID
384
385 // Insert blocks into DB
386 for i := range blocks {
387 if i == len(blocks)-1 {
388 blocks[i].Block.Timestamp = time.Now()
389 dbL1Txs, err := historyDB.GetAllL1UserTxs()
390 assert.NoError(t, err)
391 // Check batch_num is nil before forging
392 assert.Equal(t, null, dbL1Txs[len(dbL1Txs)-1].BatchNum)
393 // Save this TxId
394 txID = dbL1Txs[len(dbL1Txs)-1].TxID
395 }
396 err = historyDB.AddBlockSCData(&blocks[i])
397 assert.NoError(t, err)
398 }
399
400 // Check blocks
401 dbBlocks, err := historyDB.GetAllBlocks()
402 assert.NoError(t, err)
403 assert.Equal(t, len(blocks)+1, len(dbBlocks))
404
405 // Check batches
406 batches, err := historyDB.GetAllBatches()
407 assert.NoError(t, err)
408 assert.Equal(t, 11, len(batches))
409
410 // Check L1 Transactions
411 dbL1Txs, err := historyDB.GetAllL1UserTxs()
412 assert.NoError(t, err)
413 assert.Equal(t, 10, len(dbL1Txs))
414
415 // Tx Type
416 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[0].Type)
417 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[1].Type)
418 assert.Equal(t, common.TxTypeCreateAccountDepositTransfer, dbL1Txs[2].Type)
419 assert.Equal(t, common.TxTypeDeposit, dbL1Txs[3].Type)
420 assert.Equal(t, common.TxTypeDeposit, dbL1Txs[4].Type)
421 assert.Equal(t, common.TxTypeDepositTransfer, dbL1Txs[5].Type)
422 assert.Equal(t, common.TxTypeForceTransfer, dbL1Txs[6].Type)
423 assert.Equal(t, common.TxTypeForceExit, dbL1Txs[7].Type)
424 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[8].Type)
425 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[9].Type)
426
427 // Tx ID
428 assert.Equal(t, "0x000000000000000001000000", dbL1Txs[0].TxID.String())
429 assert.Equal(t, "0x000000000000000001000100", dbL1Txs[1].TxID.String())
430 assert.Equal(t, "0x000000000000000003000000", dbL1Txs[2].TxID.String())
431 assert.Equal(t, "0x000000000000000005000000", dbL1Txs[3].TxID.String())
432 assert.Equal(t, "0x000000000000000005000100", dbL1Txs[4].TxID.String())
433 assert.Equal(t, "0x000000000000000005000200", dbL1Txs[5].TxID.String())
434 assert.Equal(t, "0x000000000000000006000000", dbL1Txs[6].TxID.String())
435 assert.Equal(t, "0x000000000000000006000100", dbL1Txs[7].TxID.String())
436 assert.Equal(t, "0x000000000000000008000000", dbL1Txs[8].TxID.String())
437 assert.Equal(t, "0x000000000000000009000000", dbL1Txs[9].TxID.String())
438
439 // Tx From IDx
440 assert.Equal(t, common.Idx(0), dbL1Txs[0].FromIdx)
441 assert.Equal(t, common.Idx(0), dbL1Txs[1].FromIdx)
442 assert.Equal(t, common.Idx(0), dbL1Txs[2].FromIdx)
443 assert.NotEqual(t, common.Idx(0), dbL1Txs[3].FromIdx)
444 assert.NotEqual(t, common.Idx(0), dbL1Txs[4].FromIdx)
445 assert.NotEqual(t, common.Idx(0), dbL1Txs[5].FromIdx)
446 assert.NotEqual(t, common.Idx(0), dbL1Txs[6].FromIdx)
447 assert.NotEqual(t, common.Idx(0), dbL1Txs[7].FromIdx)
448 assert.Equal(t, common.Idx(0), dbL1Txs[8].FromIdx)
449 assert.Equal(t, common.Idx(0), dbL1Txs[9].FromIdx)
450 assert.Equal(t, common.Idx(0), dbL1Txs[9].FromIdx)
451 assert.Equal(t, dbL1Txs[5].FromIdx, dbL1Txs[6].FromIdx)
452 assert.Equal(t, dbL1Txs[5].FromIdx, dbL1Txs[7].FromIdx)
453
454 // Tx to IDx
455 assert.Equal(t, dbL1Txs[2].ToIdx, dbL1Txs[5].FromIdx)
456 assert.Equal(t, dbL1Txs[5].ToIdx, dbL1Txs[3].FromIdx)
457 assert.Equal(t, dbL1Txs[6].ToIdx, dbL1Txs[3].FromIdx)
458
459 // Token ID
460 assert.Equal(t, common.TokenID(1), dbL1Txs[0].TokenID)
461 assert.Equal(t, common.TokenID(1), dbL1Txs[1].TokenID)
462 assert.Equal(t, common.TokenID(1), dbL1Txs[2].TokenID)
463 assert.Equal(t, common.TokenID(1), dbL1Txs[3].TokenID)
464 assert.Equal(t, common.TokenID(1), dbL1Txs[4].TokenID)
465 assert.Equal(t, common.TokenID(1), dbL1Txs[5].TokenID)
466 assert.Equal(t, common.TokenID(1), dbL1Txs[6].TokenID)
467 assert.Equal(t, common.TokenID(1), dbL1Txs[7].TokenID)
468 assert.Equal(t, common.TokenID(2), dbL1Txs[8].TokenID)
469 assert.Equal(t, common.TokenID(2), dbL1Txs[9].TokenID)
470
471 // Batch Number
472 var bn common.BatchNum = common.BatchNum(2)
473
474 assert.Equal(t, &bn, dbL1Txs[0].BatchNum)
475 assert.Equal(t, &bn, dbL1Txs[1].BatchNum)
476
477 bn = common.BatchNum(4)
478 assert.Equal(t, &bn, dbL1Txs[2].BatchNum)
479
480 bn = common.BatchNum(7)
481 assert.Equal(t, &bn, dbL1Txs[3].BatchNum)
482 assert.Equal(t, &bn, dbL1Txs[4].BatchNum)
483 assert.Equal(t, &bn, dbL1Txs[5].BatchNum)
484
485 bn = common.BatchNum(8)
486 assert.Equal(t, &bn, dbL1Txs[6].BatchNum)
487 assert.Equal(t, &bn, dbL1Txs[7].BatchNum)
488
489 bn = common.BatchNum(10)
490 assert.Equal(t, &bn, dbL1Txs[8].BatchNum)
491
492 bn = common.BatchNum(11)
493 assert.Equal(t, &bn, dbL1Txs[9].BatchNum)
494
495 // eth_block_num
496 assert.Equal(t, int64(2), dbL1Txs[0].EthBlockNum)
497 assert.Equal(t, int64(2), dbL1Txs[1].EthBlockNum)
498 assert.Equal(t, int64(3), dbL1Txs[2].EthBlockNum)
499 assert.Equal(t, int64(4), dbL1Txs[3].EthBlockNum)
500 assert.Equal(t, int64(4), dbL1Txs[4].EthBlockNum)
501 assert.Equal(t, int64(5), dbL1Txs[5].EthBlockNum)
502 assert.Equal(t, int64(6), dbL1Txs[6].EthBlockNum)
503 assert.Equal(t, int64(6), dbL1Txs[7].EthBlockNum)
504 assert.Equal(t, int64(7), dbL1Txs[8].EthBlockNum)
505 assert.Equal(t, int64(8), dbL1Txs[9].EthBlockNum)
506
507 // User Origin
508 assert.Equal(t, true, dbL1Txs[0].UserOrigin)
509 assert.Equal(t, true, dbL1Txs[1].UserOrigin)
510 assert.Equal(t, true, dbL1Txs[2].UserOrigin)
511 assert.Equal(t, true, dbL1Txs[3].UserOrigin)
512 assert.Equal(t, true, dbL1Txs[4].UserOrigin)
513 assert.Equal(t, true, dbL1Txs[5].UserOrigin)
514 assert.Equal(t, true, dbL1Txs[6].UserOrigin)
515 assert.Equal(t, true, dbL1Txs[7].UserOrigin)
516 assert.Equal(t, true, dbL1Txs[8].UserOrigin)
517 assert.Equal(t, true, dbL1Txs[9].UserOrigin)
518
519 // Deposit Amount
520 assert.Equal(t, big.NewInt(10), dbL1Txs[0].DepositAmount)
521 assert.Equal(t, big.NewInt(10), dbL1Txs[1].DepositAmount)
522 assert.Equal(t, big.NewInt(20), dbL1Txs[2].DepositAmount)
523 assert.Equal(t, big.NewInt(10), dbL1Txs[3].DepositAmount)
524 assert.Equal(t, big.NewInt(10), dbL1Txs[4].DepositAmount)
525 assert.Equal(t, big.NewInt(10), dbL1Txs[5].DepositAmount)
526 assert.Equal(t, big.NewInt(0), dbL1Txs[6].DepositAmount)
527 assert.Equal(t, big.NewInt(0), dbL1Txs[7].DepositAmount)
528 assert.Equal(t, big.NewInt(10), dbL1Txs[8].DepositAmount)
529 assert.Equal(t, big.NewInt(10), dbL1Txs[9].DepositAmount)
530
531 // Check saved txID's batch_num is not nil
532 assert.Equal(t, txID, dbL1Txs[len(dbL1Txs)-2].TxID)
533 assert.NotEqual(t, null, dbL1Txs[len(dbL1Txs)-2].BatchNum)
534
535 // Check Coordinator TXs
536 coordTxs, err := historyDB.GetAllL1CoordinatorTxs()
537 assert.NoError(t, err)
538 assert.Equal(t, 1, len(coordTxs))
539 assert.Equal(t, common.TxTypeCreateAccountDeposit, coordTxs[0].Type)
540 assert.Equal(t, false, coordTxs[0].UserOrigin)
541
542 // Check L2 TXs
543 dbL2Txs, err := historyDB.GetAllL2Txs()
544 assert.NoError(t, err)
545 assert.Equal(t, 4, len(dbL2Txs))
546
547 // Tx Type
548 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[0].Type)
549 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[1].Type)
550 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[2].Type)
551 assert.Equal(t, common.TxTypeExit, dbL2Txs[3].Type)
552
553 // Tx ID
554 assert.Equal(t, "0x020000000001030000000001", dbL2Txs[0].TxID.String())
555 assert.Equal(t, "0x020000000001010000000001", dbL2Txs[1].TxID.String())
556 assert.Equal(t, "0x020000000001000000000001", dbL2Txs[2].TxID.String())
557 assert.Equal(t, "0x020000000001000000000002", dbL2Txs[3].TxID.String())
558
559 // Tx From and To IDx
560 assert.Equal(t, dbL2Txs[0].ToIdx, dbL2Txs[2].FromIdx)
561 assert.Equal(t, dbL2Txs[1].ToIdx, dbL2Txs[0].FromIdx)
562 assert.Equal(t, dbL2Txs[2].ToIdx, dbL2Txs[1].FromIdx)
563
564 // Batch Number
565 assert.Equal(t, common.BatchNum(5), dbL2Txs[0].BatchNum)
566 assert.Equal(t, common.BatchNum(5), dbL2Txs[1].BatchNum)
567 assert.Equal(t, common.BatchNum(5), dbL2Txs[2].BatchNum)
568 assert.Equal(t, common.BatchNum(5), dbL2Txs[3].BatchNum)
569
570 // eth_block_num
571 assert.Equal(t, int64(4), dbL2Txs[0].EthBlockNum)
572 assert.Equal(t, int64(4), dbL2Txs[1].EthBlockNum)
573 assert.Equal(t, int64(4), dbL2Txs[2].EthBlockNum)
574
575 // Amount
576 assert.Equal(t, big.NewInt(10), dbL2Txs[0].Amount)
577 assert.Equal(t, big.NewInt(10), dbL2Txs[1].Amount)
578 assert.Equal(t, big.NewInt(10), dbL2Txs[2].Amount)
579 assert.Equal(t, big.NewInt(10), dbL2Txs[3].Amount)
580}
581
582func TestExitTree(t *testing.T) {
583 nBatches := 17
584 blocks := setTestBlocks(1, 10)
585 batches := test.GenBatches(nBatches, blocks)
586 err := historyDB.AddBatches(batches)
587 assert.NoError(t, err)
588 const nTokens = 50
589 tokens, ethToken := test.GenTokens(nTokens, blocks)
590 err = historyDB.AddTokens(tokens)
591 assert.NoError(t, err)
592 tokens = append([]common.Token{ethToken}, tokens...)
593 const nAccounts = 3
594 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
595 assert.NoError(t, historyDB.AddAccounts(accs))
596 exitTree := test.GenExitTree(nBatches, batches, accs, blocks)
597 err = historyDB.AddExitTree(exitTree)
598 assert.NoError(t, err)
599}
600
601func TestGetUnforgedL1UserTxs(t *testing.T) {
602 test.WipeDB(historyDB.DB())
603
604 set := `
605 Type: Blockchain
606 AddToken(1)
607 AddToken(2)
608 AddToken(3)
609
610 CreateAccountDeposit(1) A: 20
611 CreateAccountDeposit(2) A: 20
612 CreateAccountDeposit(1) B: 5
613 CreateAccountDeposit(1) C: 5
614 CreateAccountDeposit(1) D: 5
615
616 > block
617 `
618 tc := til.NewContext(128)
619 blocks, err := tc.GenerateBlocks(set)
620 require.NoError(t, err)
621 // Sanity check
622 require.Equal(t, 1, len(blocks))
623 require.Equal(t, 5, len(blocks[0].Rollup.L1UserTxs))
624
625 toForgeL1TxsNum := int64(1)
626
627 for i := range blocks {
628 err = historyDB.AddBlockSCData(&blocks[i])
629 require.NoError(t, err)
630 }
631
632 l1UserTxs, err := historyDB.GetUnforgedL1UserTxs(toForgeL1TxsNum)
633 require.NoError(t, err)
634 assert.Equal(t, 5, len(l1UserTxs))
635 assert.Equal(t, blocks[0].Rollup.L1UserTxs, l1UserTxs)
636
637 // No l1UserTxs for this toForgeL1TxsNum
638 l1UserTxs, err = historyDB.GetUnforgedL1UserTxs(2)
639 require.NoError(t, err)
640 assert.Equal(t, 0, len(l1UserTxs))
641}
642
643func exampleInitSCVars() (*common.RollupVariables, *common.AuctionVariables, *common.WDelayerVariables) {
644 //nolint:govet
645 rollup := &common.RollupVariables{
646 0,
647 big.NewInt(10),
648 12,
649 13,
650 [5]common.BucketParams{},
651 false,
652 }
653 //nolint:govet
654 auction := &common.AuctionVariables{
655 0,
656 ethCommon.BigToAddress(big.NewInt(2)),
657 ethCommon.BigToAddress(big.NewInt(3)),
658 "https://boot.coord.com",
659 [6]*big.Int{
660 big.NewInt(1), big.NewInt(2), big.NewInt(3),
661 big.NewInt(4), big.NewInt(5), big.NewInt(6),
662 },
663 0,
664 2,
665 4320,
666 [3]uint16{10, 11, 12},
667 1000,
668 20,
669 }
670 //nolint:govet
671 wDelayer := &common.WDelayerVariables{
672 0,
673 ethCommon.BigToAddress(big.NewInt(2)),
674 ethCommon.BigToAddress(big.NewInt(3)),
675 13,
676 14,
677 false,
678 }
679 return rollup, auction, wDelayer
680}
681
682func TestSetInitialSCVars(t *testing.T) {
683 test.WipeDB(historyDB.DB())
684 _, _, _, err := historyDB.GetSCVars()
685 assert.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
686 rollup, auction, wDelayer := exampleInitSCVars()
687 err = historyDB.SetInitialSCVars(rollup, auction, wDelayer)
688 require.NoError(t, err)
689 dbRollup, dbAuction, dbWDelayer, err := historyDB.GetSCVars()
690 require.NoError(t, err)
691 require.Equal(t, rollup, dbRollup)
692 require.Equal(t, auction, dbAuction)
693 require.Equal(t, wDelayer, dbWDelayer)
694}
695
696func TestSetL1UserTxEffectiveAmounts(t *testing.T) {
697 test.WipeDB(historyDB.DB())
698
699 set := `
700 Type: Blockchain
701
702 AddToken(1)
703
704 CreateAccountDeposit(1) A: 2000
705 CreateAccountDeposit(1) B: 500
706 CreateAccountDeposit(1) C: 500
707
708 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{*}
709 > block // blockNum=2
710
711 > batchL1 // forge defined L1UserTxs{*}
712 > block // blockNum=3
713 `
714
715 tc := til.NewContext(common.RollupConstMaxL1UserTx)
716 tilCfgExtra := til.ConfigExtra{
717 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
718 CoordUser: "A",
719 }
720 blocks, err := tc.GenerateBlocks(set)
721 require.NoError(t, err)
722 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
723 require.NoError(t, err)
724 err = tc.FillBlocksForgedL1UserTxs(blocks)
725 require.NoError(t, err)
726
727 // Add only first block so that the L1UserTxs are not marked as forged
728 for i := range blocks[:1] {
729 err = historyDB.AddBlockSCData(&blocks[i])
730 require.NoError(t, err)
731 }
732 // Add second batch to trigger the update of the batch_num,
733 // while avoiding the implicit call of setL1UserTxEffectiveAmounts
734 err = historyDB.addBlock(historyDB.db, &blocks[1].Block)
735 assert.NoError(t, err)
736 err = historyDB.addBatch(historyDB.db, &blocks[1].Rollup.Batches[0].Batch)
737 assert.NoError(t, err)
738 require.NoError(t, err)
739
740 // Set the Effective{Amount,DepositAmount} of the L1UserTxs that are forged in the second block
741 l1Txs := blocks[1].Rollup.Batches[0].L1UserTxs
742 require.Equal(t, 3, len(l1Txs))
743 // Change some values to test all cases
744 l1Txs[1].EffectiveAmount = big.NewInt(0)
745 l1Txs[2].EffectiveDepositAmount = big.NewInt(0)
746 l1Txs[2].EffectiveAmount = big.NewInt(0)
747 err = historyDB.setL1UserTxEffectiveAmounts(historyDB.db, l1Txs)
748 require.NoError(t, err)
749
750 dbL1Txs, err := historyDB.GetAllL1UserTxs()
751 require.NoError(t, err)
752 for i, tx := range dbL1Txs {
753 log.Infof("%d %v %v", i, tx.EffectiveAmount, tx.EffectiveDepositAmount)
754 assert.NotNil(t, tx.EffectiveAmount)
755 assert.NotNil(t, tx.EffectiveDepositAmount)
756 switch tx.TxID {
757 case l1Txs[0].TxID:
758 assert.Equal(t, l1Txs[0].DepositAmount, tx.EffectiveDepositAmount)
759 assert.Equal(t, l1Txs[0].Amount, tx.EffectiveAmount)
760 case l1Txs[1].TxID:
761 assert.Equal(t, l1Txs[1].DepositAmount, tx.EffectiveDepositAmount)
762 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
763 case l1Txs[2].TxID:
764 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
765 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
766 }
767 }
768}
769
770func TestUpdateExitTree(t *testing.T) {
771 test.WipeDB(historyDB.DB())
772
773 set := `
774 Type: Blockchain
775
776 AddToken(1)
777
778 CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
779 CreateAccountDeposit(1) D: 500 // Idx=256+3=259
780
781 CreateAccountCoordinator(1) A // Idx=256+0=256
782 CreateAccountCoordinator(1) B // Idx=256+1=257
783
784 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
785 > batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
786 > block // blockNum=2
787
788 ForceExit(1) A: 100
789 ForceExit(1) B: 80
790
791 Exit(1) C: 50 (172)
792 Exit(1) D: 30 (172)
793
794 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
795 > batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
796 > block // blockNum=3
797
798 > block // blockNum=4 (empty block)
799 > block // blockNum=5 (empty block)
800 `
801
802 tc := til.NewContext(common.RollupConstMaxL1UserTx)
803 tilCfgExtra := til.ConfigExtra{
804 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
805 CoordUser: "A",
806 }
807 blocks, err := tc.GenerateBlocks(set)
808 require.NoError(t, err)
809 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
810 require.NoError(t, err)
811
812 // Add all blocks except for the last two
813 for i := range blocks[:len(blocks)-2] {
814 err = historyDB.AddBlockSCData(&blocks[i])
815 require.NoError(t, err)
816 }
817
818 // Add withdraws to the second-to-last block, and insert block into the DB
819 block := &blocks[len(blocks)-2]
820 require.Equal(t, int64(4), block.Block.Num)
821 tokenAddr := blocks[0].Rollup.AddedTokens[0].EthAddr
822 // block.WDelayer.Deposits = append(block.WDelayer.Deposits,
823 // common.WDelayerTransfer{Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr, Amount: big.NewInt(80)}, // 257
824 // common.WDelayerTransfer{Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr, Amount: big.NewInt(15)}, // 259
825 // )
826 block.Rollup.Withdrawals = append(block.Rollup.Withdrawals,
827 common.WithdrawInfo{Idx: 256, NumExitRoot: 4, InstantWithdraw: true},
828 common.WithdrawInfo{Idx: 257, NumExitRoot: 4, InstantWithdraw: false,
829 Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr},
830 common.WithdrawInfo{Idx: 258, NumExitRoot: 3, InstantWithdraw: true},
831 common.WithdrawInfo{Idx: 259, NumExitRoot: 3, InstantWithdraw: false,
832 Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr},
833 )
834 err = historyDB.addBlock(historyDB.db, &block.Block)
835 require.NoError(t, err)
836
837 err = historyDB.updateExitTree(historyDB.db, block.Block.Num,
838 block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
839 require.NoError(t, err)
840
841 // Check that exits in DB match with the expected values
842 dbExits, err := historyDB.GetAllExits()
843 require.NoError(t, err)
844 assert.Equal(t, 4, len(dbExits))
845 dbExitsByIdx := make(map[common.Idx]common.ExitInfo)
846 for _, dbExit := range dbExits {
847 dbExitsByIdx[dbExit.AccountIdx] = dbExit
848 }
849 for _, withdraw := range block.Rollup.Withdrawals {
850 assert.Equal(t, withdraw.NumExitRoot, dbExitsByIdx[withdraw.Idx].BatchNum)
851 if withdraw.InstantWithdraw {
852 assert.Equal(t, &block.Block.Num, dbExitsByIdx[withdraw.Idx].InstantWithdrawn)
853 } else {
854 assert.Equal(t, &block.Block.Num, dbExitsByIdx[withdraw.Idx].DelayedWithdrawRequest)
855 }
856 }
857
858 // Add delayed withdraw to the last block, and insert block into the DB
859 block = &blocks[len(blocks)-1]
860 require.Equal(t, int64(5), block.Block.Num)
861 block.WDelayer.Withdrawals = append(block.WDelayer.Withdrawals,
862 common.WDelayerTransfer{
863 Owner: tc.UsersByIdx[257].Addr,
864 Token: tokenAddr,
865 Amount: big.NewInt(80),
866 })
867 err = historyDB.addBlock(historyDB.db, &block.Block)
868 require.NoError(t, err)
869
870 err = historyDB.updateExitTree(historyDB.db, block.Block.Num,
871 block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
872 require.NoError(t, err)
873
874 // Check that delayed withdrawn has been set
875 dbExits, err = historyDB.GetAllExits()
876 require.NoError(t, err)
877 for _, dbExit := range dbExits {
878 dbExitsByIdx[dbExit.AccountIdx] = dbExit
879 }
880 require.Equal(t, &block.Block.Num, dbExitsByIdx[257].DelayedWithdrawn)
881}
882
883func TestGetBestBidCoordinator(t *testing.T) {
884 test.WipeDB(historyDB.DB())
885
886 rollup, auction, wDelayer := exampleInitSCVars()
887 err := historyDB.SetInitialSCVars(rollup, auction, wDelayer)
888 require.NoError(t, err)
889
890 tc := til.NewContext(common.RollupConstMaxL1UserTx)
891 blocks, err := tc.GenerateBlocks(`
892 Type: Blockchain
893 > block // blockNum=2
894 `)
895 require.NoError(t, err)
896 err = historyDB.AddBlockSCData(&blocks[0])
897 require.NoError(t, err)
898
899 coords := []common.Coordinator{
900 {
901 Bidder: ethCommon.BigToAddress(big.NewInt(1)),
902 Forger: ethCommon.BigToAddress(big.NewInt(2)),
903 EthBlockNum: 2,
904 URL: "foo",
905 },
906 {
907 Bidder: ethCommon.BigToAddress(big.NewInt(3)),
908 Forger: ethCommon.BigToAddress(big.NewInt(4)),
909 EthBlockNum: 2,
910 URL: "bar",
911 },
912 }
913 err = historyDB.addCoordinators(historyDB.db, coords)
914 require.NoError(t, err)
915
916 bids := []common.Bid{
917 {
918 SlotNum: 10,
919 BidValue: big.NewInt(10),
920 EthBlockNum: 2,
921 Bidder: coords[0].Bidder,
922 },
923 {
924 SlotNum: 10,
925 BidValue: big.NewInt(20),
926 EthBlockNum: 2,
927 Bidder: coords[1].Bidder,
928 },
929 }
930
931 err = historyDB.addBids(historyDB.db, bids)
932 require.NoError(t, err)
933
934 forger10, err := historyDB.GetBestBidCoordinator(10)
935 require.NoError(t, err)
936 require.Equal(t, coords[1].Forger, forger10.Forger)
937 require.Equal(t, coords[1].Bidder, forger10.Bidder)
938 require.Equal(t, coords[1].URL, forger10.URL)
939 require.Equal(t, bids[1].SlotNum, forger10.SlotNum)
940 require.Equal(t, bids[1].BidValue, forger10.BidValue)
941 for i := range forger10.DefaultSlotSetBid {
942 require.Equal(t, auction.DefaultSlotSetBid[i], forger10.DefaultSlotSetBid[i])
943 }
944
945 _, err = historyDB.GetBestBidCoordinator(11)
946 require.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
947}
948
949func TestAddBucketUpdates(t *testing.T) {
950 test.WipeDB(historyDB.DB())
951 const fromBlock int64 = 1
952 const toBlock int64 = 5 + 1
953 setTestBlocks(fromBlock, toBlock)
954
955 bucketUpdates := []common.BucketUpdate{
956 {
957 EthBlockNum: 4,
958 NumBucket: 0,
959 BlockStamp: 4,
960 Withdrawals: big.NewInt(123),
961 },
962 {
963 EthBlockNum: 5,
964 NumBucket: 2,
965 BlockStamp: 5,
966 Withdrawals: big.NewInt(42),
967 },
968 }
969 err := historyDB.addBucketUpdates(historyDB.db, bucketUpdates)
970 require.NoError(t, err)
971 dbBucketUpdates, err := historyDB.GetAllBucketUpdates()
972 require.NoError(t, err)
973 assert.Equal(t, bucketUpdates, dbBucketUpdates)
974}
975
976func TestAddTokenExchanges(t *testing.T) {
977 test.WipeDB(historyDB.DB())
978 const fromBlock int64 = 1
979 const toBlock int64 = 5 + 1
980 setTestBlocks(fromBlock, toBlock)
981
982 tokenExchanges := []common.TokenExchange{
983 {
984 EthBlockNum: 4,
985 Address: ethCommon.BigToAddress(big.NewInt(111)),
986 ValueUSD: 12345,
987 },
988 {
989 EthBlockNum: 5,
990 Address: ethCommon.BigToAddress(big.NewInt(222)),
991 ValueUSD: 67890,
992 },
993 }
994 err := historyDB.addTokenExchanges(historyDB.db, tokenExchanges)
995 require.NoError(t, err)
996 dbTokenExchanges, err := historyDB.GetAllTokenExchanges()
997 require.NoError(t, err)
998 assert.Equal(t, tokenExchanges, dbTokenExchanges)
999}
1000
1001func TestAddEscapeHatchWithdrawals(t *testing.T) {
1002 test.WipeDB(historyDB.DB())
1003 const fromBlock int64 = 1
1004 const toBlock int64 = 5 + 1
1005 setTestBlocks(fromBlock, toBlock)
1006
1007 escapeHatchWithdrawals := []common.WDelayerEscapeHatchWithdrawal{
1008 {
1009 EthBlockNum: 4,
1010 Who: ethCommon.BigToAddress(big.NewInt(111)),
1011 To: ethCommon.BigToAddress(big.NewInt(222)),
1012 TokenAddr: ethCommon.BigToAddress(big.NewInt(333)),
1013 Amount: big.NewInt(10002),
1014 },
1015 {
1016 EthBlockNum: 5,
1017 Who: ethCommon.BigToAddress(big.NewInt(444)),
1018 To: ethCommon.BigToAddress(big.NewInt(555)),
1019 TokenAddr: ethCommon.BigToAddress(big.NewInt(666)),
1020 Amount: big.NewInt(20003),
1021 },
1022 }
1023 err := historyDB.addEscapeHatchWithdrawals(historyDB.db, escapeHatchWithdrawals)
1024 require.NoError(t, err)
1025 dbEscapeHatchWithdrawals, err := historyDB.GetAllEscapeHatchWithdrawals()
1026 require.NoError(t, err)
1027 assert.Equal(t, escapeHatchWithdrawals, dbEscapeHatchWithdrawals)
1028}
1029
1030func TestGetMetricsEmpty(t *testing.T) {
1031 test.WipeDB(historyDB.DB())
1032 _, err := historyDB.GetMetrics(0)
1033 assert.NoError(t, err)
1034}
1035
1036func TestGetAvgTxFeeEmpty(t *testing.T) {
1037 test.WipeDB(historyDB.DB())
1038 _, err := historyDB.GetAvgTxFee()
1039 assert.NoError(t, err)
1040}
1041
1042func TestGetLastL1TxsNum(t *testing.T) {
1043 test.WipeDB(historyDB.DB())
1044 _, err := historyDB.GetLastL1TxsNum()
1045 assert.NoError(t, err)
1046}
1047
1048func TestGetLastTxsPosition(t *testing.T) {
1049 test.WipeDB(historyDB.DB())
1050 _, err := historyDB.GetLastTxsPosition(0)
1051 assert.Equal(t, sql.ErrNoRows.Error(), err.Error())
1052}
1053
1054// setTestBlocks WARNING: this will delete the blocks and recreate them
1055func setTestBlocks(from, to int64) []common.Block {
1056 test.WipeDB(historyDB.DB())
1057 blocks := test.GenBlocks(from, to)
1058 if err := historyDB.AddBlocks(blocks); err != nil {
1059 panic(err)
1060 }
1061 return blocks
1062}