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 assert.Equal(t, accs[i], acc)
221 }
222}
223
224func TestTxs(t *testing.T) {
225 const fromBlock int64 = 1
226 const toBlock int64 = 5
227 // Prepare blocks in the DB
228 blocks := setTestBlocks(fromBlock, toBlock)
229 // Generate fake tokens
230 const nTokens = 500
231 tokens := test.GenTokens(nTokens, blocks)
232 err := historyDB.AddTokens(tokens)
233 assert.NoError(t, err)
234 // Generate fake batches
235 const nBatches = 10
236 batches := test.GenBatches(nBatches, blocks)
237 err = historyDB.AddBatches(batches)
238 assert.NoError(t, err)
239 // Generate fake accounts
240 const nAccounts = 3
241 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
242 err = historyDB.AddAccounts(accs)
243 assert.NoError(t, err)
244
245 /*
246 Uncomment once the transaction generation is fixed
247 !! test that batches that forge user L1s !!
248 !! Missing tests to check that historic USD is not set if USDUpdate is too old (24h) !!
249
250 // Generate fake L1 txs
251 const nL1s = 64
252 _, l1txs := test.GenL1Txs(256, nL1s, 0, nil, accs, tokens, blocks, batches)
253 err = historyDB.AddL1Txs(l1txs)
254 assert.NoError(t, err)
255 // Generate fake L2 txs
256 const nL2s = 2048 - nL1s
257 _, l2txs := test.GenL2Txs(256, nL2s, 0, nil, accs, tokens, blocks, batches)
258 err = historyDB.AddL2Txs(l2txs)
259 assert.NoError(t, err)
260 // Compare fetched txs vs generated txs.
261 fetchAndAssertTxs(t, l1txs, l2txs)
262 // Test trigger: L1 integrity
263 // from_eth_addr can't be null
264 l1txs[0].FromEthAddr = ethCommon.Address{}
265 err = historyDB.AddL1Txs(l1txs)
266 assert.Error(t, err)
267 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
268 // from_bjj can't be null
269 l1txs[0].FromBJJ = nil
270 err = historyDB.AddL1Txs(l1txs)
271 assert.Error(t, err)
272 privK := babyjub.NewRandPrivKey()
273 l1txs[0].FromBJJ = privK.Public()
274 // load_amount can't be null
275 l1txs[0].LoadAmount = nil
276 err = historyDB.AddL1Txs(l1txs)
277 assert.Error(t, err)
278 // Test trigger: L2 integrity
279 // batch_num can't be null
280 l2txs[0].BatchNum = 0
281 err = historyDB.AddL2Txs(l2txs)
282 assert.Error(t, err)
283 l2txs[0].BatchNum = 1
284 // nonce can't be null
285 l2txs[0].Nonce = 0
286 err = historyDB.AddL2Txs(l2txs)
287 assert.Error(t, err)
288 // Test trigger: forge L1 txs
289 // add next batch to DB
290 batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
291 batch := batches[0]
292 batch.BatchNum = batchNum
293 batch.ForgeL1TxsNum = toForgeL1TxsNum
294 assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
295 // Set batch num in txs that should have been marked as forged in the DB
296 for i := 0; i < len(l1txs); i++ {
297 fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
298 assert.NoError(t, err)
299 if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
300 assert.Equal(t, batchNum, *fetchedTx.BatchNum)
301 } else {
302 if fetchedTx.BatchNum != nil {
303 assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
304 }
305 }
306 }
307
308 // Test helper functions for Synchronizer
309 // GetLastTxsPosition
310 expectedPosition := -1
311 var choosenToForgeL1TxsNum int64 = -1
312 for _, tx := range l1txs {
313 if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
314 choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
315 expectedPosition = tx.Position
316 } else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
317 expectedPosition = tx.Position
318 }
319 }
320 position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
321 assert.NoError(t, err)
322 assert.Equal(t, expectedPosition, position)
323
324 // GetL1UserTxs: not needed? tests were broken
325 // txs, err := historyDB.GetL1UserTxs(2)
326 // assert.NoError(t, err)
327 // assert.NotZero(t, len(txs))
328 // assert.NoError(t, err)
329 // assert.Equal(t, 22, position)
330 // // Test Update L1 TX Batch_num
331 // assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
332 // txs[0].BatchNum = common.BatchNum(1)
333 // txs, err = historyDB.GetL1UserTxs(2)
334 // assert.NoError(t, err)
335 // assert.NotZero(t, len(txs))
336 // assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
337 */
338}
339
340/*
341func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
342 for i := 0; i < len(l1txs); i++ {
343 tx := l1txs[i].Tx()
344 fmt.Println("ASDF", i, tx.TxID)
345 fetchedTx, err := historyDB.GetTx(tx.TxID)
346 require.NoError(t, err)
347 test.AssertUSD(t, tx.USD, fetchedTx.USD)
348 test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
349 assert.Equal(t, tx, fetchedTx)
350 }
351 for i := 0; i < len(l2txs); i++ {
352 tx := l2txs[i].Tx()
353 fetchedTx, err := historyDB.GetTx(tx.TxID)
354 tx.TokenID = fetchedTx.TokenID
355 assert.NoError(t, err)
356 test.AssertUSD(t, fetchedTx.USD, tx.USD)
357 test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
358 assert.Equal(t, tx, fetchedTx)
359 }
360}
361*/
362
363func TestExitTree(t *testing.T) {
364 nBatches := 17
365 blocks := setTestBlocks(0, 10)
366 batches := test.GenBatches(nBatches, blocks)
367 err := historyDB.AddBatches(batches)
368 const nTokens = 50
369 tokens := test.GenTokens(nTokens, blocks)
370 assert.NoError(t, historyDB.AddTokens(tokens))
371 assert.NoError(t, err)
372 const nAccounts = 3
373 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
374 assert.NoError(t, historyDB.AddAccounts(accs))
375 exitTree := test.GenExitTree(nBatches, batches, accs)
376 err = historyDB.AddExitTree(exitTree)
377 assert.NoError(t, err)
378}
379
380// setTestBlocks WARNING: this will delete the blocks and recreate them
381func setTestBlocks(from, to int64) []common.Block {
382 if err := cleanHistoryDB(); err != nil {
383 panic(err)
384 }
385 blocks := test.GenBlocks(from, to)
386 if err := historyDB.AddBlocks(blocks); err != nil {
387 panic(err)
388 }
389 return blocks
390}
391
392func cleanHistoryDB() error {
393 return historyDB.Reorg(-1)
394}