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 limit := uint(10)
149 // Fetch tokens6
150 fetchedTokens, _, err := historyDB.GetTokens(nil, nil, "", nil, &limit, OrderAsc)
151 assert.NoError(t, err)
152 // Compare fetched tokens vs generated tokens
153 // All the tokens should have USDUpdate setted by the DB trigger
154 for i, token := range fetchedTokens {
155 assert.Equal(t, tokens[i].TokenID, token.TokenID)
156 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
157 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
158 assert.Equal(t, tokens[i].Name, token.Name)
159 assert.Equal(t, tokens[i].Symbol, token.Symbol)
160 assert.Nil(t, token.USD)
161 assert.Nil(t, token.USDUpdate)
162 }
163}
164
165func TestAccounts(t *testing.T) {
166 const fromBlock int64 = 1
167 const toBlock int64 = 5
168 // Prepare blocks in the DB
169 blocks := setTestBlocks(fromBlock, toBlock)
170 // Generate fake tokens
171 const nTokens = 5
172 tokens := test.GenTokens(nTokens, blocks)
173 err := historyDB.AddTokens(tokens)
174 assert.NoError(t, err)
175 // Generate fake batches
176 const nBatches = 10
177 batches := test.GenBatches(nBatches, blocks)
178 err = historyDB.AddBatches(batches)
179 assert.NoError(t, err)
180 // Generate fake accounts
181 const nAccounts = 3
182 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
183 err = historyDB.AddAccounts(accs)
184 assert.NoError(t, err)
185 // Fetch accounts
186 fetchedAccs, err := historyDB.GetAccounts()
187 assert.NoError(t, err)
188 // Compare fetched accounts vs generated accounts
189 for i, acc := range fetchedAccs {
190 assert.Equal(t, accs[i], acc)
191 }
192}
193
194func TestTxs(t *testing.T) {
195 const fromBlock int64 = 1
196 const toBlock int64 = 5
197 // Prepare blocks in the DB
198 blocks := setTestBlocks(fromBlock, toBlock)
199 // Generate fake tokens
200 const nTokens = 500
201 tokens := test.GenTokens(nTokens, blocks)
202 err := historyDB.AddTokens(tokens)
203 assert.NoError(t, err)
204 // Generate fake batches
205 const nBatches = 10
206 batches := test.GenBatches(nBatches, blocks)
207 err = historyDB.AddBatches(batches)
208 assert.NoError(t, err)
209 // Generate fake accounts
210 const nAccounts = 3
211 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
212 err = historyDB.AddAccounts(accs)
213 assert.NoError(t, err)
214
215 /*
216 Uncomment once the transaction generation is fixed
217 !! test that batches that forge user L1s !!
218 !! Missing tests to check that historic USD is not set if USDUpdate is too old (24h) !!
219
220 // Generate fake L1 txs
221 const nL1s = 64
222 _, l1txs := test.GenL1Txs(256, nL1s, 0, nil, accs, tokens, blocks, batches)
223 err = historyDB.AddL1Txs(l1txs)
224 assert.NoError(t, err)
225 // Generate fake L2 txs
226 const nL2s = 2048 - nL1s
227 _, l2txs := test.GenL2Txs(256, nL2s, 0, nil, accs, tokens, blocks, batches)
228 err = historyDB.AddL2Txs(l2txs)
229 assert.NoError(t, err)
230 // Compare fetched txs vs generated txs.
231 fetchAndAssertTxs(t, l1txs, l2txs)
232 // Test trigger: L1 integrity
233 // from_eth_addr can't be null
234 l1txs[0].FromEthAddr = ethCommon.Address{}
235 err = historyDB.AddL1Txs(l1txs)
236 assert.Error(t, err)
237 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
238 // from_bjj can't be null
239 l1txs[0].FromBJJ = nil
240 err = historyDB.AddL1Txs(l1txs)
241 assert.Error(t, err)
242 privK := babyjub.NewRandPrivKey()
243 l1txs[0].FromBJJ = privK.Public()
244 // load_amount can't be null
245 l1txs[0].LoadAmount = nil
246 err = historyDB.AddL1Txs(l1txs)
247 assert.Error(t, err)
248 // Test trigger: L2 integrity
249 // batch_num can't be null
250 l2txs[0].BatchNum = 0
251 err = historyDB.AddL2Txs(l2txs)
252 assert.Error(t, err)
253 l2txs[0].BatchNum = 1
254 // nonce can't be null
255 l2txs[0].Nonce = 0
256 err = historyDB.AddL2Txs(l2txs)
257 assert.Error(t, err)
258 // Test trigger: forge L1 txs
259 // add next batch to DB
260 batchNum, toForgeL1TxsNum := test.GetNextToForgeNumAndBatch(batches)
261 batch := batches[0]
262 batch.BatchNum = batchNum
263 batch.ForgeL1TxsNum = toForgeL1TxsNum
264 assert.NoError(t, historyDB.AddBatch(&batch)) // This should update nL1s / 2 rows
265 // Set batch num in txs that should have been marked as forged in the DB
266 for i := 0; i < len(l1txs); i++ {
267 fetchedTx, err := historyDB.GetTx(l1txs[i].TxID)
268 assert.NoError(t, err)
269 if l1txs[i].ToForgeL1TxsNum == toForgeL1TxsNum {
270 assert.Equal(t, batchNum, *fetchedTx.BatchNum)
271 } else {
272 if fetchedTx.BatchNum != nil {
273 assert.NotEqual(t, batchNum, *fetchedTx.BatchNum)
274 }
275 }
276 }
277
278 // Test helper functions for Synchronizer
279 // GetLastTxsPosition
280 expectedPosition := -1
281 var choosenToForgeL1TxsNum int64 = -1
282 for _, tx := range l1txs {
283 if choosenToForgeL1TxsNum == -1 && tx.ToForgeL1TxsNum > 0 {
284 choosenToForgeL1TxsNum = tx.ToForgeL1TxsNum
285 expectedPosition = tx.Position
286 } else if choosenToForgeL1TxsNum == tx.ToForgeL1TxsNum && expectedPosition < tx.Position {
287 expectedPosition = tx.Position
288 }
289 }
290 position, err := historyDB.GetLastTxsPosition(choosenToForgeL1TxsNum)
291 assert.NoError(t, err)
292 assert.Equal(t, expectedPosition, position)
293
294 // GetL1UserTxs: not needed? tests were broken
295 // txs, err := historyDB.GetL1UserTxs(2)
296 // assert.NoError(t, err)
297 // assert.NotZero(t, len(txs))
298 // assert.NoError(t, err)
299 // assert.Equal(t, 22, position)
300 // // Test Update L1 TX Batch_num
301 // assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
302 // txs[0].BatchNum = common.BatchNum(1)
303 // txs, err = historyDB.GetL1UserTxs(2)
304 // assert.NoError(t, err)
305 // assert.NotZero(t, len(txs))
306 // assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
307 */
308}
309
310/*
311func fetchAndAssertTxs(t *testing.T, l1txs []common.L1Tx, l2txs []common.L2Tx) {
312 for i := 0; i < len(l1txs); i++ {
313 tx := l1txs[i].Tx()
314 fmt.Println("ASDF", i, tx.TxID)
315 fetchedTx, err := historyDB.GetTx(tx.TxID)
316 require.NoError(t, err)
317 test.AssertUSD(t, tx.USD, fetchedTx.USD)
318 test.AssertUSD(t, tx.LoadAmountUSD, fetchedTx.LoadAmountUSD)
319 assert.Equal(t, tx, fetchedTx)
320 }
321 for i := 0; i < len(l2txs); i++ {
322 tx := l2txs[i].Tx()
323 fetchedTx, err := historyDB.GetTx(tx.TxID)
324 tx.TokenID = fetchedTx.TokenID
325 assert.NoError(t, err)
326 test.AssertUSD(t, fetchedTx.USD, tx.USD)
327 test.AssertUSD(t, fetchedTx.FeeUSD, tx.FeeUSD)
328 assert.Equal(t, tx, fetchedTx)
329 }
330}
331*/
332
333func TestExitTree(t *testing.T) {
334 nBatches := 17
335 blocks := setTestBlocks(0, 10)
336 batches := test.GenBatches(nBatches, blocks)
337 err := historyDB.AddBatches(batches)
338 const nTokens = 50
339 tokens := test.GenTokens(nTokens, blocks)
340 assert.NoError(t, historyDB.AddTokens(tokens))
341 assert.NoError(t, err)
342 const nAccounts = 3
343 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
344 assert.NoError(t, historyDB.AddAccounts(accs))
345 exitTree := test.GenExitTree(nBatches, batches, accs)
346 err = historyDB.AddExitTree(exitTree)
347 assert.NoError(t, err)
348}
349
350// setTestBlocks WARNING: this will delete the blocks and recreate them
351func setTestBlocks(from, to int64) []common.Block {
352 if err := cleanHistoryDB(); err != nil {
353 panic(err)
354 }
355 blocks := test.GenBlocks(from, to)
356 if err := historyDB.AddBlocks(blocks); err != nil {
357 panic(err)
358 }
359 return blocks
360}
361
362func cleanHistoryDB() error {
363 return historyDB.Reorg(-1)
364}