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