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