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 // Add batches to the DB
93 err := historyDB.AddBatches(batches)
94 assert.NoError(t, err)
95 // Get batches from the DB
96 fetchedBatches, err := historyDB.GetBatches(0, common.BatchNum(nBatches))
97 assert.NoError(t, err)
98 for i, fetchedBatch := range fetchedBatches {
99 assert.Equal(t, batches[i], *fetchedBatch)
100 }
101 // Test GetLastBatchNum
102 fetchedLastBatchNum, err := historyDB.GetLastBatchNum()
103 assert.NoError(t, err)
104 assert.Equal(t, batches[len(batches)-1].BatchNum, fetchedLastBatchNum)
105 // Test GetLastL1TxsNum
106 fetchedLastL1TxsNum, err := historyDB.GetLastL1TxsNum()
107 assert.NoError(t, err)
108 assert.Equal(t, batches[nBatches-1].ForgeL1TxsNum, fetchedLastL1TxsNum)
109}
110
111func TestBids(t *testing.T) {
112 const fromBlock int64 = 1
113 const toBlock int64 = 5
114 // Prepare blocks in the DB
115 blocks := setTestBlocks(fromBlock, toBlock)
116 // Generate fake coordinators
117 const nCoords = 5
118 coords := test.GenCoordinators(nCoords, blocks)
119 // Generate fake bids
120 const nBids = 20
121 bids := test.GenBids(nBids, blocks, coords)
122 err := historyDB.addBids(bids)
123 assert.NoError(t, err)
124 // Fetch bids
125 fetchedBids, err := historyDB.GetBids()
126 assert.NoError(t, err)
127 // Compare fetched bids vs generated bids
128 for i, bid := range fetchedBids {
129 assert.Equal(t, bids[i], *bid)
130 }
131}
132
133func TestTokens(t *testing.T) {
134 const fromBlock int64 = 1
135 const toBlock int64 = 5
136 // Prepare blocks in the DB
137 blocks := setTestBlocks(fromBlock, toBlock)
138 // Generate fake tokens
139 const nTokens = 5
140 tokens := test.GenTokens(nTokens, blocks)
141 err := historyDB.AddTokens(tokens)
142 assert.NoError(t, err)
143 // Update price of generated tokens without price
144 for i := 0; i < len(tokens); i++ {
145 if tokens[i].USD == 0 {
146 value := 3.33 + float32(i)
147 tokens[i].USD = value
148 err := historyDB.UpdateTokenValue(tokens[i].TokenID, value)
149 assert.NoError(t, err)
150 }
151 }
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 assert.Greater(t, int64(1*time.Second), int64(time.Since(token.USDUpdate)))
165 }
166}
167
168func TestAccounts(t *testing.T) {
169 const fromBlock int64 = 1
170 const toBlock int64 = 5
171 // Prepare blocks in the DB
172 blocks := setTestBlocks(fromBlock, toBlock)
173 // Generate fake tokens
174 const nTokens = 5
175 tokens := test.GenTokens(nTokens, blocks)
176 err := historyDB.AddTokens(tokens)
177 assert.NoError(t, err)
178 // Generate fake batches
179 const nBatches = 10
180 batches := test.GenBatches(nBatches, blocks)
181 err = historyDB.AddBatches(batches)
182 assert.NoError(t, err)
183 // Generate fake accounts
184 const nAccounts = 3
185 accs := test.GenAccounts(nAccounts, 0, tokens, nil, batches)
186 err = historyDB.AddAccounts(accs)
187 assert.NoError(t, err)
188 // Fetch accounts
189 fetchedAccs, err := historyDB.GetAccounts()
190 assert.NoError(t, err)
191 // Compare fetched accounts vs generated accounts
192 for i, acc := range fetchedAccs {
193 assert.Equal(t, accs[i], *acc)
194 }
195}
196
197func TestTxs(t *testing.T) {
198 const fromBlock int64 = 1
199 const toBlock int64 = 5
200 // Prepare blocks in the DB
201 blocks := setTestBlocks(fromBlock, toBlock)
202 // Generate fake tokens
203 const nTokens = 5
204 const tokenValue = 1.23456
205 tokens := test.GenTokens(nTokens, blocks)
206 for i := 0; i < len(tokens); i++ {
207 tokens[i].USD = tokenValue
208 }
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, batches)
219 err = historyDB.AddAccounts(accs)
220 assert.NoError(t, err)
221 // Generate fake L1 txs
222 const nL1s = 30
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 = 20
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 for i := 0; i < len(l1txs); i++ {
233 tx := l1txs[i].Tx()
234 fetchedTx, err := historyDB.GetTx(tx.TxID)
235 assert.NoError(t, err)
236 tx.USD = tokenValue * tx.AmountFloat
237 if fetchedTx.USD > tx.USD {
238 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
239 } else {
240 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
241 }
242 tx.LoadAmountUSD = tokenValue * tx.LoadAmountFloat
243 if fetchedTx.LoadAmountUSD > tx.LoadAmountUSD {
244 assert.Less(t, 0.999, tx.LoadAmountUSD/fetchedTx.LoadAmountUSD)
245 } else {
246 assert.Less(t, 0.999, fetchedTx.LoadAmountUSD/tx.LoadAmountUSD)
247 }
248 tx.LoadAmountUSD = 0
249 tx.USD = 0
250 fetchedTx.LoadAmountUSD = 0
251 fetchedTx.USD = 0
252 assert.Equal(t, tx, fetchedTx)
253 }
254 for i := 0; i < len(l2txs); i++ {
255 tx := l2txs[i].Tx()
256 fetchedTx, err := historyDB.GetTx(tx.TxID)
257 assert.NoError(t, err)
258 tx.USD = tokenValue * tx.AmountFloat
259 if fetchedTx.USD > tx.USD {
260 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
261 } else {
262 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
263 }
264 if tx.Fee == 0 {
265 tx.FeeUSD = 0
266 } else if tx.Fee <= 32 {
267 tx.FeeUSD = tx.USD * math.Pow(10, -24+(float64(tx.Fee)/2))
268 } else if tx.Fee <= 223 {
269 tx.FeeUSD = tx.USD * math.Pow(10, -8+(0.041666666666667*(float64(tx.Fee)-32)))
270 } else {
271 tx.FeeUSD = tx.USD * math.Pow(10, float64(tx.Fee)-224)
272 }
273 if fetchedTx.FeeUSD > tx.FeeUSD {
274 assert.Less(t, 0.999, tx.FeeUSD/fetchedTx.FeeUSD)
275 } else if fetchedTx.FeeUSD < tx.FeeUSD {
276 assert.Less(t, 0.999, fetchedTx.FeeUSD/tx.FeeUSD)
277 }
278 tx.FeeUSD = 0
279 tx.USD = 0
280 fetchedTx.FeeUSD = 0
281 fetchedTx.USD = 0
282 assert.Equal(t, tx, fetchedTx)
283 }
284 // Test trigger: L1 integrity
285 // from_eth_addr can't be null
286 l1txs[0].FromEthAddr = ethCommon.Address{}
287 err = historyDB.AddL1Txs(l1txs)
288 assert.Error(t, err)
289 l1txs[0].FromEthAddr = ethCommon.BigToAddress(big.NewInt(int64(5)))
290 // from_bjj can't be null
291 l1txs[0].FromBJJ = nil
292 err = historyDB.AddL1Txs(l1txs)
293 assert.Error(t, err)
294 privK := babyjub.NewRandPrivKey()
295 l1txs[0].FromBJJ = privK.Public()
296 // load_amount can't be null
297 l1txs[0].LoadAmount = nil
298 err = historyDB.AddL1Txs(l1txs)
299 assert.Error(t, err)
300 // Test trigger: L2 integrity
301 // batch_num can't be null
302 l2txs[0].BatchNum = 0
303 err = historyDB.AddL2Txs(l2txs)
304 assert.Error(t, err)
305 l2txs[0].BatchNum = 1
306 // nonce can't be null
307 l2txs[0].Nonce = 0
308 err = historyDB.AddL2Txs(l2txs)
309 assert.Error(t, err)
310}
311
312// setTestBlocks WARNING: this will delete the blocks and recreate them
313func setTestBlocks(from, to int64) []common.Block {
314 if err := cleanHistoryDB(); err != nil {
315 panic(err)
316 }
317 blocks := test.GenBlocks(from, to)
318 if err := historyDB.AddBlocks(blocks); err != nil {
319 panic(err)
320 }
321 return blocks
322}
323
324func cleanHistoryDB() error {
325 return historyDB.Reorg(0)
326}