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