hermez-node/db/l2db/l2db_test.go
db/l2db/l2db_test.goRaw
1package l2db
2
3import (
4 "math/big"
5 "os"
6 "testing"
7 "time"
8
9 "github.com/hermeznetwork/hermez-node/common"
10 dbUtils "github.com/hermeznetwork/hermez-node/db"
11 "github.com/hermeznetwork/hermez-node/db/historydb"
12 "github.com/hermeznetwork/hermez-node/log"
13 "github.com/hermeznetwork/hermez-node/test"
14 "github.com/jmoiron/sqlx"
15 "github.com/stretchr/testify/assert"
16)
17
18var l2DB *L2DB
19var tokens []common.Token
20var tokensUSD []historydb.TokenWithUSD
21
22func TestMain(m *testing.M) {
23 // init DB
24 pass := os.Getenv("POSTGRES_PASS")
25 db, err := dbUtils.InitSQLDB(5432, "localhost", "hermez", pass, "hermez")
26 if err != nil {
27 panic(err)
28 }
29 l2DB = NewL2DB(db, 10, 100, 24*time.Hour)
30 test.WipeDB(l2DB.DB())
31 tokens, tokensUSD = prepareHistoryDB(db)
32 // Run tests
33 result := m.Run()
34 // Close DB
35 if err := db.Close(); err != nil {
36 log.Error("Error closing the history DB:", err)
37 }
38 os.Exit(result)
39}
40
41func prepareHistoryDB(db *sqlx.DB) ([]common.Token, []historydb.TokenWithUSD) {
42 historyDB := historydb.NewHistoryDB(db)
43 const fromBlock int64 = 1
44 const toBlock int64 = 5
45 // Store blocks to historyDB
46 blocks := test.GenBlocks(fromBlock, toBlock)
47 if err := historyDB.AddBlocks(blocks); err != nil {
48 panic(err)
49 }
50 // Store tokens to historyDB
51 const nTokens = 5
52 tokens, ethToken := test.GenTokens(nTokens, blocks)
53 if err := historyDB.AddTokens(tokens); err != nil {
54 panic(err)
55 }
56 tokens = append([]common.Token{ethToken}, tokens...)
57 readTokens := []historydb.TokenWithUSD{}
58 for i, token := range tokens {
59 readToken := historydb.TokenWithUSD{
60 TokenID: token.TokenID,
61 EthBlockNum: token.EthBlockNum,
62 EthAddr: token.EthAddr,
63 Name: token.Name,
64 Symbol: token.Symbol,
65 Decimals: token.Decimals,
66 }
67 if i%2 != 0 {
68 value := float64(i) * 5.4321
69 if err := historyDB.UpdateTokenValue(token.Symbol, value); err != nil {
70 panic(err)
71 }
72 now := time.Now().UTC()
73 readToken.USDUpdate = &now
74 readToken.USD = &value
75 }
76 readTokens = append(readTokens, readToken)
77 }
78 return tokens, readTokens
79}
80
81func TestAddTxTest(t *testing.T) {
82 // Gen poolTxs
83 const nInserts = 20
84 test.WipeDB(l2DB.DB())
85 txs := test.GenPoolTxs(nInserts, tokens)
86 for _, tx := range txs {
87 err := l2DB.AddTxTest(tx)
88 assert.NoError(t, err)
89 fetchedTx, err := l2DB.GetTx(tx.TxID)
90 assert.NoError(t, err)
91 // assertReadTx(t, commonToRead(tx, tokens), fetchedTx)
92 assertTx(t, tx, fetchedTx)
93 nameZone, offset := fetchedTx.Timestamp.Zone()
94 assert.Equal(t, "UTC", nameZone)
95 assert.Equal(t, 0, offset)
96 }
97}
98
99func assertTx(t *testing.T, expected, actual *common.PoolL2Tx) {
100 // Check that timestamp has been set within the last 3 seconds
101 assert.Less(t, time.Now().UTC().Unix()-3, actual.Timestamp.Unix())
102 assert.GreaterOrEqual(t, time.Now().UTC().Unix(), actual.Timestamp.Unix())
103 expected.Timestamp = actual.Timestamp
104 // Check absolute fee
105 // find token
106 token := historydb.TokenWithUSD{}
107 for _, tkn := range tokensUSD {
108 if expected.TokenID == tkn.TokenID {
109 token = tkn
110 break
111 }
112 }
113 // If the token has value in USD setted
114 if token.USDUpdate != nil {
115 assert.Equal(t, token.USDUpdate.Unix(), actual.AbsoluteFeeUpdate.Unix())
116 expected.AbsoluteFeeUpdate = actual.AbsoluteFeeUpdate
117 // Set expected fee
118 f := new(big.Float).SetInt(expected.Amount)
119 amountF, _ := f.Float64()
120 expected.AbsoluteFee = *token.USD * amountF * expected.Fee.Percentage()
121 test.AssertUSD(t, &expected.AbsoluteFee, &actual.AbsoluteFee)
122 }
123 assert.Equal(t, expected, actual)
124}
125
126func BenchmarkAddTxTest(b *testing.B) {
127 const nInserts = 20
128 test.WipeDB(l2DB.DB())
129 txs := test.GenPoolTxs(nInserts, tokens)
130 now := time.Now()
131 for _, tx := range txs {
132 _ = l2DB.AddTxTest(tx)
133 }
134 elapsedTime := time.Since(now)
135 log.Info("Time to insert 2048 txs:", elapsedTime)
136}
137
138func TestGetPending(t *testing.T) {
139 const nInserts = 20
140 test.WipeDB(l2DB.DB())
141 txs := test.GenPoolTxs(nInserts, tokens)
142 var pendingTxs []*common.PoolL2Tx
143 for _, tx := range txs {
144 err := l2DB.AddTxTest(tx)
145 assert.NoError(t, err)
146 if tx.State == common.PoolL2TxStatePending {
147 pendingTxs = append(pendingTxs, tx)
148 }
149 }
150 fetchedTxs, err := l2DB.GetPendingTxs()
151 assert.NoError(t, err)
152 assert.Equal(t, len(pendingTxs), len(fetchedTxs))
153 for i := range fetchedTxs {
154 assertTx(t, pendingTxs[i], &fetchedTxs[i])
155 }
156}
157
158/*
159 WARNING: this should be fixed once transaktio is ready
160func TestStartForging(t *testing.T) {
161 // Generate txs
162 const nInserts = 60
163 const fakeBatchNum common.BatchNum = 33
164 test.WipeDB(l2DB.DB())
165 txs := test.GenPoolTxs(nInserts, tokens)
166 var startForgingTxIDs []common.TxID
167 randomizer := 0
168 // Add txs to DB
169 for _, tx := range txs {
170 err := l2DB.AddTxTest(tx)
171 assert.NoError(t, err)
172 if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 {
173 randomizer++
174 startForgingTxIDs = append(startForgingTxIDs, tx.TxID)
175 }
176 }
177 // Start forging txs
178 err := l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
179 assert.NoError(t, err)
180 // Fetch txs and check that they've been updated correctly
181 for _, id := range startForgingTxIDs {
182 fetchedTx, err := l2DB.GetTx(id)
183 assert.NoError(t, err)
184 assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State)
185 assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
186 }
187}
188*/
189
190/*
191 WARNING: this should be fixed once transaktio is ready
192func TestDoneForging(t *testing.T) {
193 // Generate txs
194 const nInserts = 60
195 const fakeBatchNum common.BatchNum = 33
196 test.WipeDB(l2DB.DB())
197 txs := test.GenPoolTxs(nInserts, tokens)
198 var doneForgingTxIDs []common.TxID
199 randomizer := 0
200 // Add txs to DB
201 for _, tx := range txs {
202 err := l2DB.AddTxTest(tx)
203 assert.NoError(t, err)
204 if tx.State == common.PoolL2TxStateForging && randomizer%2 == 0 {
205 randomizer++
206 doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID)
207 }
208 }
209 // Start forging txs
210 err := l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum)
211 assert.NoError(t, err)
212 // Fetch txs and check that they've been updated correctly
213 for _, id := range doneForgingTxIDs {
214 fetchedTx, err := l2DB.GetTx(id)
215 assert.NoError(t, err)
216 assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State)
217 assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
218 }
219}
220*/
221
222/*
223 WARNING: this should be fixed once transaktio is ready
224func TestInvalidate(t *testing.T) {
225 // Generate txs
226 const nInserts = 60
227 const fakeBatchNum common.BatchNum = 33
228 test.WipeDB(l2DB.DB())
229 txs := test.GenPoolTxs(nInserts, tokens)
230 var invalidTxIDs []common.TxID
231 randomizer := 0
232 // Add txs to DB
233 for _, tx := range txs {
234 err := l2DB.AddTxTest(tx)
235 assert.NoError(t, err)
236 if tx.State != common.PoolL2TxStateInvalid && randomizer%2 == 0 {
237 randomizer++
238 invalidTxIDs = append(invalidTxIDs, tx.TxID)
239 }
240 }
241 // Start forging txs
242 err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
243 assert.NoError(t, err)
244 // Fetch txs and check that they've been updated correctly
245 for _, id := range invalidTxIDs {
246 fetchedTx, err := l2DB.GetTx(id)
247 assert.NoError(t, err)
248 assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
249 assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
250 }
251}
252*/
253
254/*
255 WARNING: this should be fixed once transaktio is ready
256func TestCheckNonces(t *testing.T) {
257 // Generate txs
258 const nInserts = 60
259 const fakeBatchNum common.BatchNum = 33
260 test.WipeDB(l2DB.DB())
261 txs := test.GenPoolTxs(nInserts, tokens)
262 var invalidTxIDs []common.TxID
263 // Generate accounts
264 const nAccoutns = 2
265 const currentNonce = 2
266 accs := []common.Account{}
267 for i := 0; i < nAccoutns; i++ {
268 accs = append(accs, common.Account{
269 Idx: common.Idx(i),
270 Nonce: currentNonce,
271 })
272 }
273 // Add txs to DB
274 for i := 0; i < len(txs); i++ {
275 if txs[i].State != common.PoolL2TxStateInvalid {
276 if i%2 == 0 { // Ensure transaction will be marked as invalid due to old nonce
277 txs[i].Nonce = accs[i%len(accs)].Nonce
278 txs[i].FromIdx = accs[i%len(accs)].Idx
279 invalidTxIDs = append(invalidTxIDs, txs[i].TxID)
280 } else { // Ensure transaction will NOT be marked as invalid due to old nonce
281 txs[i].Nonce = currentNonce + 1
282 }
283 }
284 err := l2DB.AddTxTest(txs[i])
285 assert.NoError(t, err)
286 }
287 // Start forging txs
288 err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
289 assert.NoError(t, err)
290 // Fetch txs and check that they've been updated correctly
291 for _, id := range invalidTxIDs {
292 fetchedTx, err := l2DB.GetTx(id)
293 assert.NoError(t, err)
294 assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
295 assert.Equal(t, fakeBatchNum, *fetchedTx.BatchNum)
296 }
297}
298*/
299
300func TestReorg(t *testing.T) {
301 // Generate txs
302 const nInserts = 20
303 const lastValidBatch common.BatchNum = 20
304 const reorgBatch common.BatchNum = lastValidBatch + 1
305 test.WipeDB(l2DB.DB())
306 txs := test.GenPoolTxs(nInserts, tokens)
307 // Add txs to the DB
308 reorgedTxIDs := []common.TxID{}
309 nonReorgedTxIDs := []common.TxID{}
310 for i := 0; i < len(txs); i++ {
311 err := l2DB.AddTxTest(txs[i])
312 assert.NoError(t, err)
313 var batchNum common.BatchNum
314 if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid {
315 reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID)
316 batchNum = reorgBatch
317 } else {
318 nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID)
319 batchNum = lastValidBatch
320 }
321 _, err = l2DB.db.Exec(
322 "UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;",
323 batchNum, txs[i].TxID,
324 )
325 assert.NoError(t, err)
326 }
327 err := l2DB.Reorg(lastValidBatch)
328 assert.NoError(t, err)
329 for _, id := range reorgedTxIDs {
330 tx, err := l2DB.GetTxAPI(id)
331 assert.NoError(t, err)
332 assert.Nil(t, tx.BatchNum)
333 assert.Equal(t, common.PoolL2TxStatePending, tx.State)
334 }
335 for _, id := range nonReorgedTxIDs {
336 fetchedTx, err := l2DB.GetTxAPI(id)
337 assert.NoError(t, err)
338 assert.Equal(t, lastValidBatch, *fetchedTx.BatchNum)
339 }
340}
341
342func TestPurge(t *testing.T) {
343 /*
344 WARNING: this should be fixed once transaktio is ready
345 // Generate txs
346 nInserts := l2DB.maxTxs + 20
347 test.WipeDB(l2DB.DB())
348 txs := test.GenPoolTxs(int(nInserts), tokens)
349 deletedIDs := []common.TxID{}
350 keepedIDs := []common.TxID{}
351 const toDeleteBatchNum common.BatchNum = 30
352 safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
353 // Add txs to the DB
354 for i := 0; i < int(l2DB.maxTxs); i++ {
355 var batchNum common.BatchNum
356 if i%2 == 0 { // keep tx
357 batchNum = safeBatchNum
358 keepedIDs = append(keepedIDs, txs[i].TxID)
359 } else { // delete after safety period
360 batchNum = toDeleteBatchNum
361 if i%3 == 0 {
362 txs[i].State = common.PoolL2TxStateForged
363 } else {
364 txs[i].State = common.PoolL2TxStateInvalid
365 }
366 deletedIDs = append(deletedIDs, txs[i].TxID)
367 }
368 err := l2DB.AddTxTest(txs[i])
369 assert.NoError(t, err)
370 // Set batchNum
371 _, err = l2DB.db.Exec(
372 "UPDATE tx_pool SET batch_num = $1 WHERE tx_id = $2;",
373 batchNum, txs[i].TxID,
374 )
375 assert.NoError(t, err)
376 }
377 for i := int(l2DB.maxTxs); i < len(txs); i++ {
378 // Delete after TTL
379 deletedIDs = append(deletedIDs, txs[i].TxID)
380 err := l2DB.AddTxTest(txs[i])
381 assert.NoError(t, err)
382 // Set timestamp
383 deleteTimestamp := time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0)
384 _, err = l2DB.db.Exec(
385 "UPDATE tx_pool SET timestamp = $1 WHERE tx_id = $2;",
386 deleteTimestamp, txs[i].TxID,
387 )
388 assert.NoError(t, err)
389 }
390 // Purge txs
391 err := l2DB.Purge(safeBatchNum)
392 assert.NoError(t, err)
393 // Check results
394 for _, id := range deletedIDs {
395 tx, err := l2DB.GetTx(id)
396 if err == nil {
397 log.Debug(tx)
398 }
399 assert.Error(t, err)
400 }
401 for _, id := range keepedIDs {
402 _, err := l2DB.GetTx(id)
403 assert.NoError(t, err)
404 }
405 */
406}
407
408func TestAuth(t *testing.T) {
409 test.WipeDB(l2DB.DB())
410 const nAuths = 5
411 // Generate authorizations
412 auths := test.GenAuths(nAuths)
413 for i := 0; i < len(auths); i++ {
414 // Add to the DB
415 err := l2DB.AddAccountCreationAuth(auths[i])
416 assert.NoError(t, err)
417 // Fetch from DB
418 auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
419 assert.NoError(t, err)
420 // Check fetched vs generated
421 assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
422 assert.Equal(t, auths[i].BJJ, auth.BJJ)
423 assert.Equal(t, auths[i].Signature, auth.Signature)
424 assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
425 nameZone, offset := auths[i].Timestamp.Zone()
426 assert.Equal(t, "UTC", nameZone)
427 assert.Equal(t, 0, offset)
428 }
429}