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