hermez-node/db/statedb/statedb_test.go
db/statedb/statedb_test.goRaw
1package statedb
2
3import (
4 "encoding/hex"
5 "fmt"
6 "io/ioutil"
7 "math/big"
8 "testing"
9
10 ethCrypto "github.com/ethereum/go-ethereum/crypto"
11 "github.com/hermeznetwork/hermez-node/common"
12 "github.com/iden3/go-iden3-crypto/babyjub"
13 "github.com/iden3/go-merkletree/db"
14 "github.com/stretchr/testify/assert"
15 "github.com/stretchr/testify/require"
16)
17
18func newAccount(t *testing.T, i int) *common.Account {
19 var sk babyjub.PrivateKey
20 _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001"))
21 require.Nil(t, err)
22 pk := sk.Public()
23
24 key, err := ethCrypto.GenerateKey()
25 require.Nil(t, err)
26 address := ethCrypto.PubkeyToAddress(key.PublicKey)
27
28 return &common.Account{
29 TokenID: common.TokenID(i),
30 Nonce: common.Nonce(i),
31 Balance: big.NewInt(1000),
32 PublicKey: pk,
33 EthAddr: address,
34 }
35}
36
37func TestNewStateDBIntermediateState(t *testing.T) {
38 dir, err := ioutil.TempDir("", "tmpdb")
39 require.Nil(t, err)
40
41 sdb, err := NewStateDB(dir, TypeTxSelector, 0)
42 assert.Nil(t, err)
43
44 // test values
45 k0 := []byte("testkey0")
46 k1 := []byte("testkey1")
47 v0 := []byte("testvalue0")
48 v1 := []byte("testvalue1")
49
50 // store some data
51 tx, err := sdb.db.NewTx()
52 assert.Nil(t, err)
53 err = tx.Put(k0, v0)
54 assert.Nil(t, err)
55 err = tx.Commit()
56 assert.Nil(t, err)
57 v, err := sdb.db.Get(k0)
58 assert.Nil(t, err)
59 assert.Equal(t, v0, v)
60
61 // call NewStateDB which should get the db at the last checkpoint state
62 // executing a Reset (discarding the last 'testkey0'&'testvalue0' data)
63 sdb, err = NewStateDB(dir, TypeTxSelector, 0)
64 assert.Nil(t, err)
65 v, err = sdb.db.Get(k0)
66 assert.NotNil(t, err)
67 assert.Equal(t, db.ErrNotFound, err)
68 assert.Nil(t, v)
69
70 // store the same data from the beginning that has ben lost since last NewStateDB
71 tx, err = sdb.db.NewTx()
72 assert.Nil(t, err)
73 err = tx.Put(k0, v0)
74 assert.Nil(t, err)
75 err = tx.Commit()
76 assert.Nil(t, err)
77 v, err = sdb.db.Get(k0)
78 assert.Nil(t, err)
79 assert.Equal(t, v0, v)
80
81 // make checkpoints with the current state
82 bn, err := sdb.GetCurrentBatch()
83 assert.Nil(t, err)
84 assert.Equal(t, common.BatchNum(0), bn)
85 err = sdb.MakeCheckpoint()
86 assert.Nil(t, err)
87 bn, err = sdb.GetCurrentBatch()
88 assert.Nil(t, err)
89 assert.Equal(t, common.BatchNum(1), bn)
90
91 // write more data
92 tx, err = sdb.db.NewTx()
93 assert.Nil(t, err)
94 err = tx.Put(k1, v1)
95 assert.Nil(t, err)
96 err = tx.Commit()
97 assert.Nil(t, err)
98
99 v, err = sdb.db.Get(k1)
100 assert.Nil(t, err)
101 assert.Equal(t, v1, v)
102
103 // call NewStateDB which should get the db at the last checkpoint state
104 // executing a Reset (discarding the last 'testkey1'&'testvalue1' data)
105 sdb, err = NewStateDB(dir, TypeTxSelector, 0)
106 assert.Nil(t, err)
107
108 v, err = sdb.db.Get(k0)
109 assert.Nil(t, err)
110 assert.Equal(t, v0, v)
111
112 v, err = sdb.db.Get(k1)
113 assert.NotNil(t, err)
114 assert.Equal(t, db.ErrNotFound, err)
115 assert.Nil(t, v)
116}
117
118func TestStateDBWithoutMT(t *testing.T) {
119 dir, err := ioutil.TempDir("", "tmpdb")
120 require.Nil(t, err)
121
122 sdb, err := NewStateDB(dir, TypeTxSelector, 0)
123 assert.Nil(t, err)
124
125 // create test accounts
126 var accounts []*common.Account
127 for i := 0; i < 100; i++ {
128 accounts = append(accounts, newAccount(t, i))
129 }
130
131 // get non-existing account, expecting an error
132 unexistingAccount := common.Idx(1)
133 _, err = sdb.GetAccount(&unexistingAccount)
134 assert.NotNil(t, err)
135 assert.Equal(t, db.ErrNotFound, err)
136
137 // add test accounts
138 for i := 0; i < len(accounts); i++ {
139 _, err = sdb.CreateAccount(common.Idx(i), accounts[i])
140 assert.Nil(t, err)
141 }
142
143 for i := 0; i < len(accounts); i++ {
144 existingAccount := common.Idx(i)
145 accGetted, err := sdb.GetAccount(&existingAccount)
146 assert.Nil(t, err)
147 assert.Equal(t, accounts[i], accGetted)
148 }
149
150 // try already existing idx and get error
151 existingAccount := common.Idx(1)
152 _, err = sdb.GetAccount(&existingAccount) // check that exist
153 assert.Nil(t, err)
154 _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice
155 assert.NotNil(t, err)
156 assert.Equal(t, ErrAccountAlreadyExists, err)
157
158 // update accounts
159 for i := 0; i < len(accounts); i++ {
160 accounts[i].Nonce = accounts[i].Nonce + 1
161 existingAccount = common.Idx(i)
162 _, err = sdb.UpdateAccount(&existingAccount, accounts[i])
163 assert.Nil(t, err)
164 }
165
166 _, err = sdb.MTGetProof(common.Idx(1))
167 assert.NotNil(t, err)
168 assert.Equal(t, ErrStateDBWithoutMT, err)
169}
170
171func TestStateDBWithMT(t *testing.T) {
172 dir, err := ioutil.TempDir("", "tmpdb")
173 require.Nil(t, err)
174
175 sdb, err := NewStateDB(dir, TypeSynchronizer, 32)
176 assert.Nil(t, err)
177
178 // create test accounts
179 var accounts []*common.Account
180 for i := 0; i < 20; i++ {
181 accounts = append(accounts, newAccount(t, i))
182 }
183
184 // get non-existing account, expecting an error
185 accountIdx := common.Idx(1)
186 _, err = sdb.GetAccount(&accountIdx)
187 assert.NotNil(t, err)
188 assert.Equal(t, db.ErrNotFound, err)
189
190 // add test accounts
191 for i := 0; i < len(accounts); i++ {
192 _, err = sdb.CreateAccount(common.Idx(i), accounts[i])
193 assert.Nil(t, err)
194 }
195
196 for i := 0; i < len(accounts); i++ {
197 accountIdx = common.Idx(i)
198 accGetted, err := sdb.GetAccount(&accountIdx)
199 assert.Nil(t, err)
200 assert.Equal(t, accounts[i], accGetted)
201 }
202
203 // try already existing idx and get error
204 accountIdx = 1
205 _, err = sdb.GetAccount(&accountIdx) // check that exist
206 assert.Nil(t, err)
207 _, err = sdb.CreateAccount(common.Idx(1), accounts[1]) // check that can not be created twice
208 assert.NotNil(t, err)
209 assert.Equal(t, ErrAccountAlreadyExists, err)
210
211 _, err = sdb.MTGetProof(common.Idx(1))
212 assert.Nil(t, err)
213
214 // update accounts
215 for i := 0; i < len(accounts); i++ {
216 accounts[i].Nonce = accounts[i].Nonce + 1
217 accountIdx = common.Idx(i)
218 _, err = sdb.UpdateAccount(&accountIdx, accounts[i])
219 assert.Nil(t, err)
220 }
221 accountIdx = 1
222 a, err := sdb.GetAccount(&accountIdx) // check that account value has been updated
223 assert.Nil(t, err)
224 assert.Equal(t, accounts[1].Nonce, a.Nonce)
225}
226
227func TestCheckpoints(t *testing.T) {
228 dir, err := ioutil.TempDir("", "sdb")
229 require.Nil(t, err)
230
231 sdb, err := NewStateDB(dir, TypeSynchronizer, 32)
232 assert.Nil(t, err)
233
234 // create test accounts
235 var accounts []*common.Account
236 for i := 0; i < 10; i++ {
237 accounts = append(accounts, newAccount(t, i))
238 }
239
240 // add test accounts
241 for i := 0; i < len(accounts); i++ {
242 _, err = sdb.CreateAccount(common.Idx(i), accounts[i])
243 assert.Nil(t, err)
244 }
245
246 // do checkpoints and check that currentBatch is correct
247 err = sdb.MakeCheckpoint()
248 assert.Nil(t, err)
249 cb, err := sdb.GetCurrentBatch()
250 assert.Nil(t, err)
251 assert.Equal(t, common.BatchNum(1), cb)
252
253 for i := 1; i < 10; i++ {
254 err = sdb.MakeCheckpoint()
255 assert.Nil(t, err)
256
257 cb, err = sdb.GetCurrentBatch()
258 assert.Nil(t, err)
259 assert.Equal(t, common.BatchNum(i+1), cb)
260 }
261
262 // printCheckpoints(t, sdb.path)
263
264 // reset checkpoint
265 err = sdb.Reset(3)
266 assert.Nil(t, err)
267
268 // check that reset can be repeated (as there exist the 'current' and
269 // 'BatchNum3', from where the 'current' is a copy)
270 err = sdb.Reset(3)
271 require.Nil(t, err)
272
273 // check that currentBatch is as expected after Reset
274 cb, err = sdb.GetCurrentBatch()
275 assert.Nil(t, err)
276 assert.Equal(t, common.BatchNum(3), cb)
277
278 // advance one checkpoint and check that currentBatch is fine
279 err = sdb.MakeCheckpoint()
280 assert.Nil(t, err)
281 cb, err = sdb.GetCurrentBatch()
282 assert.Nil(t, err)
283 assert.Equal(t, common.BatchNum(4), cb)
284
285 err = sdb.DeleteCheckpoint(common.BatchNum(9))
286 assert.Nil(t, err)
287 err = sdb.DeleteCheckpoint(common.BatchNum(10))
288 assert.Nil(t, err)
289 err = sdb.DeleteCheckpoint(common.BatchNum(9)) // does not exist, should return err
290 assert.NotNil(t, err)
291 err = sdb.DeleteCheckpoint(common.BatchNum(11)) // does not exist, should return err
292 assert.NotNil(t, err)
293
294 // Create a LocalStateDB from the initial StateDB
295 dirLocal, err := ioutil.TempDir("", "ldb")
296 require.Nil(t, err)
297 ldb, err := NewLocalStateDB(dirLocal, sdb, TypeBatchBuilder, 32)
298 assert.Nil(t, err)
299
300 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
301 err = ldb.Reset(4, true)
302 assert.Nil(t, err)
303 // check that currentBatch is 4 after the Reset
304 cb, err = ldb.GetCurrentBatch()
305 assert.Nil(t, err)
306 assert.Equal(t, common.BatchNum(4), cb)
307 // advance one checkpoint in ldb
308 err = ldb.MakeCheckpoint()
309 assert.Nil(t, err)
310 cb, err = ldb.GetCurrentBatch()
311 assert.Nil(t, err)
312 assert.Equal(t, common.BatchNum(5), cb)
313
314 // Create a 2nd LocalStateDB from the initial StateDB
315 dirLocal2, err := ioutil.TempDir("", "ldb2")
316 require.Nil(t, err)
317 ldb2, err := NewLocalStateDB(dirLocal2, sdb, TypeBatchBuilder, 32)
318 assert.Nil(t, err)
319
320 // get checkpoint 4 from sdb (StateDB) to ldb (LocalStateDB)
321 err = ldb2.Reset(4, true)
322 assert.Nil(t, err)
323 // check that currentBatch is 4 after the Reset
324 cb, err = ldb2.GetCurrentBatch()
325 assert.Nil(t, err)
326 assert.Equal(t, common.BatchNum(4), cb)
327 // advance one checkpoint in ldb2
328 err = ldb2.MakeCheckpoint()
329 assert.Nil(t, err)
330 cb, err = ldb2.GetCurrentBatch()
331 assert.Nil(t, err)
332 assert.Equal(t, common.BatchNum(5), cb)
333
334 debug := false
335 if debug {
336 printCheckpoints(t, sdb.path)
337 printCheckpoints(t, ldb.path)
338 printCheckpoints(t, ldb2.path)
339 }
340}
341
342func printCheckpoints(t *testing.T, path string) {
343 files, err := ioutil.ReadDir(path)
344 assert.Nil(t, err)
345
346 fmt.Println(path)
347 for _, f := range files {
348 fmt.Println(" " + f.Name())
349 }
350}