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