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 assert.NoError(t, historyDB.Reorg(fromBlock-1))
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 := test.GenTokens(nTokens, blocks)
120 err = historyDB.AddTokens(tokens)
121 assert.NoError(t, err)
122 feeBatch := batches[0]
123 feeBatch.BatchNum = 9999
124 feeBatch.CollectedFees = make(map[common.TokenID]*big.Int)
125 var total float64
126 for i, token := range tokens {
127 value := 3.019237 * float64(i)
128 assert.NoError(t, historyDB.UpdateTokenValue(token.Symbol, value))
129 bigAmount := big.NewInt(345000000)
130 feeBatch.CollectedFees[token.TokenID] = bigAmount
131 f := new(big.Float).SetInt(bigAmount)
132 amount, _ := f.Float64()
133 total += value * (amount / math.Pow(10, float64(token.Decimals)))
134 }
135 err = historyDB.AddBatch(&feeBatch)
136 assert.NoError(t, err)
137 fetchedBatches, err = historyDB.GetBatches(feeBatch.BatchNum-1, feeBatch.BatchNum+1)
138 assert.NoError(t, err)
139 for _, fetchedBatch := range fetchedBatches {
140 if fetchedBatch.BatchNum == feeBatch.BatchNum {
141 assert.Equal(t, total, *fetchedBatch.TotalFeesUSD)
142 }
143 }
144}
145
146func TestBids(t *testing.T) {
147 const fromBlock int64 = 1
148 const toBlock int64 = 5
149 // Prepare blocks in the DB
150 blocks := setTestBlocks(fromBlock, toBlock)
151 // Generate fake coordinators
152 const nCoords = 5
153 coords := test.GenCoordinators(nCoords, blocks)
154 err := historyDB.AddCoordinators(coords)
155 assert.NoError(t, err)
156 // Generate fake bids
157 const nBids = 20
158 bids := test.GenBids(nBids, blocks, coords)
159 err = historyDB.AddBids(bids)
160 assert.NoError(t, err)
161 // Fetch bids
162 fetchedBids, err := historyDB.GetBids()
163 assert.NoError(t, err)
164 // Compare fetched bids vs generated bids
165 for i, bid := range fetchedBids {
166 assert.Equal(t, bids[i], bid)
167 }
168}
169
170func TestTokens(t *testing.T) {
171 const fromBlock int64 = 1
172 const toBlock int64 = 5
173 // Prepare blocks in the DB
174 blocks := setTestBlocks(fromBlock, toBlock)
175 // Generate fake tokens
176 const nTokens = 5
177 tokens := test.GenTokens(nTokens, blocks)
178 err := historyDB.AddTokens(tokens)
179 assert.NoError(t, err)
180 limit := uint(10)
181 // Fetch tokens6
182 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
183 assert.NoError(t, err)
184 // Compare fetched tokens vs generated tokens
185 // All the tokens should have USDUpdate setted by the DB trigger
186 for i, token := range fetchedTokens {
187 assert.Equal(t, tokens[i].TokenID, token.TokenID)
188 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
189 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
190 assert.Equal(t, tokens[i].Name, token.Name)
191 assert.Equal(t, tokens[i].Symbol, token.Symbol)
192 assert.Nil(t, token.USD)
193 assert.Nil(t, token.USDUpdate)
194 }
195}
196
197func TestAccounts(t *testing.T) {
198 const fromBlock int64 = 1
199 const toBlock int64 = 5
200 // Prepare blocks in the DB
201 blocks := setTestBlocks(fromBlock, toBlock)
202 // Generate fake tokens
203 const nTokens = 5
204 tokens := test.GenTokens(nTokens, blocks)
205 err := historyDB.AddTokens(tokens)
206 assert.NoError(t, err)
207 // Generate fake batches
208 const nBatches = 10
209 batches := test.GenBatches(nBatches, blocks)
210 err = historyDB.AddBatches(batches)
211 assert.NoError(t, err)
212 // Generate fake accounts
213 const nAccounts = 3
214 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
215 err = historyDB.AddAccounts(accs)
216 assert.NoError(t, err)
217 // Fetch accounts
218 fetchedAccs, err := historyDB.GetAccounts()
219 assert.NoError(t, err)
220 // Compare fetched accounts vs generated accounts
221 for i, acc := range fetchedAccs {
222 accs[i].Balance = nil
223 assert.Equal(t, accs[i], acc)
224 }
225}
226
227func TestTxs(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 tokens
233 const nTokens = 500
234 tokens := test.GenTokens(nTokens, blocks)
235 err := historyDB.AddTokens(tokens)
236 assert.NoError(t, err)
237 // Generate fake batches
238 const nBatches = 10
239 batches := test.GenBatches(nBatches, blocks)
240 err = historyDB.AddBatches(batches)
241 assert.NoError(t, err)
242 // Generate fake accounts
243 const nAccounts = 3
244 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
245 err = historyDB.AddAccounts(accs)
246 assert.NoError(t, err)
247
248 /*
249 Uncomment once the transaction generation is fixed
250 !! test that batches that forge user L1s !!
251 !! Missing tests to check that historic USD is not set if USDUpdate is too old (24h) !!
252
253 // Generate fake L1 txs
254 const nL1s = 64
255 _, l1txs := test.GenL1Txs(256, nL1s, 0, nil, accs, tokens, blocks, batches)
256 err = historyDB.AddL1Txs(l1txs)
257 assert.NoError(t, err)
258 // Generate fake L2 txs
259 const nL2s = 2048 - nL1s
260 _, l2txs := test.GenL2Txs(256, nL2s, 0, nil, accs, tokens, blocks, batches)
261 err = historyDB.AddL2Txs(l2txs)
262 assert.NoError(t, err)
263 // Compare fetched txs vs generated txs.
264 fetchAndAssertTxs(t, l1txs, l2txs)
265 // Test trigger: L1 integrity
266 // from_eth_addr can't be null
267 l1txs[0].FromEthAddr = ethCommon.Address{}
268 err = historyDB.AddL1Txs(l1txs)
269 assert.Error(t, err)
270 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
271 // from_bjj can't be null
272 l1txs[0].FromBJJ = nil
273 err = historyDB.AddL1Txs(l1txs)
274 assert.Error(t, err)
275 privK := babyjub.NewRandPrivKey()
276 l1txs[0].FromBJJ = privK.Public()
277 // load_amount can't be null
278 l1txs[0].LoadAmount = nil
279 err = historyDB.AddL1Txs(l1txs)
280 assert.Error(t, err)
281 // Test trigger: L2 integrity
282 // batch_num can't be null
283 l2txs[0].BatchNum = 0
284 err = historyDB.AddL2Txs(l2txs)
285 assert.Error(t, err)
286 l2txs[0].BatchNum = 1
287 // nonce can't be null
288 l2txs[0].Nonce = 0
289 err = historyDB.AddL2Txs(l2txs)
290 assert.Error(t, err)
291 // Test trigger: forge L1 txs
292 // add next batch to DB
293 batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
294 batch := batches[0]
295 batch.BatchNum = batchNum
296 batch.ForgeL1TxsNum = toForgeL1TxsNum
297 assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
298 // Set batch num in txs that should have been marked as forged in the DB
299 for i := 0; i < len(l1txs); i++ {
300 fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
301 assert.NoError(t, err)
302 if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
303 assert.Equal(t, batchNum, *fetchedTx.BatchNum)
304 } else {
305 if fetchedTx.BatchNum != nil {
306 assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
307 }
308 }
309 }
310
311 // Test helper functions for Synchronizer
312 // GetLastTxsPosition
313 expectedPosition := -1
314 var choosenToForgeL1TxsNum int64 = -1
315 for _, tx := range l1txs {
316 if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
317 choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
318 expectedPosition = tx.Position
319 } else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
320 expectedPosition = tx.Position
321 }
322 }
323 position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
324 assert.NoError(t, err)
325 assert.Equal(t, expectedPosition, position)
326
327 // GetL1UserTxs: not needed? tests were broken
328 // txs, err := historyDB.GetL1UserTxs(2)
329 // assert.NoError(t, err)
330 // assert.NotZero(t, len(txs))
331 // assert.NoError(t, err)
332 // assert.Equal(t, 22, position)
333 // // Test Update L1 TX Batch_num
334 // assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
335 // txs[0].BatchNum = common.BatchNum(1)
336 // txs, err = historyDB.GetL1UserTxs(2)
337 // assert.NoError(t, err)
338 // assert.NotZero(t, len(txs))
339 // assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
340 */
341}
342
343/*
344func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
345 for i := 0; i < len(l1txs); i++ {
346 tx := l1txs[i].Tx()
347 fmt.Println("ASDF", i, tx.TxID)
348 fetchedTx, err := historyDB.GetTx(tx.TxID)
349 require.NoError(t, err)
350 test.AssertUSD(t, tx.USD, fetchedTx.USD)
351 test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
352 assert.Equal(t, tx, fetchedTx)
353 }
354 for i := 0; i < len(l2txs); i++ {
355 tx := l2txs[i].Tx()
356 fetchedTx, err := historyDB.GetTx(tx.TxID)
357 tx.TokenID = fetchedTx.TokenID
358 assert.NoError(t, err)
359 test.AssertUSD(t, fetchedTx.USD, tx.USD)
360 test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
361 assert.Equal(t, tx, fetchedTx)
362 }
363}
364*/
365
366func TestExitTree(t *testing.T) {
367 nBatches := 17
368 blocks := setTestBlocks(0, 10)
369 batches := test.GenBatches(nBatches, blocks)
370 err := historyDB.AddBatches(batches)
371 const nTokens = 50
372 tokens := test.GenTokens(nTokens, blocks)
373 assert.NoError(t, historyDB.AddTokens(tokens))
374 assert.NoError(t, err)
375 const nAccounts = 3
376 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
377 assert.NoError(t, historyDB.AddAccounts(accs))
378 exitTree := test.GenExitTree(nBatches, batches, accs)
379 err = historyDB.AddExitTree(exitTree)
380 assert.NoError(t, err)
381}
382
383func TestGetL1UserTxs(t *testing.T) {
384 require.NoError(t, cleanHistoryDB())
385
386 set := `
387 Type: Blockchain
388 AddToken(1)
389 AddToken(2)
390 AddToken(3)
391
392 CreateAccountDeposit(1) A: 20
393 CreateAccountDeposit(2) A: 20
394 CreateAccountDeposit(1) B: 5
395 CreateAccountDeposit(1) C: 5
396 CreateAccountDeposit(1) D: 5
397
398 > block
399 `
400 tc := til.NewContext(128)
401 blocks, err := tc.GenerateBlocks(set)
402 require.Nil(t, err)
403 // Sanity check
404 require.Equal(t, 1, len(blocks))
405 require.Equal(t, 5, len(blocks[0].L1UserTxs))
406 // fmt.Printf("DBG Blocks: %+v\n", blocks)
407
408 toForgeL1TxsNum := int64(1)
409
410 for i := range blocks {
411 err = historyDB.AddBlockSCData(&blocks[i])
412 require.Nil(t, err)
413 }
414
415 l1UserTxs, err := historyDB.GetL1UserTxs(toForgeL1TxsNum)
416 require.Nil(t, err)
417 assert.Equal(t, 5, len(l1UserTxs))
418 assert.Equal(t, blocks[0].L1UserTxs, l1UserTxs)
419
420 // No l1UserTxs for this toForgeL1TxsNum
421 l1UserTxs, err = historyDB.GetL1UserTxs(2)
422 require.Nil(t, err)
423 assert.Equal(t, 0, len(l1UserTxs))
424}
425
426// setTestBlocks WARNING: this will delete the blocks and recreate them
427func setTestBlocks(from, to int64) []common.Block {
428 if err := cleanHistoryDB(); err != nil {
429 panic(err)
430 }
431 blocks := test.GenBlocks(from, to)
432 if err := historyDB.AddBlocks(blocks); err != nil {
433 panic(err)
434 }
435 return blocks
436}
437
438func cleanHistoryDB() error {
439 return historyDB.Reorg(-1)
440}