hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
1package historydb
2
3import (
4 "database/sql"
5 "math"
6 "math/big"
7 "os"
8 "testing"
9
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 "github.com/hermeznetwork/hermez-node/common"
12 dbUtils "github.com/hermeznetwork/hermez-node/db"
13 "github.com/hermeznetwork/hermez-node/log"
14 "github.com/hermeznetwork/hermez-node/test"
15 "github.com/hermeznetwork/hermez-node/test/til"
16 "github.com/stretchr/testify/assert"
17 "github.com/stretchr/testify/require"
18)
19
20var historyDB *HistoryDB
21
22// In order to run the test you need to run a Posgres DB with
23// a database named "history" that is accessible by
24// user: "hermez"
25// pass: set it using the env var POSTGRES_PASS
26// 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;"
27// After running the test you can stop the container by running: sudo docker kill hermez-db-test
28// If you already did that for the L2DB you don't have to do it again
29
30func TestMain(m *testing.M) {
31 // init DB
32 pass := os.Getenv("POSTGRES_PASS")
33 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
34 if err != nil {
35 panic(err)
36 }
37 historyDB = NewHistoryDB(db)
38 if err != nil {
39 panic(err)
40 }
41 // Run tests
42 result := m.Run()
43 // Close DB
44 if err := db.Close(); err != nil {
45 log.Error("Error closing the history DB:", err)
46 }
47 os.Exit(result)
48}
49
50func TestBlocks(t *testing.T) {
51 var fromBlock, toBlock int64
52 fromBlock = 1
53 toBlock = 5
54 // Delete peviously created rows (clean previous test execs)
55 test.WipeDB(historyDB.DB())
56 // Generate fake blocks
57 blocks := test.GenBlocks(fromBlock, toBlock)
58 // Insert blocks into DB
59 for i := 0; i < len(blocks); i++ {
60 err := historyDB.AddBlock(&blocks[i])
61 assert.NoError(t, err)
62 }
63 // Get all blocks from DB
64 fetchedBlocks, err := historyDB.GetBlocks(fromBlock, toBlock)
65 assert.Equal(t, len(blocks), len(fetchedBlocks))
66 // Compare generated vs getted blocks
67 assert.NoError(t, err)
68 for i := range fetchedBlocks {
69 assertEqualBlock(t, &blocks[i], &fetchedBlocks[i])
70 }
71 // Get blocks from the DB one by one
72 for i := fromBlock; i < toBlock; i++ {
73 fetchedBlock, err := historyDB.GetBlock(i)
74 assert.NoError(t, err)
75 assertEqualBlock(t, &blocks[i-1], fetchedBlock)
76 }
77 // Get last block
78 lastBlock, err := historyDB.GetLastBlock()
79 assert.NoError(t, err)
80 assertEqualBlock(t, &blocks[len(blocks)-1], lastBlock)
81}
82
83func assertEqualBlock(t *testing.T, expected *common.Block, actual *common.Block) {
84 assert.Equal(t, expected.EthBlockNum, actual.EthBlockNum)
85 assert.Equal(t, expected.Hash, actual.Hash)
86 assert.Equal(t, expected.Timestamp.Unix(), actual.Timestamp.Unix())
87}
88
89func TestBatches(t *testing.T) {
90 const fromBlock int64 = 1
91 const toBlock int64 = 3
92 // Prepare blocks in the DB
93 blocks := setTestBlocks(fromBlock, toBlock)
94 // Generate fake batches
95 const nBatches = 9
96 batches := test.GenBatches(nBatches, blocks)
97 // Test GetLastL1TxsNum with no batches
98 fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
99 assert.NoError(t, err)
100 assert.Nil(t, fetchedLastL1TxsNum)
101 // Add batches to the DB
102 err = historyDB.AddBatches(batches)
103 assert.NoError(t, err)
104 // Get batches from the DB
105 fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(nBatches))
106 assert.NoError(t, err)
107 for i, fetchedBatch := range fetchedBatches {
108 assert.Equal(t, batches[i], fetchedBatch)
109 }
110 // Test GetLastBatchNum
111 fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
112 assert.NoError(t, err)
113 assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
114 // Test GetLastL1TxsNum
115 fetchedLastL1TxsNum, err = historyDB.GetLastL1TxsNum()
116 assert.NoError(t, err)
117 assert.Equal(t, *batches[nBatches-1].ForgeL1TxsNum, *fetchedLastL1TxsNum)
118 // Test total fee
119 // Generate fake tokens
120 const nTokens = 5
121 tokens, ethToken := test.GenTokens(nTokens, blocks)
122 err = historyDB.AddTokens(tokens)
123 assert.NoError(t, err)
124 tokens = append([]common.Token{ethToken}, tokens...)
125 feeBatch := batches[0]
126 feeBatch.BatchNum = 9999
127 feeBatch.CollectedFees = make(map[common.TokenID]*big.Int)
128 var total float64
129 for i, token := range tokens {
130 value := 3.019237 * float64(i)
131 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
132 bigAmount := big.NewInt(345000000)
133 feeBatch.CollectedFees[token.TokenID] = bigAmount
134 f := new(big.Float).SetInt(bigAmount)
135 amount, _ := f.Float64()
136 total += value * (amount / math.Pow(10, float64(token.Decimals)))
137 }
138 err = historyDB.AddBatch(&feeBatch)
139 assert.NoError(t, err)
140 fetchedBatches, err = historyDB.GetBatches(feeBatch.BatchNum-1, feeBatch.BatchNum+1)
141 assert.NoError(t, err)
142 for _, fetchedBatch := range fetchedBatches {
143 if fetchedBatch.BatchNum == feeBatch.BatchNum {
144 assert.Equal(t, total, *fetchedBatch.TotalFeesUSD)
145 }
146 }
147}
148
149func TestBids(t *testing.T) {
150 const fromBlock int64 = 1
151 const toBlock int64 = 5
152 // Prepare blocks in the DB
153 blocks := setTestBlocks(fromBlock, toBlock)
154 // Generate fake coordinators
155 const nCoords = 5
156 coords := test.GenCoordinators(nCoords, blocks)
157 err := historyDB.AddCoordinators(coords)
158 assert.NoError(t, err)
159 // Generate fake bids
160 const nBids = 20
161 bids := test.GenBids(nBids, blocks, coords)
162 err = historyDB.AddBids(bids)
163 assert.NoError(t, err)
164 // Fetch bids
165 fetchedBids, err := historyDB.GetAllBids()
166 assert.NoError(t, err)
167 // Compare fetched bids vs generated bids
168 for i, bid := range fetchedBids {
169 assert.Equal(t, bids[i], bid)
170 }
171}
172
173func TestTokens(t *testing.T) {
174 const fromBlock int64 = 1
175 const toBlock int64 = 5
176 // Prepare blocks in the DB
177 blocks := setTestBlocks(fromBlock, toBlock)
178 // Generate fake tokens
179 const nTokens = 5
180 tokens, ethToken := test.GenTokens(nTokens, blocks)
181 err := historyDB.AddTokens(tokens)
182 assert.NoError(t, err)
183 tokens = append([]common.Token{ethToken}, tokens...)
184 limit := uint(10)
185 // Fetch tokens6
186 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
187 assert.NoError(t, err)
188 // Compare fetched tokens vs generated tokens
189 // All the tokens should have USDUpdate setted by the DB trigger
190 for i, token := range fetchedTokens {
191 assert.Equal(t, tokens[i].TokenID, token.TokenID)
192 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
193 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
194 assert.Equal(t, tokens[i].Name, token.Name)
195 assert.Equal(t, tokens[i].Symbol, token.Symbol)
196 assert.Nil(t, token.USD)
197 assert.Nil(t, token.USDUpdate)
198 }
199}
200
201func TestAccounts(t *testing.T) {
202 const fromBlock int64 = 1
203 const toBlock int64 = 5
204 // Prepare blocks in the DB
205 blocks := setTestBlocks(fromBlock, toBlock)
206 // Generate fake tokens
207 const nTokens = 5
208 tokens, ethToken := test.GenTokens(nTokens, blocks)
209 err := historyDB.AddTokens(tokens)
210 assert.NoError(t, err)
211 tokens = append([]common.Token{ethToken}, tokens...)
212 // Generate fake batches
213 const nBatches = 10
214 batches := test.GenBatches(nBatches, blocks)
215 err = historyDB.AddBatches(batches)
216 assert.NoError(t, err)
217 // Generate fake accounts
218 const nAccounts = 3
219 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
220 err = historyDB.AddAccounts(accs)
221 assert.NoError(t, err)
222 // Fetch accounts
223 fetchedAccs, err := historyDB.GetAccounts()
224 assert.NoError(t, err)
225 // Compare fetched accounts vs generated accounts
226 for i, acc := range fetchedAccs {
227 accs[i].Balance = nil
228 assert.Equal(t, accs[i], acc)
229 }
230}
231
232func TestTxs(t *testing.T) {
233 const fromBlock int64 = 1
234 const toBlock int64 = 5
235 // Prepare blocks in the DB
236 blocks := setTestBlocks(fromBlock, toBlock)
237 // Generate fake tokens
238 const nTokens = 500
239 tokens, ethToken := test.GenTokens(nTokens, blocks)
240 err := historyDB.AddTokens(tokens)
241 assert.NoError(t, err)
242 tokens = append([]common.Token{ethToken}, tokens...)
243 // Generate fake batches
244 const nBatches = 10
245 batches := test.GenBatches(nBatches, blocks)
246 err = historyDB.AddBatches(batches)
247 assert.NoError(t, err)
248 // Generate fake accounts
249 const nAccounts = 3
250 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
251 err = historyDB.AddAccounts(accs)
252 assert.NoError(t, err)
253
254 /*
255 Uncomment once the transaction generation is fixed
256 !! test that batches that forge user L1s !!
257 !! Missing tests to check that historic USD is not set if USDUpdate is too old (24h) !!
258
259 // Generate fake L1 txs
260 const nL1s = 64
261 _, l1txs := test.GenL1Txs(256, nL1s, 0, nil, accs, tokens, blocks, batches)
262 err = historyDB.AddL1Txs(l1txs)
263 assert.NoError(t, err)
264 // Generate fake L2 txs
265 const nL2s = 2048 - nL1s
266 _, l2txs := test.GenL2Txs(256, nL2s, 0, nil, accs, tokens, blocks, batches)
267 err = historyDB.AddL2Txs(l2txs)
268 assert.NoError(t, err)
269 // Compare fetched txs vs generated txs.
270 fetchAndAssertTxs(t, l1txs, l2txs)
271 // Test trigger: L1 integrity
272 // from_eth_addr can't be null
273 l1txs[0].FromEthAddr = ethCommon.Address{}
274 err = historyDB.AddL1Txs(l1txs)
275 assert.Error(t, err)
276 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
277 // from_bjj can't be null
278 l1txs[0].FromBJJ = nil
279 err = historyDB.AddL1Txs(l1txs)
280 assert.Error(t, err)
281 privK := babyjub.NewRandPrivKey()
282 l1txs[0].FromBJJ = privK.Public()
283 // load_amount can't be null
284 l1txs[0].LoadAmount = nil
285 err = historyDB.AddL1Txs(l1txs)
286 assert.Error(t, err)
287 // Test trigger: L2 integrity
288 // batch_num can't be null
289 l2txs[0].BatchNum = 0
290 err = historyDB.AddL2Txs(l2txs)
291 assert.Error(t, err)
292 l2txs[0].BatchNum = 1
293 // nonce can't be null
294 l2txs[0].Nonce = 0
295 err = historyDB.AddL2Txs(l2txs)
296 assert.Error(t, err)
297 // Test trigger: forge L1 txs
298 // add next batch to DB
299 batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
300 batch := batches[0]
301 batch.BatchNum = batchNum
302 batch.ForgeL1TxsNum = toForgeL1TxsNum
303 assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
304 // Set batch num in txs that should have been marked as forged in the DB
305 for i := 0; i < len(l1txs); i++ {
306 fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
307 assert.NoError(t, err)
308 if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
309 assert.Equal(t, batchNum, *fetchedTx.BatchNum)
310 } else {
311 if fetchedTx.BatchNum != nil {
312 assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
313 }
314 }
315 }
316
317 // Test helper functions for Synchronizer
318 // GetLastTxsPosition
319 expectedPosition := -1
320 var choosenToForgeL1TxsNum int64 = -1
321 for _, tx := range l1txs {
322 if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
323 choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
324 expectedPosition = tx.Position
325 } else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
326 expectedPosition = tx.Position
327 }
328 }
329 position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
330 assert.NoError(t, err)
331 assert.Equal(t, expectedPosition, position)
332
333 // GetL1UserTxs: not needed? tests were broken
334 // txs, err := historyDB.GetL1UserTxs(2)
335 // assert.NoError(t, err)
336 // assert.NotZero(t, len(txs))
337 // assert.NoError(t, err)
338 // assert.Equal(t, 22, position)
339 // // Test Update L1 TX Batch_num
340 // assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
341 // txs[0].BatchNum = common.BatchNum(1)
342 // txs, err = historyDB.GetL1UserTxs(2)
343 // assert.NoError(t, err)
344 // assert.NotZero(t, len(txs))
345 // assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
346 */
347}
348
349/*
350func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
351 for i := 0; i < len(l1txs); i++ {
352 tx := l1txs[i].Tx()
353 fmt.Println("ASDF", i, tx.TxID)
354 fetchedTx, err := historyDB.GetTx(tx.TxID)
355 require.NoError(t, err)
356 test.AssertUSD(t, tx.USD, fetchedTx.USD)
357 test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
358 assert.Equal(t, tx, fetchedTx)
359 }
360 for i := 0; i < len(l2txs); i++ {
361 tx := l2txs[i].Tx()
362 fetchedTx, err := historyDB.GetTx(tx.TxID)
363 tx.TokenID = fetchedTx.TokenID
364 assert.NoError(t, err)
365 test.AssertUSD(t, fetchedTx.USD, tx.USD)
366 test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
367 assert.Equal(t, tx, fetchedTx)
368 }
369}
370*/
371
372func TestExitTree(t *testing.T) {
373 nBatches := 17
374 blocks := setTestBlocks(1, 10)
375 batches := test.GenBatches(nBatches, blocks)
376 err := historyDB.AddBatches(batches)
377 assert.NoError(t, err)
378 const nTokens = 50
379 tokens, ethToken := test.GenTokens(nTokens, blocks)
380 err = historyDB.AddTokens(tokens)
381 assert.NoError(t, err)
382 tokens = append([]common.Token{ethToken}, tokens...)
383 const nAccounts = 3
384 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
385 assert.NoError(t, historyDB.AddAccounts(accs))
386 exitTree := test.GenExitTree(nBatches, batches, accs)
387 err = historyDB.AddExitTree(exitTree)
388 assert.NoError(t, err)
389}
390
391func TestGetL1UserTxs(t *testing.T) {
392 test.WipeDB(historyDB.DB())
393
394 set := `
395 Type: Blockchain
396 AddToken(1)
397 AddToken(2)
398 AddToken(3)
399
400 CreateAccountDeposit(1) A: 20
401 CreateAccountDeposit(2) A: 20
402 CreateAccountDeposit(1) B: 5
403 CreateAccountDeposit(1) C: 5
404 CreateAccountDeposit(1) D: 5
405
406 > block
407 `
408 tc := til.NewContext(128)
409 blocks, err := tc.GenerateBlocks(set)
410 require.Nil(t, err)
411 // Sanity check
412 require.Equal(t, 1, len(blocks))
413 require.Equal(t, 5, len(blocks[0].Rollup.L1UserTxs))
414 // fmt.Printf("DBG Blocks: %+v\n", blocks)
415
416 toForgeL1TxsNum := int64(1)
417
418 for i := range blocks {
419 err = historyDB.AddBlockSCData(&blocks[i])
420 require.Nil(t, err)
421 }
422
423 l1UserTxs, err := historyDB.GetL1UserTxs(toForgeL1TxsNum)
424 require.Nil(t, err)
425 assert.Equal(t, 5, len(l1UserTxs))
426 assert.Equal(t, blocks[0].Rollup.L1UserTxs, l1UserTxs)
427
428 // No l1UserTxs for this toForgeL1TxsNum
429 l1UserTxs, err = historyDB.GetL1UserTxs(2)
430 require.Nil(t, err)
431 assert.Equal(t, 0, len(l1UserTxs))
432}
433
434func TestSetInitialSCVars(t *testing.T) {
435 test.WipeDB(historyDB.DB())
436 _, _, _, err := historyDB.GetSCVars()
437 assert.Equal(t, sql.ErrNoRows, err)
438
439 //nolint:govet
440 rollup := &common.RollupVariables{
441 0,
442 big.NewInt(10),
443 12,
444 13,
445 }
446 //nolint:govet
447 auction := &common.AuctionVariables{
448 0,
449 ethCommon.BigToAddress(big.NewInt(2)),
450 ethCommon.BigToAddress(big.NewInt(3)),
451 [6]*big.Int{
452 big.NewInt(1), big.NewInt(2), big.NewInt(3),
453 big.NewInt(4), big.NewInt(5), big.NewInt(6),
454 },
455 2,
456 4320,
457 [3]uint16{10, 11, 12},
458 1000,
459 20,
460 }
461 //nolint:govet
462 wDelayer := &common.WDelayerVariables{
463 0,
464 ethCommon.BigToAddress(big.NewInt(2)),
465 ethCommon.BigToAddress(big.NewInt(3)),
466 ethCommon.BigToAddress(big.NewInt(4)),
467 13,
468 14,
469 false,
470 }
471 err = historyDB.SetInitialSCVars(rollup, auction, wDelayer)
472 require.Nil(t, err)
473 dbRollup, dbAuction, dbWDelayer, err := historyDB.GetSCVars()
474 assert.Nil(t, err)
475 require.Equal(t, rollup, dbRollup)
476 require.Equal(t, auction, dbAuction)
477 require.Equal(t, wDelayer, dbWDelayer)
478}
479
480// setTestBlocks WARNING: this will delete the blocks and recreate them
481func setTestBlocks(from, to int64) []common.Block {
482 test.WipeDB(historyDB.DB())
483 blocks := test.GenBlocks(from, to)
484 if err := historyDB.AddBlocks(blocks); err != nil {
485 panic(err)
486 }
487 return blocks
488}