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