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