hermez-node/db/l2db/l2db_test.go
db/l2db/l2db_test.goRaw
1package l2db
2
3import (
4 "fmt"
5 "math/big"
6 "os"
7 "strconv"
8 "testing"
9 "time"
10
11 eth "github.com/ethereum/go-ethereum/common"
12 "github.com/hermeznetwork/hermez-node/common"
13 "github.com/iden3/go-iden3-crypto/babyjub"
14 "github.com/stretchr/testify/assert"
15)
16
17var l2DB *L2DB
18
19// In order to run the test you need to run a Posgres DB with
20// a database named "l2" that is accessible by
21// user: "hermez"
22// pass: set it using the env var POSTGRES_PASS
23// This can be achieved by running: POSTGRES_PASS=your_strong_pass && sudo docker run --rm --name hermez-db-test -p 5432:5432 -e POSTGRES_DB=history -e POSTGRES_USER=hermez -e POSTGRES_PASSWORD=$POSTGRES_PASS -d postgres && sleep 2s && sudo docker exec -it hermez-db-test psql -a history -U hermez -c "CREATE DATABASE l2;"
24// After running the test you can stop the container by running: sudo docker kill hermez-ydb-test
25// If you already did that for the HistoryDB you don't have to do it again
26func TestMain(m *testing.M) {
27 // init DB
28 var err error
29 pass := os.Getenv("POSTGRES_PASS")
30 l2DB, err = NewL2DB(5432, "localhost", "hermez", pass, "l2", 10, 512, 24*time.Hour)
31 if err != nil {
32 panic(err)
33 }
34 // Run tests
35 result := m.Run()
36 // Close DB
37 if err := l2DB.Close(); err != nil {
38 fmt.Println("Error closing the history DB:", err)
39 }
40 os.Exit(result)
41}
42
43func TestAddTx(t *testing.T) {
44 const nInserts = 20
45 cleanDB()
46 txs := genTxs(nInserts)
47 for _, tx := range txs {
48 err := l2DB.AddTx(tx)
49 assert.NoError(t, err)
50 fetchedTx, err := l2DB.GetTx(tx.TxID)
51 assert.NoError(t, err)
52 assert.Equal(t, tx.Timestamp.Unix(), fetchedTx.Timestamp.Unix())
53 tx.Timestamp = fetchedTx.Timestamp
54 assert.Equal(t, tx.AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix())
55 tx.Timestamp = fetchedTx.Timestamp
56 tx.AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate
57 assert.Equal(t, tx, fetchedTx)
58 }
59}
60
61func BenchmarkAddTx(b *testing.B) {
62 const nInserts = 20
63 cleanDB()
64 txs := genTxs(nInserts)
65 now := time.Now()
66 for _, tx := range txs {
67 _ = l2DB.AddTx(tx)
68 }
69 elapsedTime := time.Since(now)
70 fmt.Println("Time to insert 2048 txs:", elapsedTime)
71}
72
73func TestGetPending(t *testing.T) {
74 const nInserts = 20
75 cleanDB()
76 txs := genTxs(nInserts)
77 var pendingTxs []*common.PoolL2Tx
78 for _, tx := range txs {
79 err := l2DB.AddTx(tx)
80 assert.NoError(t, err)
81 if tx.State == common.PoolL2TxStatePending {
82 pendingTxs = append(pendingTxs, tx)
83 }
84 }
85 fetchedTxs, err := l2DB.GetPendingTxs()
86 assert.NoError(t, err)
87 assert.Equal(t, len(pendingTxs), len(fetchedTxs))
88 for i, fetchedTx := range fetchedTxs {
89 assert.Equal(t, pendingTxs[i].Timestamp.Unix(), fetchedTx.Timestamp.Unix())
90 pendingTxs[i].Timestamp = fetchedTx.Timestamp
91 assert.Equal(t, pendingTxs[i].AbsoluteFeeUpdate.Unix(), fetchedTx.AbsoluteFeeUpdate.Unix())
92 pendingTxs[i].AbsoluteFeeUpdate = fetchedTx.AbsoluteFeeUpdate
93 assert.Equal(t, pendingTxs[i], fetchedTx)
94 }
95}
96
97func TestStartForging(t *testing.T) {
98 const nInserts = 24
99 const fakeBlockNum = 33
100 cleanDB()
101 txs := genTxs(nInserts)
102 var startForgingTxs []*common.PoolL2Tx
103 var startForgingTxIDs []common.TxID
104 randomizer := 0
105 for _, tx := range txs {
106 err := l2DB.AddTx(tx)
107 assert.NoError(t, err)
108 if tx.State == common.PoolL2TxStatePending && randomizer%2 == 0 {
109 randomizer++
110 startForgingTxs = append(startForgingTxs, tx)
111 startForgingTxIDs = append(startForgingTxIDs, tx.TxID)
112 }
113 if tx.State == common.PoolL2TxStateForging {
114 startForgingTxs = append(startForgingTxs, tx)
115 }
116 }
117 fmt.Println(startForgingTxs) // TODO added print here to avoid lint complaining about startForgingTxs not being used
118 err := l2DB.StartForging(startForgingTxIDs, fakeBlockNum)
119 assert.NoError(t, err)
120 // TODO: Fetch txs and check that they've been updated correctly
121}
122
123func genTxs(n int) []*common.PoolL2Tx {
124 // WARNING: This tx doesn't follow the protocol (signature, txID, ...)
125 // it's just to test geting/seting from/to the DB.
126 // Type and RqTxCompressedData: not initialized because it's not stored
127 // on the DB and add noise when checking results.
128 txs := make([]*common.PoolL2Tx, 0, n)
129 privK := babyjub.NewRandPrivKey()
130 for i := 0; i < n; i++ {
131 var state common.PoolL2TxState
132 if i%4 == 0 {
133 state = common.PoolL2TxStatePending
134 } else if i%4 == 1 {
135 state = common.PoolL2TxStateInvalid
136 } else if i%4 == 2 {
137 state = common.PoolL2TxStateForging
138 } else if i%4 == 3 {
139 state = common.PoolL2TxStateForged
140 }
141 tx := &common.PoolL2Tx{
142 TxID: common.TxID(common.Hash([]byte(strconv.Itoa(i)))),
143 FromIdx: 47,
144 ToIdx: 96,
145 ToEthAddr: eth.BigToAddress(big.NewInt(234523534)),
146 ToBJJ: privK.Public(),
147 TokenID: 73,
148 Amount: big.NewInt(3487762374627846747),
149 Fee: 99,
150 Nonce: 28,
151 State: state,
152 Signature: privK.SignPoseidon(big.NewInt(674238462)),
153 Timestamp: time.Now().UTC(),
154 }
155 if i%2 == 0 { // Optional parameters: rq
156 tx.RqFromIdx = 893
157 tx.RqToIdx = 334
158 tx.RqToEthAddr = eth.BigToAddress(big.NewInt(239457111187))
159 tx.RqToBJJ = privK.Public()
160 tx.RqTokenID = 222
161 tx.RqAmount = big.NewInt(3487762374627846747)
162 tx.RqFee = 11
163 tx.RqNonce = 78
164 }
165 if i%3 == 0 { // Optional parameters: things that get updated "a posteriori"
166 tx.BatchNum = 489
167 tx.AbsoluteFee = 39.12345
168 tx.AbsoluteFeeUpdate = time.Now().UTC()
169 }
170 txs = append(txs, tx)
171 }
172 return txs
173}
174
175func cleanDB() {
176 if _, err := l2DB.db.Exec("DELETE FROM tx_pool"); err != nil {
177 panic(err)
178 }
179 if _, err := l2DB.db.Exec("DELETE FROM account_creation_auth"); err != nil {
180 panic(err)
181 }
182}