hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
1package historydb
2
3import (
4 "database/sql"
5 "fmt"
6 "math"
7 "math/big"
8 "os"
9 "strings"
10 "testing"
11 "time"
12
13 ethCommon "github.com/ethereum/go-ethereum/common"
14 "github.com/hermeznetwork/hermez-node/common"
15 dbUtils "github.com/hermeznetwork/hermez-node/db"
16 "github.com/hermeznetwork/hermez-node/log"
17 "github.com/hermeznetwork/hermez-node/test"
18 "github.com/hermeznetwork/hermez-node/test/til"
19 "github.com/hermeznetwork/tracerr"
20 "github.com/stretchr/testify/assert"
21 "github.com/stretchr/testify/require"
22)
23
24var historyDB *HistoryDB
25
26// In order to run the test you need to run a Posgres DB with
27// a database named "history" that is accessible by
28// user: "hermez"
29// pass: set it using the env var POSTGRES_PASS
30// 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;"
31// After running the test you can stop the container by running: sudo docker kill hermez-db-test
32// If you already did that for the L2DB you don't have to do it again
33
34func TestMain(m *testing.M) {
35 // init DB
36 pass := os.Getenv("POSTGRES_PASS")
37 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
38 if err != nil {
39 panic(err)
40 }
41 historyDB = NewHistoryDB(db)
42 if err != nil {
43 panic(err)
44 }
45 // Run tests
46 result := m.Run()
47 // Close DB
48 if err := db.Close(); err != nil {
49 log.Error("Error closing the history DB:", err)
50 }
51 os.Exit(result)
52}
53
54func TestBlocks(t *testing.T) {
55 var fromBlock, toBlock int64
56 fromBlock = 0
57 toBlock = 7
58 // Reset DB
59 test.WipeDB(historyDB.DB())
60 // Generate blocks using til
61 set1 := `
62 Type: Blockchain
63 // block 0 is stored as default in the DB
64 // block 1 does not exist
65 > block // blockNum=2
66 > block // blockNum=3
67 > block // blockNum=4
68 > block // blockNum=5
69 > block // blockNum=6
70 `
71 tc := til.NewContext(uint16(0), 1)
72 blocks, err := tc.GenerateBlocks(set1)
73 require.NoError(t, err)
74 // Save timestamp of a block with UTC and change it without UTC
75 timestamp := time.Now().Add(time.Second * 13)
76 blocks[fromBlock].Block.Timestamp = timestamp
77 // Insert blocks into DB
78 for i := 0; i < len(blocks); i++ {
79 err := historyDB.AddBlock(&blocks[i].Block)
80 assert.NoError(t, err)
81 }
82 // Add block 0 to the generated blocks
83 blocks = append(
84 []common.BlockData{{Block: test.Block0}}, //nolint:gofmt
85 blocks...,
86 )
87 // Get all blocks from DB
88 fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
89 assert.Equal(t, len(blocks), len(fetchedBlocks))
90 // Compare generated vs getted blocks
91 assert.NoError(t, err)
92 for i := range fetchedBlocks {
93 assertEqualBlock(t, &blocks[i].Block, &fetchedBlocks[i])
94 }
95 // Compare saved timestamp vs getted
96 nameZoneUTC, offsetUTC := timestamp.UTC().Zone()
97 zoneFetchedBlock, offsetFetchedBlock := fetchedBlocks[fromBlock].Timestamp.Zone()
98 assert.Equal(t, nameZoneUTC, zoneFetchedBlock)
99 assert.Equal(t, offsetUTC, offsetFetchedBlock)
100 // Get blocks from the DB one by one
101 for i := int64(2); i < toBlock; i++ { // avoid block 0 for simplicity
102 fetchedBlock, err := historyDB.GetBlock(i)
103 assert.NoError(t, err)
104 assertEqualBlock(t, &blocks[i-1].Block, fetchedBlock)
105 }
106 // Get last block
107 lastBlock, err := historyDB.GetLastBlock()
108 assert.NoError(t, err)
109 assertEqualBlock(t, &blocks[len(blocks)-1].Block, lastBlock)
110}
111
112func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
113 assert.Equal(t, expected.Num, actual.Num)
114 assert.Equal(t, expected.Hash, actual.Hash)
115 assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
116}
117
118func TestBatches(t *testing.T) {
119 // Reset DB
120 test.WipeDB(historyDB.DB())
121 // Generate batches using til (and blocks for foreign key)
122 set := `
123 Type: Blockchain
124
125 AddToken(1) // Will have value in USD
126 AddToken(2) // Will NOT have value in USD
127 CreateAccountDeposit(1) A: 2000
128 CreateAccountDeposit(2) A: 2000
129 CreateAccountDeposit(1) B: 1000
130 CreateAccountDeposit(2) B: 1000
131 > batchL1
132 > batchL1
133 Transfer(1) A-B: 100 (5)
134 Transfer(2) B-A: 100 (199)
135 > batch // batchNum=2, L2 only batch, forges transfers (mixed case of with(out) USD value)
136 > block
137 Transfer(1) A-B: 100 (5)
138 > batch // batchNum=3, L2 only batch, forges transfer (with USD value)
139 Transfer(2) B-A: 100 (199)
140 > batch // batchNum=4, L2 only batch, forges transfer (without USD value)
141 > block
142 `
143 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
144 tilCfgExtra := til.ConfigExtra{
145 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
146 CoordUser: "A",
147 }
148 blocks, err := tc.GenerateBlocks(set)
149 require.NoError(t, err)
150 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
151 require.NoError(t, err)
152 // Insert to DB
153 batches := []common.Batch{}
154 tokensValue := make(map[common.TokenID]float64)
155 lastL1TxsNum := new(int64)
156 lastL1BatchBlockNum := int64(0)
157 for _, block := range blocks {
158 // Insert block
159 assert.NoError(t, historyDB.AddBlock(&block.Block))
160 // Insert tokens
161 for i, token := range block.Rollup.AddedTokens {
162 assert.NoError(t, historyDB.AddToken(&token)) //nolint:gosec
163 if i%2 != 0 {
164 // Set value to the token
165 value := (float64(i) + 5) * 5.389329
166 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
167 tokensValue[token.TokenID] = value / math.Pow(10, float64(token.Decimals))
168 }
169 }
170 // Combine all generated batches into single array
171 for _, batch := range block.Rollup.Batches {
172 batches = append(batches, batch.Batch)
173 forgeTxsNum := batch.Batch.ForgeL1TxsNum
174 if forgeTxsNum != nil && (lastL1TxsNum == nil || *lastL1TxsNum < *forgeTxsNum) {
175 *lastL1TxsNum = *forgeTxsNum
176 lastL1BatchBlockNum = batch.Batch.EthBlockNum
177 }
178 }
179 }
180 // Insert batches
181 assert.NoError(t, historyDB.AddBatches(batches))
182 // Set expected total fee
183 for _, batch := range batches {
184 total := .0
185 for tokenID, amount := range batch.CollectedFees {
186 af := new(big.Float).SetInt(amount)
187 amountFloat, _ := af.Float64()
188 total += tokensValue[tokenID] * amountFloat
189 }
190 batch.TotalFeesUSD = &total
191 }
192 // Get batches from the DB
193 fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(len(batches)+1))
194 assert.NoError(t, err)
195 assert.Equal(t, len(batches), len(fetchedBatches))
196 for i, fetchedBatch := range fetchedBatches {
197 assert.Equal(t, batches[i], fetchedBatch)
198 }
199 // Test GetLastBatchNum
200 fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
201 assert.NoError(t, err)
202 assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
203 // Test GetLastL1TxsNum
204 fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
205 assert.NoError(t, err)
206 assert.Equal(t, lastL1TxsNum, fetchedLastL1TxsNum)
207 // Test GetLastL1BatchBlockNum
208 fetchedLastL1BatchBlockNum, err := historyDB.GetLastL1BatchBlockNum()
209 assert.NoError(t, err)
210 assert.Equal(t, lastL1BatchBlockNum, fetchedLastL1BatchBlockNum)
211}
212
213func TestBids(t *testing.T) {
214 const fromBlock int64 = 1
215 const toBlock int64 = 5
216 // Prepare blocks in the DB
217 blocks := setTestBlocks(fromBlock, toBlock)
218 // Generate fake coordinators
219 const nCoords = 5
220 coords := test.GenCoordinators(nCoords, blocks)
221 err := historyDB.AddCoordinators(coords)
222 assert.NoError(t, err)
223 // Generate fake bids
224 const nBids = 20
225 bids := test.GenBids(nBids, blocks, coords)
226 err = historyDB.AddBids(bids)
227 assert.NoError(t, err)
228 // Fetch bids
229 fetchedBids, err := historyDB.GetAllBids()
230 assert.NoError(t, err)
231 // Compare fetched bids vs generated bids
232 for i, bid := range fetchedBids {
233 assert.Equal(t, bids[i], bid)
234 }
235}
236
237func TestTokens(t *testing.T) {
238 const fromBlock int64 = 1
239 const toBlock int64 = 5
240 // Prepare blocks in the DB
241 blocks := setTestBlocks(fromBlock, toBlock)
242 // Generate fake tokens
243 const nTokens = 5
244 tokens, ethToken := test.GenTokens(nTokens, blocks)
245 err := historyDB.AddTokens(tokens)
246 assert.NoError(t, err)
247 tokens = append([]common.Token{ethToken}, tokens...)
248 limit := uint(10)
249 // Fetch tokens
250 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
251 assert.NoError(t, err)
252 // Compare fetched tokens vs generated tokens
253 // All the tokens should have USDUpdate setted by the DB trigger
254 for i, token := range fetchedTokens {
255 assert.Equal(t, tokens[i].TokenID, token.TokenID)
256 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
257 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
258 assert.Equal(t, tokens[i].Name, token.Name)
259 assert.Equal(t, tokens[i].Symbol, token.Symbol)
260 assert.Nil(t, token.USD)
261 assert.Nil(t, token.USDUpdate)
262 }
263
264 // Update token value
265 for i, token := range tokens {
266 value := 1.01 * float64(i)
267 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
268 }
269 // Fetch tokens
270 fetchedTokens, _, err = historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
271 assert.NoError(t, err)
272 // Compare fetched tokens vs generated tokens
273 // All the tokens should have USDUpdate setted by the DB trigger
274 for i, token := range fetchedTokens {
275 value := 1.01 * float64(i)
276 assert.Equal(t, value, *token.USD)
277 nameZone, offset := token.USDUpdate.Zone()
278 assert.Equal(t, "UTC", nameZone)
279 assert.Equal(t, 0, offset)
280 }
281}
282
283func TestTokensUTF8(t *testing.T) {
284 // Reset DB
285 test.WipeDB(historyDB.DB())
286 const fromBlock int64 = 1
287 const toBlock int64 = 5
288 // Prepare blocks in the DB
289 blocks := setTestBlocks(fromBlock, toBlock)
290 // Generate fake tokens
291 const nTokens = 5
292 tokens, ethToken := test.GenTokens(nTokens, blocks)
293 nonUTFTokens := make([]common.Token, len(tokens)+1)
294 // Force token.name and token.symbol to be non UTF-8 Strings
295 for i, token := range tokens {
296 token.Name = fmt.Sprint("NON-UTF8-NAME-\xc5-", i)
297 token.Symbol = fmt.Sprint("S-\xc5-", i)
298 tokens[i] = token
299 nonUTFTokens[i] = token
300 }
301 err := historyDB.AddTokens(tokens)
302 assert.NoError(t, err)
303 // Work with nonUTFTokens as tokens one gets updated and non UTF-8 characters are lost
304 nonUTFTokens = append([]common.Token{ethToken}, nonUTFTokens...)
305 limit := uint(10)
306 // Fetch tokens
307 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
308 assert.NoError(t, err)
309 // Compare fetched tokens vs generated tokens
310 // All the tokens should have USDUpdate setted by the DB trigger
311 for i, token := range fetchedTokens {
312 assert.Equal(t, nonUTFTokens[i].TokenID, token.TokenID)
313 assert.Equal(t, nonUTFTokens[i].EthBlockNum, token.EthBlockNum)
314 assert.Equal(t, nonUTFTokens[i].EthAddr, token.EthAddr)
315 assert.Equal(t, strings.ToValidUTF8(nonUTFTokens[i].Name, " "), token.Name)
316 assert.Equal(t, strings.ToValidUTF8(nonUTFTokens[i].Symbol, " "), token.Symbol)
317 assert.Nil(t, token.USD)
318 assert.Nil(t, token.USDUpdate)
319 }
320
321 // Update token value
322 for i, token := range nonUTFTokens {
323 value := 1.01 * float64(i)
324 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
325 }
326 // Fetch tokens
327 fetchedTokens, _, err = historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
328 assert.NoError(t, err)
329 // Compare fetched tokens vs generated tokens
330 // All the tokens should have USDUpdate setted by the DB trigger
331 for i, token := range fetchedTokens {
332 value := 1.01 * float64(i)
333 assert.Equal(t, value, *token.USD)
334 nameZone, offset := token.USDUpdate.Zone()
335 assert.Equal(t, "UTC", nameZone)
336 assert.Equal(t, 0, offset)
337 }
338}
339
340func TestAccounts(t *testing.T) {
341 const fromBlock int64 = 1
342 const toBlock int64 = 5
343 // Prepare blocks in the DB
344 blocks := setTestBlocks(fromBlock, toBlock)
345 // Generate fake tokens
346 const nTokens = 5
347 tokens, ethToken := test.GenTokens(nTokens, blocks)
348 err := historyDB.AddTokens(tokens)
349 assert.NoError(t, err)
350 tokens = append([]common.Token{ethToken}, tokens...)
351 // Generate fake batches
352 const nBatches = 10
353 batches := test.GenBatches(nBatches, blocks)
354 err = historyDB.AddBatches(batches)
355 assert.NoError(t, err)
356 // Generate fake accounts
357 const nAccounts = 3
358 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
359 err = historyDB.AddAccounts(accs)
360 assert.NoError(t, err)
361 // Fetch accounts
362 fetchedAccs, err := historyDB.GetAllAccounts()
363 assert.NoError(t, err)
364 // Compare fetched accounts vs generated accounts
365 for i, acc := range fetchedAccs {
366 accs[i].Balance = nil
367 assert.Equal(t, accs[i], acc)
368 }
369}
370
371func TestTxs(t *testing.T) {
372 // Reset DB
373 test.WipeDB(historyDB.DB())
374
375 set := `
376 Type: Blockchain
377
378 AddToken(1)
379 AddToken(2)
380 CreateAccountDeposit(1) A: 10
381 CreateAccountDeposit(1) B: 10
382 > batchL1
383 > batchL1
384 > block
385
386 CreateAccountDepositTransfer(1) C-A: 20, 10
387 CreateAccountCoordinator(1) User0
388 > batchL1
389 > batchL1
390 > block
391
392 Deposit(1) B: 10
393 Deposit(1) C: 10
394 Transfer(1) C-A : 10 (1)
395 Transfer(1) B-C : 10 (1)
396 Transfer(1) A-B : 10 (1)
397 Exit(1) A: 10 (1)
398 > batch
399 > block
400
401 DepositTransfer(1) A-B: 10, 10
402 > batchL1
403 > block
404
405 ForceTransfer(1) A-B: 10
406 ForceExit(1) A: 5
407 > batchL1
408 > batchL1
409 > block
410
411 CreateAccountDeposit(2) D: 10
412 > batchL1
413 > block
414
415 CreateAccountDeposit(2) E: 10
416 > batchL1
417 > batchL1
418 > block
419
420 `
421 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
422 tilCfgExtra := til.ConfigExtra{
423 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
424 CoordUser: "A",
425 }
426 blocks, err := tc.GenerateBlocks(set)
427 require.NoError(t, err)
428 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
429 require.NoError(t, err)
430
431 // Sanity check
432 require.Equal(t, 7, len(blocks))
433 require.Equal(t, 2, len(blocks[0].Rollup.L1UserTxs))
434 require.Equal(t, 1, len(blocks[1].Rollup.L1UserTxs))
435 require.Equal(t, 2, len(blocks[2].Rollup.L1UserTxs))
436 require.Equal(t, 1, len(blocks[3].Rollup.L1UserTxs))
437 require.Equal(t, 2, len(blocks[4].Rollup.L1UserTxs))
438 require.Equal(t, 1, len(blocks[5].Rollup.L1UserTxs))
439 require.Equal(t, 1, len(blocks[6].Rollup.L1UserTxs))
440
441 var null *common.BatchNum = nil
442 var txID common.TxID
443
444 // Insert blocks into DB
445 for i := range blocks {
446 if i == len(blocks)-1 {
447 blocks[i].Block.Timestamp = time.Now()
448 dbL1Txs, err := historyDB.GetAllL1UserTxs()
449 assert.NoError(t, err)
450 // Check batch_num is nil before forging
451 assert.Equal(t, null, dbL1Txs[len(dbL1Txs)-1].BatchNum)
452 // Save this TxId
453 txID = dbL1Txs[len(dbL1Txs)-1].TxID
454 }
455 err = historyDB.AddBlockSCData(&blocks[i])
456 assert.NoError(t, err)
457 }
458
459 // Check blocks
460 dbBlocks, err := historyDB.GetAllBlocks()
461 assert.NoError(t, err)
462 assert.Equal(t, len(blocks)+1, len(dbBlocks))
463
464 // Check batches
465 batches, err := historyDB.GetAllBatches()
466 assert.NoError(t, err)
467 assert.Equal(t, 11, len(batches))
468
469 // Check L1 Transactions
470 dbL1Txs, err := historyDB.GetAllL1UserTxs()
471 assert.NoError(t, err)
472 assert.Equal(t, 10, len(dbL1Txs))
473
474 // Tx Type
475 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[0].Type)
476 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[1].Type)
477 assert.Equal(t, common.TxTypeCreateAccountDepositTransfer, dbL1Txs[2].Type)
478 assert.Equal(t, common.TxTypeDeposit, dbL1Txs[3].Type)
479 assert.Equal(t, common.TxTypeDeposit, dbL1Txs[4].Type)
480 assert.Equal(t, common.TxTypeDepositTransfer, dbL1Txs[5].Type)
481 assert.Equal(t, common.TxTypeForceTransfer, dbL1Txs[6].Type)
482 assert.Equal(t, common.TxTypeForceExit, dbL1Txs[7].Type)
483 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[8].Type)
484 assert.Equal(t, common.TxTypeCreateAccountDeposit, dbL1Txs[9].Type)
485
486 // Tx ID
487 assert.Equal(t, "0x000000000000000001000000", dbL1Txs[0].TxID.String())
488 assert.Equal(t, "0x000000000000000001000100", dbL1Txs[1].TxID.String())
489 assert.Equal(t, "0x000000000000000003000000", dbL1Txs[2].TxID.String())
490 assert.Equal(t, "0x000000000000000005000000", dbL1Txs[3].TxID.String())
491 assert.Equal(t, "0x000000000000000005000100", dbL1Txs[4].TxID.String())
492 assert.Equal(t, "0x000000000000000005000200", dbL1Txs[5].TxID.String())
493 assert.Equal(t, "0x000000000000000006000000", dbL1Txs[6].TxID.String())
494 assert.Equal(t, "0x000000000000000006000100", dbL1Txs[7].TxID.String())
495 assert.Equal(t, "0x000000000000000008000000", dbL1Txs[8].TxID.String())
496 assert.Equal(t, "0x000000000000000009000000", dbL1Txs[9].TxID.String())
497
498 // Tx From IDx
499 assert.Equal(t, common.Idx(0), dbL1Txs[0].FromIdx)
500 assert.Equal(t, common.Idx(0), dbL1Txs[1].FromIdx)
501 assert.Equal(t, common.Idx(0), dbL1Txs[2].FromIdx)
502 assert.NotEqual(t, common.Idx(0), dbL1Txs[3].FromIdx)
503 assert.NotEqual(t, common.Idx(0), dbL1Txs[4].FromIdx)
504 assert.NotEqual(t, common.Idx(0), dbL1Txs[5].FromIdx)
505 assert.NotEqual(t, common.Idx(0), dbL1Txs[6].FromIdx)
506 assert.NotEqual(t, common.Idx(0), dbL1Txs[7].FromIdx)
507 assert.Equal(t, common.Idx(0), dbL1Txs[8].FromIdx)
508 assert.Equal(t, common.Idx(0), dbL1Txs[9].FromIdx)
509 assert.Equal(t, common.Idx(0), dbL1Txs[9].FromIdx)
510 assert.Equal(t, dbL1Txs[5].FromIdx, dbL1Txs[6].FromIdx)
511 assert.Equal(t, dbL1Txs[5].FromIdx, dbL1Txs[7].FromIdx)
512
513 // Tx to IDx
514 assert.Equal(t, dbL1Txs[2].ToIdx, dbL1Txs[5].FromIdx)
515 assert.Equal(t, dbL1Txs[5].ToIdx, dbL1Txs[3].FromIdx)
516 assert.Equal(t, dbL1Txs[6].ToIdx, dbL1Txs[3].FromIdx)
517
518 // Token ID
519 assert.Equal(t, common.TokenID(1), dbL1Txs[0].TokenID)
520 assert.Equal(t, common.TokenID(1), dbL1Txs[1].TokenID)
521 assert.Equal(t, common.TokenID(1), dbL1Txs[2].TokenID)
522 assert.Equal(t, common.TokenID(1), dbL1Txs[3].TokenID)
523 assert.Equal(t, common.TokenID(1), dbL1Txs[4].TokenID)
524 assert.Equal(t, common.TokenID(1), dbL1Txs[5].TokenID)
525 assert.Equal(t, common.TokenID(1), dbL1Txs[6].TokenID)
526 assert.Equal(t, common.TokenID(1), dbL1Txs[7].TokenID)
527 assert.Equal(t, common.TokenID(2), dbL1Txs[8].TokenID)
528 assert.Equal(t, common.TokenID(2), dbL1Txs[9].TokenID)
529
530 // Batch Number
531 var bn common.BatchNum = common.BatchNum(2)
532
533 assert.Equal(t, &bn, dbL1Txs[0].BatchNum)
534 assert.Equal(t, &bn, dbL1Txs[1].BatchNum)
535
536 bn = common.BatchNum(4)
537 assert.Equal(t, &bn, dbL1Txs[2].BatchNum)
538
539 bn = common.BatchNum(7)
540 assert.Equal(t, &bn, dbL1Txs[3].BatchNum)
541 assert.Equal(t, &bn, dbL1Txs[4].BatchNum)
542 assert.Equal(t, &bn, dbL1Txs[5].BatchNum)
543
544 bn = common.BatchNum(8)
545 assert.Equal(t, &bn, dbL1Txs[6].BatchNum)
546 assert.Equal(t, &bn, dbL1Txs[7].BatchNum)
547
548 bn = common.BatchNum(10)
549 assert.Equal(t, &bn, dbL1Txs[8].BatchNum)
550
551 bn = common.BatchNum(11)
552 assert.Equal(t, &bn, dbL1Txs[9].BatchNum)
553
554 // eth_block_num
555 assert.Equal(t, int64(2), dbL1Txs[0].EthBlockNum)
556 assert.Equal(t, int64(2), dbL1Txs[1].EthBlockNum)
557 assert.Equal(t, int64(3), dbL1Txs[2].EthBlockNum)
558 assert.Equal(t, int64(4), dbL1Txs[3].EthBlockNum)
559 assert.Equal(t, int64(4), dbL1Txs[4].EthBlockNum)
560 assert.Equal(t, int64(5), dbL1Txs[5].EthBlockNum)
561 assert.Equal(t, int64(6), dbL1Txs[6].EthBlockNum)
562 assert.Equal(t, int64(6), dbL1Txs[7].EthBlockNum)
563 assert.Equal(t, int64(7), dbL1Txs[8].EthBlockNum)
564 assert.Equal(t, int64(8), dbL1Txs[9].EthBlockNum)
565
566 // User Origin
567 assert.Equal(t, true, dbL1Txs[0].UserOrigin)
568 assert.Equal(t, true, dbL1Txs[1].UserOrigin)
569 assert.Equal(t, true, dbL1Txs[2].UserOrigin)
570 assert.Equal(t, true, dbL1Txs[3].UserOrigin)
571 assert.Equal(t, true, dbL1Txs[4].UserOrigin)
572 assert.Equal(t, true, dbL1Txs[5].UserOrigin)
573 assert.Equal(t, true, dbL1Txs[6].UserOrigin)
574 assert.Equal(t, true, dbL1Txs[7].UserOrigin)
575 assert.Equal(t, true, dbL1Txs[8].UserOrigin)
576 assert.Equal(t, true, dbL1Txs[9].UserOrigin)
577
578 // Deposit Amount
579 assert.Equal(t, big.NewInt(10), dbL1Txs[0].DepositAmount)
580 assert.Equal(t, big.NewInt(10), dbL1Txs[1].DepositAmount)
581 assert.Equal(t, big.NewInt(20), dbL1Txs[2].DepositAmount)
582 assert.Equal(t, big.NewInt(10), dbL1Txs[3].DepositAmount)
583 assert.Equal(t, big.NewInt(10), dbL1Txs[4].DepositAmount)
584 assert.Equal(t, big.NewInt(10), dbL1Txs[5].DepositAmount)
585 assert.Equal(t, big.NewInt(0), dbL1Txs[6].DepositAmount)
586 assert.Equal(t, big.NewInt(0), dbL1Txs[7].DepositAmount)
587 assert.Equal(t, big.NewInt(10), dbL1Txs[8].DepositAmount)
588 assert.Equal(t, big.NewInt(10), dbL1Txs[9].DepositAmount)
589
590 // Check saved txID's batch_num is not nil
591 assert.Equal(t, txID, dbL1Txs[len(dbL1Txs)-2].TxID)
592 assert.NotEqual(t, null, dbL1Txs[len(dbL1Txs)-2].BatchNum)
593
594 // Check Coordinator TXs
595 coordTxs, err := historyDB.GetAllL1CoordinatorTxs()
596 assert.NoError(t, err)
597 assert.Equal(t, 1, len(coordTxs))
598 assert.Equal(t, common.TxTypeCreateAccountDeposit, coordTxs[0].Type)
599 assert.Equal(t, false, coordTxs[0].UserOrigin)
600
601 // Check L2 TXs
602 dbL2Txs, err := historyDB.GetAllL2Txs()
603 assert.NoError(t, err)
604 assert.Equal(t, 4, len(dbL2Txs))
605
606 // Tx Type
607 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[0].Type)
608 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[1].Type)
609 assert.Equal(t, common.TxTypeTransfer, dbL2Txs[2].Type)
610 assert.Equal(t, common.TxTypeExit, dbL2Txs[3].Type)
611
612 // Tx ID
613 assert.Equal(t, "0x020000000001030000000000", dbL2Txs[0].TxID.String())
614 assert.Equal(t, "0x020000000001010000000000", dbL2Txs[1].TxID.String())
615 assert.Equal(t, "0x020000000001000000000000", dbL2Txs[2].TxID.String())
616 assert.Equal(t, "0x020000000001000000000001", dbL2Txs[3].TxID.String())
617
618 // Tx From and To IDx
619 assert.Equal(t, dbL2Txs[0].ToIdx, dbL2Txs[2].FromIdx)
620 assert.Equal(t, dbL2Txs[1].ToIdx, dbL2Txs[0].FromIdx)
621 assert.Equal(t, dbL2Txs[2].ToIdx, dbL2Txs[1].FromIdx)
622
623 // Batch Number
624 assert.Equal(t, common.BatchNum(5), dbL2Txs[0].BatchNum)
625 assert.Equal(t, common.BatchNum(5), dbL2Txs[1].BatchNum)
626 assert.Equal(t, common.BatchNum(5), dbL2Txs[2].BatchNum)
627 assert.Equal(t, common.BatchNum(5), dbL2Txs[3].BatchNum)
628
629 // eth_block_num
630 assert.Equal(t, int64(4), dbL2Txs[0].EthBlockNum)
631 assert.Equal(t, int64(4), dbL2Txs[1].EthBlockNum)
632 assert.Equal(t, int64(4), dbL2Txs[2].EthBlockNum)
633
634 // Amount
635 assert.Equal(t, big.NewInt(10), dbL2Txs[0].Amount)
636 assert.Equal(t, big.NewInt(10), dbL2Txs[1].Amount)
637 assert.Equal(t, big.NewInt(10), dbL2Txs[2].Amount)
638 assert.Equal(t, big.NewInt(10), dbL2Txs[3].Amount)
639}
640
641func TestExitTree(t *testing.T) {
642 nBatches := 17
643 blocks := setTestBlocks(1, 10)
644 batches := test.GenBatches(nBatches, blocks)
645 err := historyDB.AddBatches(batches)
646 assert.NoError(t, err)
647 const nTokens = 50
648 tokens, ethToken := test.GenTokens(nTokens, blocks)
649 err = historyDB.AddTokens(tokens)
650 assert.NoError(t, err)
651 tokens = append([]common.Token{ethToken}, tokens...)
652 const nAccounts = 3
653 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
654 assert.NoError(t, historyDB.AddAccounts(accs))
655 exitTree := test.GenExitTree(nBatches, batches, accs, blocks)
656 err = historyDB.AddExitTree(exitTree)
657 assert.NoError(t, err)
658}
659
660func TestGetUnforgedL1UserTxs(t *testing.T) {
661 test.WipeDB(historyDB.DB())
662
663 set := `
664 Type: Blockchain
665 AddToken(1)
666 AddToken(2)
667 AddToken(3)
668
669 CreateAccountDeposit(1) A: 20
670 CreateAccountDeposit(2) A: 20
671 CreateAccountDeposit(1) B: 5
672 CreateAccountDeposit(1) C: 5
673 CreateAccountDeposit(1) D: 5
674
675 > block
676 `
677 tc := til.NewContext(uint16(0), 128)
678 blocks, err := tc.GenerateBlocks(set)
679 require.NoError(t, err)
680 // Sanity check
681 require.Equal(t, 1, len(blocks))
682 require.Equal(t, 5, len(blocks[0].Rollup.L1UserTxs))
683
684 toForgeL1TxsNum := int64(1)
685
686 for i := range blocks {
687 err = historyDB.AddBlockSCData(&blocks[i])
688 require.NoError(t, err)
689 }
690
691 l1UserTxs, err := historyDB.GetUnforgedL1UserTxs(toForgeL1TxsNum)
692 require.NoError(t, err)
693 assert.Equal(t, 5, len(l1UserTxs))
694 assert.Equal(t, blocks[0].Rollup.L1UserTxs, l1UserTxs)
695
696 // No l1UserTxs for this toForgeL1TxsNum
697 l1UserTxs, err = historyDB.GetUnforgedL1UserTxs(2)
698 require.NoError(t, err)
699 assert.Equal(t, 0, len(l1UserTxs))
700}
701
702func exampleInitSCVars() (*common.RollupVariables, *common.AuctionVariables, *common.WDelayerVariables) {
703 //nolint:govet
704 rollup := &common.RollupVariables{
705 0,
706 big.NewInt(10),
707 12,
708 13,
709 [5]common.BucketParams{},
710 false,
711 }
712 //nolint:govet
713 auction := &common.AuctionVariables{
714 0,
715 ethCommon.BigToAddress(big.NewInt(2)),
716 ethCommon.BigToAddress(big.NewInt(3)),
717 "https://boot.coord.com",
718 [6]*big.Int{
719 big.NewInt(1), big.NewInt(2), big.NewInt(3),
720 big.NewInt(4), big.NewInt(5), big.NewInt(6),
721 },
722 0,
723 2,
724 4320,
725 [3]uint16{10, 11, 12},
726 1000,
727 20,
728 }
729 //nolint:govet
730 wDelayer := &common.WDelayerVariables{
731 0,
732 ethCommon.BigToAddress(big.NewInt(2)),
733 ethCommon.BigToAddress(big.NewInt(3)),
734 13,
735 14,
736 false,
737 }
738 return rollup, auction, wDelayer
739}
740
741func TestSetInitialSCVars(t *testing.T) {
742 test.WipeDB(historyDB.DB())
743 _, _, _, err := historyDB.GetSCVars()
744 assert.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
745 rollup, auction, wDelayer := exampleInitSCVars()
746 err = historyDB.SetInitialSCVars(rollup, auction, wDelayer)
747 require.NoError(t, err)
748 dbRollup, dbAuction, dbWDelayer, err := historyDB.GetSCVars()
749 require.NoError(t, err)
750 require.Equal(t, rollup, dbRollup)
751 require.Equal(t, auction, dbAuction)
752 require.Equal(t, wDelayer, dbWDelayer)
753}
754
755func TestSetL1UserTxEffectiveAmounts(t *testing.T) {
756 test.WipeDB(historyDB.DB())
757
758 set := `
759 Type: Blockchain
760
761 AddToken(1)
762
763 CreateAccountDeposit(1) A: 2000
764 CreateAccountDeposit(1) B: 500
765 CreateAccountDeposit(1) C: 500
766
767 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{*}
768 > block // blockNum=2
769
770 > batchL1 // forge defined L1UserTxs{*}
771 > block // blockNum=3
772 `
773
774 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
775 tilCfgExtra := til.ConfigExtra{
776 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
777 CoordUser: "A",
778 }
779 blocks, err := tc.GenerateBlocks(set)
780 require.NoError(t, err)
781 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
782 require.NoError(t, err)
783 err = tc.FillBlocksForgedL1UserTxs(blocks)
784 require.NoError(t, err)
785
786 // Add only first block so that the L1UserTxs are not marked as forged
787 for i := range blocks[:1] {
788 err = historyDB.AddBlockSCData(&blocks[i])
789 require.NoError(t, err)
790 }
791 // Add second batch to trigger the update of the batch_num,
792 // while avoiding the implicit call of setL1UserTxEffectiveAmounts
793 err = historyDB.addBlock(historyDB.db, &blocks[1].Block)
794 assert.NoError(t, err)
795 err = historyDB.addBatch(historyDB.db, &blocks[1].Rollup.Batches[0].Batch)
796 assert.NoError(t, err)
797 require.NoError(t, err)
798
799 // Set the Effective{Amount,DepositAmount} of the L1UserTxs that are forged in the second block
800 l1Txs := blocks[1].Rollup.Batches[0].L1UserTxs
801 require.Equal(t, 3, len(l1Txs))
802 // Change some values to test all cases
803 l1Txs[1].EffectiveAmount = big.NewInt(0)
804 l1Txs[2].EffectiveDepositAmount = big.NewInt(0)
805 l1Txs[2].EffectiveAmount = big.NewInt(0)
806 err = historyDB.setL1UserTxEffectiveAmounts(historyDB.db, l1Txs)
807 require.NoError(t, err)
808
809 dbL1Txs, err := historyDB.GetAllL1UserTxs()
810 require.NoError(t, err)
811 for i, tx := range dbL1Txs {
812 log.Infof("%d %v %v", i, tx.EffectiveAmount, tx.EffectiveDepositAmount)
813 assert.NotNil(t, tx.EffectiveAmount)
814 assert.NotNil(t, tx.EffectiveDepositAmount)
815 switch tx.TxID {
816 case l1Txs[0].TxID:
817 assert.Equal(t, l1Txs[0].DepositAmount, tx.EffectiveDepositAmount)
818 assert.Equal(t, l1Txs[0].Amount, tx.EffectiveAmount)
819 case l1Txs[1].TxID:
820 assert.Equal(t, l1Txs[1].DepositAmount, tx.EffectiveDepositAmount)
821 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
822 case l1Txs[2].TxID:
823 assert.Equal(t, big.NewInt(0), tx.EffectiveDepositAmount)
824 assert.Equal(t, big.NewInt(0), tx.EffectiveAmount)
825 }
826 }
827}
828
829func TestUpdateExitTree(t *testing.T) {
830 test.WipeDB(historyDB.DB())
831
832 set := `
833 Type: Blockchain
834
835 AddToken(1)
836
837 CreateAccountDeposit(1) C: 2000 // Idx=256+2=258
838 CreateAccountDeposit(1) D: 500 // Idx=256+3=259
839
840 CreateAccountCoordinator(1) A // Idx=256+0=256
841 CreateAccountCoordinator(1) B // Idx=256+1=257
842
843 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5}
844 > batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil}
845 > block // blockNum=2
846
847 ForceExit(1) A: 100
848 ForceExit(1) B: 80
849
850 Exit(1) C: 50 (172)
851 Exit(1) D: 30 (172)
852
853 > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{3}
854 > batchL1 // forge L1UserTxs{3}, freeze defined L1UserTxs{nil}
855 > block // blockNum=3
856
857 > block // blockNum=4 (empty block)
858 > block // blockNum=5 (empty block)
859 `
860
861 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
862 tilCfgExtra := til.ConfigExtra{
863 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
864 CoordUser: "A",
865 }
866 blocks, err := tc.GenerateBlocks(set)
867 require.NoError(t, err)
868 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
869 require.NoError(t, err)
870
871 // Add all blocks except for the last two
872 for i := range blocks[:len(blocks)-2] {
873 err = historyDB.AddBlockSCData(&blocks[i])
874 require.NoError(t, err)
875 }
876
877 // Add withdraws to the second-to-last block, and insert block into the DB
878 block := &blocks[len(blocks)-2]
879 require.Equal(t, int64(4), block.Block.Num)
880 tokenAddr := blocks[0].Rollup.AddedTokens[0].EthAddr
881 // block.WDelayer.Deposits = append(block.WDelayer.Deposits,
882 // common.WDelayerTransfer{Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr, Amount: big.NewInt(80)}, // 257
883 // common.WDelayerTransfer{Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr, Amount: big.NewInt(15)}, // 259
884 // )
885 block.Rollup.Withdrawals = append(block.Rollup.Withdrawals,
886 common.WithdrawInfo{Idx: 256, NumExitRoot: 4, InstantWithdraw: true},
887 common.WithdrawInfo{Idx: 257, NumExitRoot: 4, InstantWithdraw: false,
888 Owner: tc.UsersByIdx[257].Addr, Token: tokenAddr},
889 common.WithdrawInfo{Idx: 258, NumExitRoot: 3, InstantWithdraw: true},
890 common.WithdrawInfo{Idx: 259, NumExitRoot: 3, InstantWithdraw: false,
891 Owner: tc.UsersByIdx[259].Addr, Token: tokenAddr},
892 )
893 err = historyDB.addBlock(historyDB.db, &block.Block)
894 require.NoError(t, err)
895
896 err = historyDB.updateExitTree(historyDB.db, block.Block.Num,
897 block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
898 require.NoError(t, err)
899
900 // Check that exits in DB match with the expected values
901 dbExits, err := historyDB.GetAllExits()
902 require.NoError(t, err)
903 assert.Equal(t, 4, len(dbExits))
904 dbExitsByIdx := make(map[common.Idx]common.ExitInfo)
905 for _, dbExit := range dbExits {
906 dbExitsByIdx[dbExit.AccountIdx] = dbExit
907 }
908 for _, withdraw := range block.Rollup.Withdrawals {
909 assert.Equal(t, withdraw.NumExitRoot, dbExitsByIdx[withdraw.Idx].BatchNum)
910 if withdraw.InstantWithdraw {
911 assert.Equal(t, &block.Block.Num, dbExitsByIdx[withdraw.Idx].InstantWithdrawn)
912 } else {
913 assert.Equal(t, &block.Block.Num, dbExitsByIdx[withdraw.Idx].DelayedWithdrawRequest)
914 }
915 }
916
917 // Add delayed withdraw to the last block, and insert block into the DB
918 block = &blocks[len(blocks)-1]
919 require.Equal(t, int64(5), block.Block.Num)
920 block.WDelayer.Withdrawals = append(block.WDelayer.Withdrawals,
921 common.WDelayerTransfer{
922 Owner: tc.UsersByIdx[257].Addr,
923 Token: tokenAddr,
924 Amount: big.NewInt(80),
925 })
926 err = historyDB.addBlock(historyDB.db, &block.Block)
927 require.NoError(t, err)
928
929 err = historyDB.updateExitTree(historyDB.db, block.Block.Num,
930 block.Rollup.Withdrawals, block.WDelayer.Withdrawals)
931 require.NoError(t, err)
932
933 // Check that delayed withdrawn has been set
934 dbExits, err = historyDB.GetAllExits()
935 require.NoError(t, err)
936 for _, dbExit := range dbExits {
937 dbExitsByIdx[dbExit.AccountIdx] = dbExit
938 }
939 require.Equal(t, &block.Block.Num, dbExitsByIdx[257].DelayedWithdrawn)
940}
941
942func TestGetBestBidCoordinator(t *testing.T) {
943 test.WipeDB(historyDB.DB())
944
945 rollup, auction, wDelayer := exampleInitSCVars()
946 err := historyDB.SetInitialSCVars(rollup, auction, wDelayer)
947 require.NoError(t, err)
948
949 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
950 blocks, err := tc.GenerateBlocks(`
951 Type: Blockchain
952 > block // blockNum=2
953 `)
954 require.NoError(t, err)
955 err = historyDB.AddBlockSCData(&blocks[0])
956 require.NoError(t, err)
957
958 coords := []common.Coordinator{
959 {
960 Bidder: ethCommon.BigToAddress(big.NewInt(1)),
961 Forger: ethCommon.BigToAddress(big.NewInt(2)),
962 EthBlockNum: 2,
963 URL: "foo",
964 },
965 {
966 Bidder: ethCommon.BigToAddress(big.NewInt(3)),
967 Forger: ethCommon.BigToAddress(big.NewInt(4)),
968 EthBlockNum: 2,
969 URL: "bar",
970 },
971 }
972 err = historyDB.addCoordinators(historyDB.db, coords)
973 require.NoError(t, err)
974
975 bids := []common.Bid{
976 {
977 SlotNum: 10,
978 BidValue: big.NewInt(10),
979 EthBlockNum: 2,
980 Bidder: coords[0].Bidder,
981 },
982 {
983 SlotNum: 10,
984 BidValue: big.NewInt(20),
985 EthBlockNum: 2,
986 Bidder: coords[1].Bidder,
987 },
988 }
989
990 err = historyDB.addBids(historyDB.db, bids)
991 require.NoError(t, err)
992
993 forger10, err := historyDB.GetBestBidCoordinator(10)
994 require.NoError(t, err)
995 require.Equal(t, coords[1].Forger, forger10.Forger)
996 require.Equal(t, coords[1].Bidder, forger10.Bidder)
997 require.Equal(t, coords[1].URL, forger10.URL)
998 require.Equal(t, bids[1].SlotNum, forger10.SlotNum)
999 require.Equal(t, bids[1].BidValue, forger10.BidValue)
1000 for i := range forger10.DefaultSlotSetBid {
1001 require.Equal(t, auction.DefaultSlotSetBid[i], forger10.DefaultSlotSetBid[i])
1002 }
1003
1004 _, err = historyDB.GetBestBidCoordinator(11)
1005 require.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
1006}
1007
1008func TestAddBucketUpdates(t *testing.T) {
1009 test.WipeDB(historyDB.DB())
1010 const fromBlock int64 = 1
1011 const toBlock int64 = 5 + 1
1012 setTestBlocks(fromBlock, toBlock)
1013
1014 bucketUpdates := []common.BucketUpdate{
1015 {
1016 EthBlockNum: 4,
1017 NumBucket: 0,
1018 BlockStamp: 4,
1019 Withdrawals: big.NewInt(123),
1020 },
1021 {
1022 EthBlockNum: 5,
1023 NumBucket: 2,
1024 BlockStamp: 5,
1025 Withdrawals: big.NewInt(42),
1026 },
1027 }
1028 err := historyDB.addBucketUpdates(historyDB.db, bucketUpdates)
1029 require.NoError(t, err)
1030 dbBucketUpdates, err := historyDB.GetAllBucketUpdates()
1031 require.NoError(t, err)
1032 assert.Equal(t, bucketUpdates, dbBucketUpdates)
1033}
1034
1035func TestAddTokenExchanges(t *testing.T) {
1036 test.WipeDB(historyDB.DB())
1037 const fromBlock int64 = 1
1038 const toBlock int64 = 5 + 1
1039 setTestBlocks(fromBlock, toBlock)
1040
1041 tokenExchanges := []common.TokenExchange{
1042 {
1043 EthBlockNum: 4,
1044 Address: ethCommon.BigToAddress(big.NewInt(111)),
1045 ValueUSD: 12345,
1046 },
1047 {
1048 EthBlockNum: 5,
1049 Address: ethCommon.BigToAddress(big.NewInt(222)),
1050 ValueUSD: 67890,
1051 },
1052 }
1053 err := historyDB.addTokenExchanges(historyDB.db, tokenExchanges)
1054 require.NoError(t, err)
1055 dbTokenExchanges, err := historyDB.GetAllTokenExchanges()
1056 require.NoError(t, err)
1057 assert.Equal(t, tokenExchanges, dbTokenExchanges)
1058}
1059
1060func TestAddEscapeHatchWithdrawals(t *testing.T) {
1061 test.WipeDB(historyDB.DB())
1062 const fromBlock int64 = 1
1063 const toBlock int64 = 5 + 1
1064 setTestBlocks(fromBlock, toBlock)
1065
1066 escapeHatchWithdrawals := []common.WDelayerEscapeHatchWithdrawal{
1067 {
1068 EthBlockNum: 4,
1069 Who: ethCommon.BigToAddress(big.NewInt(111)),
1070 To: ethCommon.BigToAddress(big.NewInt(222)),
1071 TokenAddr: ethCommon.BigToAddress(big.NewInt(333)),
1072 Amount: big.NewInt(10002),
1073 },
1074 {
1075 EthBlockNum: 5,
1076 Who: ethCommon.BigToAddress(big.NewInt(444)),
1077 To: ethCommon.BigToAddress(big.NewInt(555)),
1078 TokenAddr: ethCommon.BigToAddress(big.NewInt(666)),
1079 Amount: big.NewInt(20003),
1080 },
1081 }
1082 err := historyDB.addEscapeHatchWithdrawals(historyDB.db, escapeHatchWithdrawals)
1083 require.NoError(t, err)
1084 dbEscapeHatchWithdrawals, err := historyDB.GetAllEscapeHatchWithdrawals()
1085 require.NoError(t, err)
1086 assert.Equal(t, escapeHatchWithdrawals, dbEscapeHatchWithdrawals)
1087}
1088
1089func TestGetMetrics(t *testing.T) {
1090 test.WipeDB(historyDB.DB())
1091
1092 set := `
1093 Type: Blockchain
1094
1095 AddToken(1)
1096
1097 CreateAccountDeposit(1) A: 1000 // numTx=1
1098 CreateAccountDeposit(1) B: 2000 // numTx=2
1099 CreateAccountDeposit(1) C: 3000 //numTx=3
1100
1101 // block 0 is stored as default in the DB
1102 // block 1 does not exist
1103 > batchL1 // numBatches=1
1104 > batchL1 // numBatches=2
1105 > block // blockNum=2
1106
1107 Transfer(1) C-A : 10 (1) // numTx=4
1108 > batch // numBatches=3
1109 > block // blockNum=3
1110 Transfer(1) B-C : 10 (1) // numTx=5
1111 > batch // numBatches=5
1112 > block // blockNum=4
1113 Transfer(1) A-B : 10 (1) // numTx=6
1114 > batch // numBatches=5
1115 > block // blockNum=5
1116 Transfer(1) A-B : 10 (1) // numTx=7
1117 > batch // numBatches=6
1118 > block // blockNum=6
1119 `
1120
1121 const numBatches int = 6
1122 const numTx int = 7
1123 const blockNum = 6 - 1
1124
1125 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
1126 tilCfgExtra := til.ConfigExtra{
1127 BootCoordAddr: ethCommon.HexToAddress("0xE39fEc6224708f0772D2A74fd3f9055A90E0A9f2"),
1128 CoordUser: "A",
1129 }
1130 blocks, err := tc.GenerateBlocks(set)
1131 require.NoError(t, err)
1132 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
1133 require.NoError(t, err)
1134
1135 // Sanity check
1136 require.Equal(t, blockNum, len(blocks))
1137
1138 // Adding one batch per block
1139 // batch frequency can be chosen
1140 const frequency int = 15
1141
1142 for i := range blocks {
1143 blocks[i].Block.Timestamp = time.Now().Add(-time.Second * time.Duration(frequency*(len(blocks)-i)))
1144 err = historyDB.AddBlockSCData(&blocks[i])
1145 assert.NoError(t, err)
1146 }
1147
1148 res, err := historyDB.GetMetrics(common.BatchNum(numBatches))
1149 assert.NoError(t, err)
1150
1151 assert.Equal(t, float64(numTx)/float64(numBatches-1), res.TransactionsPerBatch)
1152
1153 // Frequency is not exactly the desired one, some decimals may appear
1154 assert.GreaterOrEqual(t, res.BatchFrequency, float64(frequency))
1155 assert.Less(t, res.BatchFrequency, float64(frequency+1))
1156 // Truncate frecuency into an int to do an exact check
1157 assert.Equal(t, frequency, int(res.BatchFrequency))
1158 // This may also be different in some decimals
1159 // Truncate it to the third decimal to compare
1160 assert.Equal(t, math.Trunc((float64(numTx)/float64(frequency*blockNum-frequency))/0.001)*0.001, math.Trunc(res.TransactionsPerSecond/0.001)*0.001)
1161 assert.Equal(t, int64(3), res.TotalAccounts)
1162 assert.Equal(t, int64(3), res.TotalBJJs)
1163 // Til does not set fees
1164 assert.Equal(t, float64(0), res.AvgTransactionFee)
1165}
1166
1167func TestGetMetricsMoreThan24Hours(t *testing.T) {
1168 test.WipeDB(historyDB.DB())
1169
1170 testUsersLen := 3
1171 var set []til.Instruction
1172 for user := 0; user < testUsersLen; user++ {
1173 set = append(set, til.Instruction{
1174 Typ: common.TxTypeCreateAccountDeposit,
1175 TokenID: common.TokenID(0),
1176 DepositAmount: big.NewInt(1000000),
1177 Amount: big.NewInt(0),
1178 From: fmt.Sprintf("User%02d", user),
1179 })
1180 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1181 }
1182 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1183 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1184 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1185
1186 // Transfers
1187 for x := 0; x < 6000; x++ {
1188 set = append(set, til.Instruction{
1189 Typ: common.TxTypeTransfer,
1190 TokenID: common.TokenID(0),
1191 DepositAmount: big.NewInt(1),
1192 Amount: big.NewInt(0),
1193 From: "User00",
1194 To: "User01",
1195 })
1196 set = append(set, til.Instruction{Typ: til.TypeNewBatch})
1197 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1198 }
1199
1200 var chainID uint16 = 0
1201 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
1202 blocks, err := tc.GenerateBlocksFromInstructions(set)
1203 assert.NoError(t, err)
1204
1205 tilCfgExtra := til.ConfigExtra{
1206 CoordUser: "A",
1207 }
1208 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
1209 require.NoError(t, err)
1210
1211 const numBatches int = 6002
1212 const numTx int = 6003
1213 const blockNum = 6005 - 1
1214
1215 // Sanity check
1216 require.Equal(t, blockNum, len(blocks))
1217
1218 // Adding one batch per block
1219 // batch frequency can be chosen
1220 const frequency int = 15
1221
1222 for i := range blocks {
1223 blocks[i].Block.Timestamp = time.Now().Add(-time.Second * time.Duration(frequency*(len(blocks)-i)))
1224 err = historyDB.AddBlockSCData(&blocks[i])
1225 assert.NoError(t, err)
1226 }
1227
1228 res, err := historyDB.GetMetrics(common.BatchNum(numBatches))
1229 assert.NoError(t, err)
1230
1231 assert.Equal(t, math.Trunc((float64(numTx)/float64(numBatches-1))/0.001)*0.001, math.Trunc(res.TransactionsPerBatch/0.001)*0.001)
1232
1233 // Frequency is not exactly the desired one, some decimals may appear
1234 assert.GreaterOrEqual(t, res.BatchFrequency, float64(frequency))
1235 assert.Less(t, res.BatchFrequency, float64(frequency+1))
1236 // Truncate frecuency into an int to do an exact check
1237 assert.Equal(t, frequency, int(res.BatchFrequency))
1238 // This may also be different in some decimals
1239 // Truncate it to the third decimal to compare
1240 assert.Equal(t, math.Trunc((float64(numTx)/float64(frequency*blockNum-frequency))/0.001)*0.001, math.Trunc(res.TransactionsPerSecond/0.001)*0.001)
1241 assert.Equal(t, int64(3), res.TotalAccounts)
1242 assert.Equal(t, int64(3), res.TotalBJJs)
1243 // Til does not set fees
1244 assert.Equal(t, float64(0), res.AvgTransactionFee)
1245}
1246
1247func TestGetMetricsEmpty(t *testing.T) {
1248 test.WipeDB(historyDB.DB())
1249 _, err := historyDB.GetMetrics(0)
1250 assert.NoError(t, err)
1251}
1252
1253func TestGetAvgTxFeeEmpty(t *testing.T) {
1254 test.WipeDB(historyDB.DB())
1255 _, err := historyDB.GetAvgTxFee()
1256 assert.NoError(t, err)
1257}
1258
1259func TestGetLastL1TxsNum(t *testing.T) {
1260 test.WipeDB(historyDB.DB())
1261 _, err := historyDB.GetLastL1TxsNum()
1262 assert.NoError(t, err)
1263}
1264
1265func TestGetLastTxsPosition(t *testing.T) {
1266 test.WipeDB(historyDB.DB())
1267 _, err := historyDB.GetLastTxsPosition(0)
1268 assert.Equal(t, sql.ErrNoRows.Error(), err.Error())
1269}
1270
1271func TestGetFirstBatchBlockNumBySlot(t *testing.T) {
1272 test.WipeDB(historyDB.DB())
1273
1274 set := `
1275 Type: Blockchain
1276
1277 // Slot = 0
1278
1279 > block // 2
1280 > block // 3
1281 > block // 4
1282 > block // 5
1283
1284 // Slot = 1
1285
1286 > block // 6
1287 > block // 7
1288 > batch
1289 > block // 8
1290 > block // 9
1291
1292 // Slot = 2
1293
1294 > batch
1295 > block // 10
1296 > block // 11
1297 > block // 12
1298 > block // 13
1299
1300 `
1301 tc := til.NewContext(uint16(0), common.RollupConstMaxL1UserTx)
1302 blocks, err := tc.GenerateBlocks(set)
1303 assert.NoError(t, err)
1304
1305 tilCfgExtra := til.ConfigExtra{
1306 CoordUser: "A",
1307 }
1308 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
1309 require.NoError(t, err)
1310
1311 for i := range blocks {
1312 for j := range blocks[i].Rollup.Batches {
1313 blocks[i].Rollup.Batches[j].Batch.SlotNum = int64(i) / 4
1314 }
1315 }
1316
1317 // Add all blocks
1318 for i := range blocks {
1319 err = historyDB.AddBlockSCData(&blocks[i])
1320 require.NoError(t, err)
1321 }
1322
1323 _, err = historyDB.GetFirstBatchBlockNumBySlot(0)
1324 require.Equal(t, sql.ErrNoRows, tracerr.Unwrap(err))
1325
1326 bn1, err := historyDB.GetFirstBatchBlockNumBySlot(1)
1327 require.NoError(t, err)
1328 assert.Equal(t, int64(8), bn1)
1329
1330 bn2, err := historyDB.GetFirstBatchBlockNumBySlot(2)
1331 require.NoError(t, err)
1332 assert.Equal(t, int64(10), bn2)
1333}
1334
1335func TestTxItemID(t *testing.T) {
1336 test.WipeDB(historyDB.DB())
1337 testUsersLen := 10
1338 var set []til.Instruction
1339 for user := 0; user < testUsersLen; user++ {
1340 set = append(set, til.Instruction{
1341 Typ: common.TxTypeCreateAccountDeposit,
1342 TokenID: common.TokenID(0),
1343 DepositAmount: big.NewInt(1000000),
1344 Amount: big.NewInt(0),
1345 From: fmt.Sprintf("User%02d", user),
1346 })
1347 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1348 }
1349 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1350 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1351 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1352 for user := 0; user < testUsersLen; user++ {
1353 set = append(set, til.Instruction{
1354 Typ: common.TxTypeDeposit,
1355 TokenID: common.TokenID(0),
1356 DepositAmount: big.NewInt(100000),
1357 Amount: big.NewInt(0),
1358 From: fmt.Sprintf("User%02d", user),
1359 })
1360 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1361 }
1362 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1363 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1364 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1365 for user := 0; user < testUsersLen; user++ {
1366 set = append(set, til.Instruction{
1367 Typ: common.TxTypeDepositTransfer,
1368 TokenID: common.TokenID(0),
1369 DepositAmount: big.NewInt(10000 * int64(user+1)),
1370 Amount: big.NewInt(1000 * int64(user+1)),
1371 From: fmt.Sprintf("User%02d", user),
1372 To: fmt.Sprintf("User%02d", (user+1)%testUsersLen),
1373 })
1374 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1375 }
1376 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1377 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1378 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1379 for user := 0; user < testUsersLen; user++ {
1380 set = append(set, til.Instruction{
1381 Typ: common.TxTypeForceTransfer,
1382 TokenID: common.TokenID(0),
1383 Amount: big.NewInt(100 * int64(user+1)),
1384 DepositAmount: big.NewInt(0),
1385 From: fmt.Sprintf("User%02d", user),
1386 To: fmt.Sprintf("User%02d", (user+1)%testUsersLen),
1387 })
1388 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1389 }
1390 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1391 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1392 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1393 for user := 0; user < testUsersLen; user++ {
1394 set = append(set, til.Instruction{
1395 Typ: common.TxTypeForceExit,
1396 TokenID: common.TokenID(0),
1397 Amount: big.NewInt(10 * int64(user+1)),
1398 DepositAmount: big.NewInt(0),
1399 From: fmt.Sprintf("User%02d", user),
1400 })
1401 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1402 }
1403 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1404 set = append(set, til.Instruction{Typ: til.TypeNewBatchL1})
1405 set = append(set, til.Instruction{Typ: til.TypeNewBlock})
1406 var chainID uint16 = 0
1407 tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx)
1408 blocks, err := tc.GenerateBlocksFromInstructions(set)
1409 assert.NoError(t, err)
1410
1411 tilCfgExtra := til.ConfigExtra{
1412 CoordUser: "A",
1413 }
1414 err = tc.FillBlocksExtra(blocks, &tilCfgExtra)
1415 require.NoError(t, err)
1416
1417 // Add all blocks
1418 for i := range blocks {
1419 err = historyDB.AddBlockSCData(&blocks[i])
1420 require.NoError(t, err)
1421 }
1422
1423 txs, err := historyDB.GetAllL1UserTxs()
1424 require.NoError(t, err)
1425 position := 0
1426 for _, tx := range txs {
1427 if tx.Position == 0 {
1428 position = 0
1429 }
1430 assert.Equal(t, position, tx.Position)
1431 position++
1432 }
1433}
1434
1435// setTestBlocks WARNING: this will delete the blocks and recreate them
1436func setTestBlocks(from, to int64) []common.Block {
1437 test.WipeDB(historyDB.DB())
1438 blocks := test.GenBlocks(from, to)
1439 if err := historyDB.AddBlocks(blocks); err != nil {
1440 panic(err)
1441 }
1442 return blocks
1443}