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