hermez-node/db/historydb/historydb_test.go
db/historydb/historydb_test.goRaw
1package historydb
2
3import (
4 "fmt"
5 "math"
6 "math/big"
7 "os"
8 "testing"
9 "time"
10
11 ethCommon "github.com/ethereum/go-ethereum/common"
12 "github.com/hermeznetwork/hermez-node/common"
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 var err error
31 pass := os.Getenv("POSTGRES_PASS")
32 historyDB, err = NewHistoryDB(5432, "localhost", "hermez", pass, "history")
33 if err != nil {
34 panic(err)
35 }
36 // Run tests
37 result := m.Run()
38 // Close DB
39 if err := historyDB.Close(); err != nil {
40 fmt.Println("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 // Generate fake bids
124 const nBids = 20
125 bids := test.GenBids(nBids, blocks, coords)
126 err := historyDB.addBids(bids)
127 assert.NoError(t, err)
128 // Fetch bids
129 fetchedBids, err := historyDB.GetBids()
130 assert.NoError(t, err)
131 // Compare fetched bids vs generated bids
132 for i, bid := range fetchedBids {
133 assert.Equal(t, bids[i], *bid)
134 }
135}
136
137func TestTokens(t *testing.T) {
138 const fromBlock int64 = 1
139 const toBlock int64 = 5
140 // Prepare blocks in the DB
141 blocks := setTestBlocks(fromBlock, toBlock)
142 // Generate fake tokens
143 const nTokens = 5
144 tokens := test.GenTokens(nTokens, blocks)
145 err := historyDB.AddTokens(tokens)
146 assert.NoError(t, err)
147 // Update price of generated tokens without price
148 for i := 0; i < len(tokens); i++ {
149 if tokens[i].USD == 0 {
150 value := 3.33 + float32(i)
151 tokens[i].USD = value
152 err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
153 assert.NoError(t, err)
154 }
155 }
156 // Fetch tokens
157 fetchedTokens, err := historyDB.GetTokens()
158 assert.NoError(t, err)
159 // Compare fetched tokens vs generated tokens
160 // All the tokens should have USDUpdate setted by the DB trigger
161 for i, token := range fetchedTokens {
162 assert.Equal(t, tokens[i].TokenID, token.TokenID)
163 assert.Equal(t, tokens[i].EthBlockNum, token.EthBlockNum)
164 assert.Equal(t, tokens[i].EthAddr, token.EthAddr)
165 assert.Equal(t, tokens[i].Name, token.Name)
166 assert.Equal(t, tokens[i].Symbol, token.Symbol)
167 assert.Equal(t, tokens[i].USD, token.USD)
168 assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
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, 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 = 5
208 const tokenValue = 1.23456
209 tokens := test.GenTokens(nTokens, blocks)
210 for i := 0; i < len(tokens); i++ {
211 tokens[i].USD = tokenValue
212 }
213 err := historyDB.AddTokens(tokens)
214 assert.NoError(t, err)
215 // Generate fake batches
216 const nBatches = 10
217 batches := test.GenBatches(nBatches, blocks)
218 err = historyDB.AddBatches(batches)
219 assert.NoError(t, err)
220 // Generate fake accounts
221 const nAccounts = 3
222 accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
223 err = historyDB.AddAccounts(accs)
224 assert.NoError(t, err)
225 // Generate fake L1 txs
226 const nL1s = 30
227 _, l1txs := test.GenL1Txs(0, nL1s, 0, nil, accs, tokens, blocks, batches)
228 err = historyDB.AddL1Txs(l1txs)
229 assert.NoError(t, err)
230 // Generate fake L2 txs
231 const nL2s = 20
232 _, l2txs := test.GenL2Txs(0, nL2s, 0, nil, accs, tokens, blocks, batches)
233 err = historyDB.AddL2Txs(l2txs)
234 assert.NoError(t, err)
235 // Compare fetched txs vs generated txs.
236 for i := 0; i < len(l1txs); i++ {
237 tx := l1txs[i].Tx()
238 fetchedTx, err := historyDB.GetTx(tx.TxID)
239 assert.NoError(t, err)
240 tx.USD = tokenValue * tx.AmountFloat
241 if fetchedTx.USD > tx.USD {
242 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
243 } else {
244 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
245 }
246 tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
247 if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
248 assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
249 } else {
250 assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
251 }
252 tx.LoadAmountUSD = 0
253 tx.USD = 0
254 fetchedTx.LoadAmountUSD = 0
255 fetchedTx.USD = 0
256 assert.Equal(t, tx, fetchedTx)
257 }
258 for i := 0; i < len(l2txs); i++ {
259 tx := l2txs[i].Tx()
260 fetchedTx, err := historyDB.GetTx(tx.TxID)
261 assert.NoError(t, err)
262 tx.USD = tokenValue * tx.AmountFloat
263 if fetchedTx.USD > tx.USD {
264 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
265 } else {
266 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
267 }
268 if tx.Fee == 0 {
269 tx.FeeUSD = 0
270 } else if tx.Fee <= 32 {
271 tx.FeeUSD = tx.USD * math.Pow(10, -24+(float64(tx.Fee)/2))
272 } else if tx.Fee <= 223 {
273 tx.FeeUSD = tx.USD * math.Pow(10, -8+(0.041666666666667*(float64(tx.Fee)-32)))
274 } else {
275 tx.FeeUSD = tx.USD * math.Pow(10, float64(tx.Fee)-224)
276 }
277 if fetchedTx.FeeUSD > tx.FeeUSD {
278 assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
279 } else if fetchedTx.FeeUSD < tx.FeeUSD {
280 assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
281 }
282 tx.FeeUSD = 0
283 tx.USD = 0
284 fetchedTx.FeeUSD = 0
285 fetchedTx.USD = 0
286 assert.Equal(t, tx, fetchedTx)
287 }
288 // Test trigger: L1 integrity
289 // from_eth_addr can't be null
290 l1txs[0].FromEthAddr = ethCommon.Address{}
291 err = historyDB.AddL1Txs(l1txs)
292 assert.Error(t, err)
293 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
294 // from_bjj can't be null
295 l1txs[0].FromBJJ = nil
296 err = historyDB.AddL1Txs(l1txs)
297 assert.Error(t, err)
298 privK := babyjub.NewRandPrivKey()
299 l1txs[0].FromBJJ = privK.Public()
300 // load_amount can't be null
301 l1txs[0].LoadAmount = nil
302 err = historyDB.AddL1Txs(l1txs)
303 assert.Error(t, err)
304 // Test trigger: L2 integrity
305 // batch_num can't be null
306 l2txs[0].BatchNum = 0
307 err = historyDB.AddL2Txs(l2txs)
308 assert.Error(t, err)
309 l2txs[0].BatchNum = 1
310 // nonce can't be null
311 l2txs[0].Nonce = 0
312 err = historyDB.AddL2Txs(l2txs)
313 assert.Error(t, err)
314}
315
316// setTestBlocks WARNING: this will delete the blocks and recreate them
317func setTestBlocks(from, to int64) []common.Block {
318 if err := cleanHistoryDB(); err != nil {
319 panic(err)
320 }
321 blocks := test.GenBlocks(from, to)
322 if err := historyDB.AddBlocks(blocks); err != nil {
323 panic(err)
324 }
325 return blocks
326}
327
328func cleanHistoryDB() error {
329 return historyDB.Reorg(0)
330}