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