arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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}