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