hermez-node/db/l2db/l2db_test.go
db/l2db/l2db_test.goRaw
1package l2db
2
3import (
4 "fmt"
5 "math"
6 "os"
7 "testing"
8 "time"
9
10 "github.com/hermeznetwork/hermez-node/common"
11 "github.com/hermeznetwork/hermez-node/log"
12 "github.com/hermeznetwork/hermez-node/test"
13 "github.com/stretchr/testify/assert"
14)
15
16var l2DB *L2DB
17
18func TestMain(m *testing.M) {
19 // init DB
20 var err error
21 pass := os.Getenv("POSTGRES_PASS")
22 l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 100, 24*time.Hour)
23 if err != nil {
24 log.Error("L2DB migration failed: " + err.Error())
25 panic(err)
26 } else {
27 log.Debug("L2DB migration succed")
28 }
29 // Run tests
30 result := m.Run()
31 // Close DB
32 if err := l2DB.Close(); err != nil {
33 fmt.Println("Error closing the history DB:", err)
34 }
35 os.Exit(result)
36}
37
38func TestAddTx(t *testing.T) {
39 const nInserts = 20
40 test.CleanL2DB(l2DB.DB())
41 txs := test.GenPoolTxs(nInserts)
42 for _, tx := range txs {
43 err := l2DB.AddTx(tx)
44 assert.NoError(t, err)
45 fetchedTx, err := l2DB.GetTx(tx.TxID)
46 assert.NoError(t, err)
47 assert.Equal(t, tx.Timestamp.Unix(), fetchedTx.Timestamp.Unix())
48 tx.Timestamp = fetchedTx.Timestamp
49 assert.Equal(t, tx.AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix())
50 tx.Timestamp = fetchedTx.Timestamp
51 tx.AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate
52 assert.Equal(t, tx, fetchedTx)
53 }
54}
55
56func BenchmarkAddTx(b *testing.B) {
57 const nInserts = 20
58 test.CleanL2DB(l2DB.DB())
59 txs := test.GenPoolTxs(nInserts)
60 now := time.Now()
61 for _, tx := range txs {
62 _ = l2DB.AddTx(tx)
63 }
64 elapsedTime := time.Since(now)
65 fmt.Println("Time to insert 2048 txs:", elapsedTime)
66}
67
68func TestGetPending(t *testing.T) {
69 const nInserts = 20
70 test.CleanL2DB(l2DB.DB())
71 txs := test.GenPoolTxs(nInserts)
72 var pendingTxs []*common.PoolL2Tx
73 for _, tx := range txs {
74 err := l2DB.AddTx(tx)
75 assert.NoError(t, err)
76 if tx.State == common.PoolL2TxStatePending {
77 pendingTxs = append(pendingTxs, tx)
78 }
79 }
80 fetchedTxs, err := l2DB.GetPendingTxs()
81 assert.NoError(t, err)
82 assert.Equal(t, len(pendingTxs), len(fetchedTxs))
83 for i, fetchedTx := range fetchedTxs {
84 assert.Equal(t, pendingTxs[i].Timestamp.Unix(), fetchedTx.Timestamp.Unix())
85 pendingTxs[i].Timestamp = fetchedTx.Timestamp
86 assert.Equal(t, pendingTxs[i].AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix())
87 pendingTxs[i].AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate
88 assert.Equal(t, pendingTxs[i], fetchedTx)
89 }
90}
91
92func TestStartForging(t *testing.T) {
93 // Generate txs
94 const nInserts = 60
95 const fakeBatchNum common.BatchNum = 33
96 test.CleanL2DB(l2DB.DB())
97 txs := test.GenPoolTxs(nInserts)
98 var startForgingTxIDs []common.TxID
99 randomizer := 0
100 // Add txs to DB
101 for _, tx := range txs {
102 err := l2DB.AddTx(tx)
103 assert.NoError(t, err)
104 if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 {
105 randomizer++
106 startForgingTxIDs = append(startForgingTxIDs, tx.TxID)
107 }
108 }
109 // Start forging txs
110 err := l2DB.StartForging(startForgingTxIDs, fakeBatchNum)
111 assert.NoError(t, err)
112 // Fetch txs and check that they've been updated correctly
113 for _, id := range startForgingTxIDs {
114 fetchedTx, err := l2DB.GetTx(id)
115 assert.NoError(t, err)
116 assert.Equal(t, common.PoolL2TxStateForging, fetchedTx.State)
117 assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
118 }
119}
120
121func TestDoneForging(t *testing.T) {
122 // Generate txs
123 const nInserts = 60
124 const fakeBatchNum common.BatchNum = 33
125 test.CleanL2DB(l2DB.DB())
126 txs := test.GenPoolTxs(nInserts)
127 var doneForgingTxIDs []common.TxID
128 randomizer := 0
129 // Add txs to DB
130 for _, tx := range txs {
131 err := l2DB.AddTx(tx)
132 assert.NoError(t, err)
133 if tx.State == common.PoolL2TxStateForging && randomizer%2 == 0 {
134 randomizer++
135 doneForgingTxIDs = append(doneForgingTxIDs, tx.TxID)
136 }
137 }
138 // Start forging txs
139 err := l2DB.DoneForging(doneForgingTxIDs, fakeBatchNum)
140 assert.NoError(t, err)
141 // Fetch txs and check that they've been updated correctly
142 for _, id := range doneForgingTxIDs {
143 fetchedTx, err := l2DB.GetTx(id)
144 assert.NoError(t, err)
145 assert.Equal(t, common.PoolL2TxStateForged, fetchedTx.State)
146 assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
147 }
148}
149
150func TestInvalidate(t *testing.T) {
151 // Generate txs
152 const nInserts = 60
153 const fakeBatchNum common.BatchNum = 33
154 test.CleanL2DB(l2DB.DB())
155 txs := test.GenPoolTxs(nInserts)
156 var invalidTxIDs []common.TxID
157 randomizer := 0
158 // Add txs to DB
159 for _, tx := range txs {
160 err := l2DB.AddTx(tx)
161 assert.NoError(t, err)
162 if tx.State != common.PoolL2TxStateInvalid && randomizer%2 == 0 {
163 randomizer++
164 invalidTxIDs = append(invalidTxIDs, tx.TxID)
165 }
166 }
167 // Start forging txs
168 err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
169 assert.NoError(t, err)
170 // Fetch txs and check that they've been updated correctly
171 for _, id := range invalidTxIDs {
172 fetchedTx, err := l2DB.GetTx(id)
173 assert.NoError(t, err)
174 assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
175 assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
176 }
177}
178
179func TestCheckNonces(t *testing.T) {
180 // Generate txs
181 const nInserts = 60
182 const fakeBatchNum common.BatchNum = 33
183 test.CleanL2DB(l2DB.DB())
184 txs := test.GenPoolTxs(nInserts)
185 var invalidTxIDs []common.TxID
186 // Generate accounts
187 const nAccoutns = 2
188 const currentNonce = 2
189 accs := []common.Account{}
190 for i := 0; i < nAccoutns; i++ {
191 accs = append(accs, common.Account{
192 Idx: common.Idx(i),
193 Nonce: currentNonce,
194 })
195 }
196 // Add txs to DB
197 for i := 0; i < len(txs); i++ {
198 if txs[i].State != common.PoolL2TxStateInvalid {
199 if i%2 == 0 { // Ensure transaction will be marked as invalid due to old nonce
200 txs[i].Nonce = accs[i%len(accs)].Nonce
201 txs[i].FromIdx = accs[i%len(accs)].Idx
202 invalidTxIDs = append(invalidTxIDs, txs[i].TxID)
203 } else { // Ensure transaction will NOT be marked as invalid due to old nonce
204 txs[i].Nonce = currentNonce + 1
205 }
206 }
207 err := l2DB.AddTx(txs[i])
208 assert.NoError(t, err)
209 }
210 // Start forging txs
211 err := l2DB.InvalidateTxs(invalidTxIDs, fakeBatchNum)
212 assert.NoError(t, err)
213 // Fetch txs and check that they've been updated correctly
214 for _, id := range invalidTxIDs {
215 fetchedTx, err := l2DB.GetTx(id)
216 assert.NoError(t, err)
217 assert.Equal(t, common.PoolL2TxStateInvalid, fetchedTx.State)
218 assert.Equal(t, fakeBatchNum, fetchedTx.BatchNum)
219 }
220}
221
222func TestUpdateTxValue(t *testing.T) {
223 // Generate txs
224 const nInserts = 255 // Force all possible fee selector values
225 test.CleanL2DB(l2DB.DB())
226 txs := test.GenPoolTxs(nInserts)
227 // Generate tokens
228 const nTokens = 2
229 tokens := []common.Token{}
230 for i := 0; i < nTokens; i++ {
231 tokens = append(tokens, common.Token{
232 TokenID: common.TokenID(i),
233 USD: float64(i) * 1.3,
234 })
235 }
236 // Add txs to DB
237 for i := 0; i < len(txs); i++ {
238 // Set Token
239 token := tokens[i%len(tokens)]
240 txs[i].TokenID = token.TokenID
241 // Insert to DB
242 err := l2DB.AddTx(txs[i])
243 assert.NoError(t, err)
244 // Set USD values (for comparing results when fetching from DB)
245 txs[i].USD = txs[i].AmountFloat * token.USD
246 if txs[i].Fee == 0 {
247 txs[i].AbsoluteFee = 0
248 } else if txs[i].Fee <= 32 {
249 txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, -24+(float64(txs[i].Fee)/2))
250 } else if txs[i].Fee <= 223 {
251 txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, -8+(0.041666666666667*(float64(txs[i].Fee)-32)))
252 } else {
253 txs[i].AbsoluteFee = txs[i].USD * math.Pow(10, float64(txs[i].Fee)-224)
254 }
255 }
256 // Update token value
257 err := l2DB.UpdateTxValue(tokens)
258 assert.NoError(t, err)
259 // Fetch txs and check that they've been updated correctly
260 for _, tx := range txs {
261 fetchedTx, err := l2DB.GetTx(tx.TxID)
262 assert.NoError(t, err)
263 if fetchedTx.USD > tx.USD {
264 assert.Less(t, 0.999, tx.USD/fetchedTx.USD)
265 } else if fetchedTx.USD < tx.USD {
266 assert.Less(t, 0.999, fetchedTx.USD/tx.USD)
267 }
268 if fetchedTx.AbsoluteFee > tx.AbsoluteFee {
269 assert.Less(t, 0.999, tx.AbsoluteFee/fetchedTx.AbsoluteFee)
270 } else if fetchedTx.AbsoluteFee < tx.AbsoluteFee {
271 assert.Less(t, 0.999, fetchedTx.AbsoluteFee/tx.AbsoluteFee)
272 }
273 // Time is set in the DB, so it cannot be compared exactly
274 assert.Greater(t, float64(15*time.Second), time.Since(fetchedTx.AbsoluteFeeUpdate).Seconds())
275 }
276}
277
278func TestReorg(t *testing.T) {
279 // Generate txs
280 const nInserts = 20
281 const lastValidBatch common.BatchNum = 20
282 const reorgBatch common.BatchNum = lastValidBatch + 1
283 test.CleanL2DB(l2DB.DB())
284 txs := test.GenPoolTxs(nInserts)
285 // Add txs to the DB
286 reorgedTxIDs := []common.TxID{}
287 nonReorgedTxIDs := []common.TxID{}
288 for i := 0; i < len(txs); i++ {
289 if txs[i].State == common.PoolL2TxStateForged || txs[i].State == common.PoolL2TxStateInvalid {
290 txs[i].BatchNum = reorgBatch
291 reorgedTxIDs = append(reorgedTxIDs, txs[i].TxID)
292 } else {
293 txs[i].BatchNum = lastValidBatch
294 nonReorgedTxIDs = append(nonReorgedTxIDs, txs[i].TxID)
295 }
296 err := l2DB.AddTx(txs[i])
297 assert.NoError(t, err)
298 }
299 err := l2DB.Reorg(lastValidBatch)
300 assert.NoError(t, err)
301 var nullBatchNum common.BatchNum
302 for _, id := range reorgedTxIDs {
303 tx, err := l2DB.GetTx(id)
304 assert.NoError(t, err)
305 assert.Equal(t, nullBatchNum, tx.BatchNum)
306 assert.Equal(t, common.PoolL2TxStatePending, tx.State)
307 }
308 for _, id := range nonReorgedTxIDs {
309 tx, err := l2DB.GetTx(id)
310 assert.NoError(t, err)
311 assert.Equal(t, lastValidBatch, tx.BatchNum)
312 }
313}
314
315func TestPurge(t *testing.T) {
316 // Generate txs
317 nInserts := l2DB.maxTxs + 20
318 test.CleanL2DB(l2DB.DB())
319 txs := test.GenPoolTxs(int(nInserts))
320 deletedIDs := []common.TxID{}
321 keepedIDs := []common.TxID{}
322 const toDeleteBatchNum common.BatchNum = 30
323 safeBatchNum := toDeleteBatchNum + l2DB.safetyPeriod + 1
324 // Add txs to the DB
325 for i := 0; i < int(l2DB.maxTxs); i++ {
326 if i%1 == 0 { // keep tx
327 txs[i].BatchNum = safeBatchNum
328 keepedIDs = append(keepedIDs, txs[i].TxID)
329 } else if i%2 == 0 { // delete after safety period
330 txs[i].BatchNum = toDeleteBatchNum
331 if i%3 == 0 {
332 txs[i].State = common.PoolL2TxStateForged
333 } else {
334 txs[i].State = common.PoolL2TxStateInvalid
335 }
336 deletedIDs = append(deletedIDs, txs[i].TxID)
337 }
338 err := l2DB.AddTx(txs[i])
339 assert.NoError(t, err)
340 }
341 for i := int(l2DB.maxTxs); i < len(txs); i++ {
342 // Delete after TTL
343 txs[i].Timestamp = time.Unix(time.Now().UTC().Unix()-int64(l2DB.ttl.Seconds()+float64(4*time.Second)), 0)
344 deletedIDs = append(deletedIDs, txs[i].TxID)
345 err := l2DB.AddTx(txs[i])
346 assert.NoError(t, err)
347 }
348 // Purge txs
349 err := l2DB.Purge(safeBatchNum - 1)
350 assert.NoError(t, err)
351 // Check results
352 for _, id := range deletedIDs {
353 tx, err := l2DB.GetTx(id)
354 if err == nil {
355 log.Debug(tx)
356 }
357 assert.Error(t, err)
358 }
359 for _, id := range keepedIDs {
360 _, err := l2DB.GetTx(id)
361 assert.NoError(t, err)
362 }
363}
364
365func TestAuth(t *testing.T) {
366 test.CleanL2DB(l2DB.DB())
367 const nAuths = 5
368 // Generate authorizations
369 auths := test.GenAuths(nAuths)
370 for i := 0; i < len(auths); i++ {
371 // Add to the DB
372 err := l2DB.AddAccountCreationAuth(auths[i])
373 assert.NoError(t, err)
374 // Fetch from DB
375 auth, err := l2DB.GetAccountCreationAuth(auths[i].EthAddr)
376 assert.NoError(t, err)
377 // Check fetched vs generated
378 assert.Equal(t, auths[i].EthAddr, auth.EthAddr)
379 assert.Equal(t, auths[i].BJJ, auth.BJJ)
380 assert.Equal(t, auths[i].Signature, auth.Signature)
381 assert.Equal(t, auths[i].Timestamp.Unix(), auths[i].Timestamp.Unix())
382 }
383}