hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
1package historydb
2
3import (
4 "fmt"
5 "math/big"
6 "os"
7 "testing"
8 "time"
9
10 ethCommon "github.com/ethereum/go-ethereum/common"
11 "github.com/hermeznetwork/hermez-node/common"
12 "github.com/hermeznetwork/hermez-node/test"
13 "github.com/iden3/go-iden3-crypto/babyjub"
14 "github.com/stretchr/testify/assert"
15)
16
17var historyDB *HistoryDB
18
19// In order to run the test you need to run a Posgres DB with
20// a database named "history" that is accessible by
21// user: "hermez"
22// pass: set it using the env var POSTGRES_PASS
23// 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;"
24// After running the test you can stop the container by running: sudo docker kill hermez-db-test
25// If you already did that for the L2DB you don't have to do it again
26
27func TestMain(m *testing.M) {
28 // init DB
29 var err error
30 pass := os.Getenv("POSTGRES_PASS")
31 historyDB, err = NewHistoryDB(5432, "localhost", "hermez", pass, "history")
32 if err != nil {
33 panic(err)
34 }
35 // Run tests
36 result := m.Run()
37 // Close DB
38 if err := historyDB.Close(); err != nil {
39 fmt.Println("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, fetchedBlock := range fetchedBlocks {
63 assertEqualBlock(t, &blocks[i], fetchedBlock)
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 // Update price of generated tokens without price
149 for i := 0; i < len(tokens); i++ {
150 if tokens[i].USD == 0 {
151 value := 3.33 + float64(i)
152 tokens[i].USD = value
153 err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
154 assert.NoError(t, err)
155 }
156 }
157 // Fetch tokens
158 fetchedTokens, err := historyDB.GetTokens()
159 assert.NoError(t, err)
160 // Compare fetched tokens vs generated tokens
161 // All the tokens should have USDUpdate setted by the DB trigger
162 for i, token := range fetchedTokens {
163 assert.Equal(t, tokens[i].TokenID, token.TokenID)
164 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
165 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
166 assert.Equal(t, tokens[i].Name, token.Name)
167 assert.Equal(t, tokens[i].Symbol, token.Symbol)
168 assert.Equal(t, tokens[i].USD, token.USD)
169 assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
170 }
171}
172
173func TestAccounts(t *testing.T) {
174 const fromBlock int64 = 1
175 const toBlock int64 = 5
176 // Prepare blocks in the DB
177 blocks := setTestBlocks(fromBlock, toBlock)
178 // Generate fake tokens
179 const nTokens = 5
180 tokens := test.GenTokens(nTokens, blocks)
181 err := historyDB.AddTokens(tokens)
182 assert.NoError(t, err)
183 // Generate fake batches
184 const nBatches = 10
185 batches := test.GenBatches(nBatches, blocks)
186 err = historyDB.AddBatches(batches)
187 assert.NoError(t, err)
188 // Generate fake accounts
189 const nAccounts = 3
190 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
191 err = historyDB.AddAccounts(accs)
192 assert.NoError(t, err)
193 // Fetch accounts
194 fetchedAccs, err := historyDB.GetAccounts()
195 assert.NoError(t, err)
196 // Compare fetched accounts vs generated accounts
197 for i, acc := range fetchedAccs {
198 assert.Equal(t, accs[i], *acc)
199 }
200}
201
202func TestTxs(t *testing.T) {
203 const fromBlock int64 = 1
204 const toBlock int64 = 5
205 // Prepare blocks in the DB
206 blocks := setTestBlocks(fromBlock, toBlock)
207 // Generate fake tokens
208 const nTokens = 5
209 const tokenValue = 1.23456
210 tokens := test.GenTokens(nTokens, blocks)
211 for i := 0; i < len(tokens); i++ {
212 tokens[i].USD = tokenValue
213 }
214 err := historyDB.AddTokens(tokens)
215 assert.NoError(t, err)
216 // Generate fake batches
217 const nBatches = 10
218 batches := test.GenBatches(nBatches, blocks)
219 err = historyDB.AddBatches(batches)
220 assert.NoError(t, err)
221 // Generate fake accounts
222 const nAccounts = 3
223 accs := test.GenAccounts(nAccounts, 0, tokens, nil, nil, batches)
224 err = historyDB.AddAccounts(accs)
225 assert.NoError(t, err)
226 // Generate fake L1 txs
227 const nL1s = 30
228 _, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
229 err = historyDB.AddL1Txs(l1txs)
230 assert.NoError(t, err)
231 // Generate fake L2 txs
232 const nL2s = 20
233 _, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
234 err = historyDB.AddL2Txs(l2txs)
235 assert.NoError(t, err)
236 // Compare fetched txs vs generated txs.
237 for i := 0; i < len(l1txs); i++ {
238 tx := l1txs[i].Tx()
239 fetchedTx, err := historyDB.GetTx(tx.TxID)
240 assert.NoError(t, err)
241 tx.USD = tokenValue * tx.AmountFloat
242 if fetchedTx.USD > tx.USD {
243 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
244 } else {
245 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
246 }
247 tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
248 if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
249 assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
250 } else {
251 assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
252 }
253 tx.LoadAmountUSD = 0
254 tx.USD = 0
255 fetchedTx.LoadAmountUSD = 0
256 fetchedTx.USD = 0
257 assert.Equal(t, tx, fetchedTx)
258 }
259 for i := 0; i < len(l2txs); i++ {
260 tx := l2txs[i].Tx()
261 fetchedTx, err := historyDB.GetTx(tx.TxID)
262 assert.NoError(t, err)
263 tx.USD = tokenValue * tx.AmountFloat
264 if fetchedTx.USD > tx.USD {
265 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
266 } else {
267 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
268 }
269 tx.FeeUSD = tx.USD * tx.Fee.Percentage()
270 if fetchedTx.FeeUSD > tx.FeeUSD {
271 assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
272 } else if fetchedTx.FeeUSD < tx.FeeUSD {
273 assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
274 }
275 tx.FeeUSD = 0
276 tx.USD = 0
277 fetchedTx.FeeUSD = 0
278 fetchedTx.USD = 0
279 assert.Equal(t, tx, fetchedTx)
280 }
281 // Test trigger: L1 integrity
282 // from_eth_addr can't be null
283 l1txs[0].FromEthAddr = ethCommon.Address{}
284 err = historyDB.AddL1Txs(l1txs)
285 assert.Error(t, err)
286 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
287 // from_bjj can't be null
288 l1txs[0].FromBJJ = nil
289 err = historyDB.AddL1Txs(l1txs)
290 assert.Error(t, err)
291 privK := babyjub.NewRandPrivKey()
292 l1txs[0].FromBJJ = privK.Public()
293 // load_amount can't be null
294 l1txs[0].LoadAmount = nil
295 err = historyDB.AddL1Txs(l1txs)
296 assert.Error(t, err)
297 // Test trigger: L2 integrity
298 // batch_num can't be null
299 l2txs[0].BatchNum = 0
300 err = historyDB.AddL2Txs(l2txs)
301 assert.Error(t, err)
302 l2txs[0].BatchNum = 1
303 // nonce can't be null
304 l2txs[0].Nonce = 0
305 err = historyDB.AddL2Txs(l2txs)
306 assert.Error(t, err)
307 // Test helper functions for Synchronizer
308 txs, err := historyDB.GetL1UserTxs(2)
309 assert.NoError(t, err)
310 assert.NotZero(t, len(txs))
311 position, err := historyDB.GetLastTxsPosition(2)
312 assert.NoError(t, err)
313 assert.Equal(t, 22, position)
314 // Test Update L1 TX Batch_num
315 assert.Equal(t, common.BatchNum(0), txs[0].BatchNum)
316 txs[0].BatchNum = common.BatchNum(1)
317 // err = historyDB.UpdateTxsBatchNum(txs)
318 err = historyDB.SetBatchNumL1UserTxs(2, 1)
319 assert.NoError(t, err)
320 txs, err = historyDB.GetL1UserTxs(2)
321 assert.NoError(t, err)
322 assert.NotZero(t, len(txs))
323 assert.Equal(t, common.BatchNum(1), txs[0].BatchNum)
324}
325
326func TestExitTree(t *testing.T) {
327 nBatches := 17
328 blocks := setTestBlocks(0, 10)
329 batches := test.GenBatches(nBatches, blocks)
330 err := historyDB.AddBatches(batches)
331 assert.NoError(t, err)
332
333 exitTree := test.GenExitTree(nBatches)
334 err = historyDB.AddExitTree(exitTree)
335 assert.NoError(t, err)
336}
337
338// setTestBlocks WARNING: this will delete the blocks and recreate them
339func setTestBlocks(from, to int64) []common.Block {
340 if err := cleanHistoryDB(); err != nil {
341 panic(err)
342 }
343 blocks := test.GenBlocks(from, to)
344 if err := historyDB.AddBlocks(blocks); err != nil {
345 panic(err)
346 }
347 return blocks
348}
349
350func cleanHistoryDB() error {
351 return historyDB.Reorg(-1)
352}