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