hermez-node/db/statedb/statedb.go
db/statedb/statedb.goRaw
1package statedb
2
3import (
4 "errors"
5 "fmt"
6 "math/big"
7
8 "github.com/hermeznetwork/hermez-node/common"
9 "github.com/hermeznetwork/hermez-node/db/kvdb"
10 "github.com/hermeznetwork/hermez-node/log"
11 "github.com/hermeznetwork/tracerr"
12 "github.com/iden3/go-merkletree"
13 "github.com/iden3/go-merkletree/db"
14 "github.com/iden3/go-merkletree/db/pebble"
15)
16
17var (
18 // ErrStateDBWithoutMT is used when a method that requires a MerkleTree
19 // is called in a StateDB that does not have a MerkleTree defined
20 ErrStateDBWithoutMT = errors.New("Can not call method to use MerkleTree in a StateDB without MerkleTree")
21
22 // ErrAccountAlreadyExists is used when CreateAccount is called and the
23 // Account already exists
24 ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
25
26 // ErrIdxNotFound is used when trying to get the Idx from EthAddr or
27 // EthAddr&ToBJJ
28 ErrIdxNotFound = errors.New("Idx can not be found")
29 // ErrGetIdxNoCase is used when trying to get the Idx from EthAddr &
30 // BJJ with not compatible combination
31 ErrGetIdxNoCase = errors.New("Can not get Idx due unexpected combination of ethereum Address & BabyJubJub PublicKey")
32
33 // PrefixKeyIdx is the key prefix for idx in the db
34 PrefixKeyIdx = []byte("i:")
35 // PrefixKeyAccHash is the key prefix for account hash in the db
36 PrefixKeyAccHash = []byte("h:")
37 // PrefixKeyMT is the key prefix for merkle tree in the db
38 PrefixKeyMT = []byte("m:")
39 // PrefixKeyAddr is the key prefix for address in the db
40 PrefixKeyAddr = []byte("a:")
41 // PrefixKeyAddrBJJ is the key prefix for address-babyjubjub in the db
42 PrefixKeyAddrBJJ = []byte("ab:")
43)
44
45const (
46 // TypeSynchronizer defines a StateDB used by the Synchronizer, that
47 // generates the ExitTree when processing the txs
48 TypeSynchronizer = "synchronizer"
49 // TypeTxSelector defines a StateDB used by the TxSelector, without
50 // computing ExitTree neither the ZKInputs
51 TypeTxSelector = "txselector"
52 // TypeBatchBuilder defines a StateDB used by the BatchBuilder, that
53 // generates the ExitTree and the ZKInput when processing the txs
54 TypeBatchBuilder = "batchbuilder"
55)
56
57// TypeStateDB determines the type of StateDB
58type TypeStateDB string
59
60// StateDB represents the StateDB object
61type StateDB struct {
62 path string
63 Typ TypeStateDB
64 db *kvdb.KVDB
65 nLevels int
66 MT *merkletree.MerkleTree
67 keep int
68}
69
70// Last offers a subset of view methods of the StateDB that can be
71// called via the LastRead method of StateDB in a thread-safe manner to obtain
72// a consistent view to the last batch of the StateDB.
73type Last struct {
74 db db.Storage
75}
76
77// GetAccount returns the account for the given Idx
78func (s *Last) GetAccount(idx common.Idx) (*common.Account, error) {
79 return GetAccountInTreeDB(s.db, idx)
80}
81
82// GetCurrentBatch returns the current BatchNum stored in Last.db
83func (s *Last) GetCurrentBatch() (common.BatchNum, error) {
84 cbBytes, err := s.db.Get(kvdb.KeyCurrentBatch)
85 if tracerr.Unwrap(err) == db.ErrNotFound {
86 return 0, nil
87 } else if err != nil {
88 return 0, tracerr.Wrap(err)
89 }
90 return common.BatchNumFromBytes(cbBytes)
91}
92
93// DB returns the underlying storage of Last
94func (s *Last) DB() db.Storage {
95 return s.db
96}
97
98// GetAccounts returns all the accounts in the db. Use for debugging pruposes
99// only.
100func (s *Last) GetAccounts() ([]common.Account, error) {
101 return getAccounts(s.db)
102}
103
104// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
105// storage. Checkpoints older than the value defined by `keep` will be
106// deleted.
107func NewStateDB(pathDB string, keep int, typ TypeStateDB, nLevels int) (*StateDB, error) {
108 var kv *kvdb.KVDB
109 var err error
110
111 kv, err = kvdb.NewKVDB(pathDB, keep)
112 if err != nil {
113 return nil, tracerr.Wrap(err)
114 }
115
116 var mt *merkletree.MerkleTree = nil
117 if typ == TypeSynchronizer || typ == TypeBatchBuilder {
118 mt, err = merkletree.NewMerkleTree(kv.StorageWithPrefix(PrefixKeyMT), nLevels)
119 if err != nil {
120 return nil, tracerr.Wrap(err)
121 }
122 }
123 if typ == TypeTxSelector && nLevels != 0 {
124 return nil, tracerr.Wrap(fmt.Errorf("invalid StateDB parameters: StateDB type==TypeStateDB can not have nLevels!=0"))
125 }
126
127 return &StateDB{
128 path: pathDB,
129 db: kv,
130 nLevels: nLevels,
131 MT: mt,
132 Typ: typ,
133 keep: keep,
134 }, nil
135}
136
137// LastRead is a thread-safe method to query the last checkpoint of the StateDB
138// via the Last type methods
139func (s *StateDB) LastRead(fn func(sdbLast *Last) error) error {
140 return s.db.LastRead(
141 func(db *pebble.Storage) error {
142 return fn(&Last{
143 db: db,
144 })
145 },
146 )
147}
148
149// LastGetAccount is a thread-safe method to query an account in the last
150// checkpoint of the StateDB.
151func (s *StateDB) LastGetAccount(idx common.Idx) (*common.Account, error) {
152 var account *common.Account
153 if err := s.LastRead(func(sdb *Last) error {
154 var err error
155 account, err = sdb.GetAccount(idx)
156 return err
157 }); err != nil {
158 return nil, tracerr.Wrap(err)
159 }
160 return account, nil
161}
162
163// LastGetCurrentBatch is a thread-safe method to get the current BatchNum in
164// the last checkpoint of the StateDB.
165func (s *StateDB) LastGetCurrentBatch() (common.BatchNum, error) {
166 var batchNum common.BatchNum
167 if err := s.LastRead(func(sdb *Last) error {
168 var err error
169 batchNum, err = sdb.GetCurrentBatch()
170 return err
171 }); err != nil {
172 return 0, tracerr.Wrap(err)
173 }
174 return batchNum, nil
175}
176
177// LastMTGetRoot returns the root of the underlying Merkle Tree in the last
178// checkpoint of the StateDB.
179func (s *StateDB) LastMTGetRoot() (*big.Int, error) {
180 var root *big.Int
181 if err := s.LastRead(func(sdb *Last) error {
182 mt, err := merkletree.NewMerkleTree(sdb.DB().WithPrefix(PrefixKeyMT), s.nLevels)
183 if err != nil {
184 return tracerr.Wrap(err)
185 }
186 root = mt.Root().BigInt()
187 return nil
188 }); err != nil {
189 return nil, tracerr.Wrap(err)
190 }
191 return root, nil
192}
193
194// MakeCheckpoint does a checkpoint at the given batchNum in the defined path.
195// Internally this advances & stores the current BatchNum, and then stores a
196// Checkpoint of the current state of the StateDB.
197func (s *StateDB) MakeCheckpoint() error {
198 log.Debugw("Making StateDB checkpoint", "batch", s.CurrentBatch()+1, "type", s.Typ)
199 return s.db.MakeCheckpoint()
200}
201
202// CurrentBatch returns the current in-memory CurrentBatch of the StateDB.db
203func (s *StateDB) CurrentBatch() common.BatchNum {
204 return s.db.CurrentBatch
205}
206
207// CurrentIdx returns the current in-memory CurrentIdx of the StateDB.db
208func (s *StateDB) CurrentIdx() common.Idx {
209 return s.db.CurrentIdx
210}
211
212// getCurrentBatch returns the current BatchNum stored in the StateDB.db
213func (s *StateDB) getCurrentBatch() (common.BatchNum, error) {
214 return s.db.GetCurrentBatch()
215}
216
217// GetCurrentIdx returns the stored Idx from the localStateDB, which is the
218// last Idx used for an Account in the localStateDB.
219func (s *StateDB) GetCurrentIdx() (common.Idx, error) {
220 return s.db.GetCurrentIdx()
221}
222
223// SetCurrentIdx stores Idx in the StateDB
224func (s *StateDB) SetCurrentIdx(idx common.Idx) error {
225 return s.db.SetCurrentIdx(idx)
226}
227
228// Reset resets the StateDB to the checkpoint at the given batchNum. Reset
229// does not delete the checkpoints between old current and the new current,
230// those checkpoints will remain in the storage, and eventually will be
231// deleted when MakeCheckpoint overwrites them.
232func (s *StateDB) Reset(batchNum common.BatchNum) error {
233 log.Debugw("Making StateDB Reset", "batch", batchNum, "type", s.Typ)
234 if err := s.db.Reset(batchNum); err != nil {
235 return tracerr.Wrap(err)
236 }
237 if s.MT != nil {
238 // open the MT for the current s.db
239 mt, err := merkletree.NewMerkleTree(s.db.StorageWithPrefix(PrefixKeyMT), s.MT.MaxLevels())
240 if err != nil {
241 return tracerr.Wrap(err)
242 }
243 s.MT = mt
244 }
245 return nil
246}
247
248// GetAccount returns the account for the given Idx
249func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
250 return GetAccountInTreeDB(s.db.DB(), idx)
251}
252
253func accountsIter(db db.Storage, fn func(a *common.Account) (bool, error)) error {
254 idxDB := db.WithPrefix(PrefixKeyIdx)
255 if err := idxDB.Iterate(func(k []byte, v []byte) (bool, error) {
256 idx, err := common.IdxFromBytes(k)
257 if err != nil {
258 return false, tracerr.Wrap(err)
259 }
260 acc, err := GetAccountInTreeDB(db, idx)
261 if err != nil {
262 return false, tracerr.Wrap(err)
263 }
264 ok, err := fn(acc)
265 if err != nil {
266 return false, tracerr.Wrap(err)
267 }
268 return ok, nil
269 }); err != nil {
270 return tracerr.Wrap(err)
271 }
272 return nil
273}
274
275func getAccounts(db db.Storage) ([]common.Account, error) {
276 accs := []common.Account{}
277 if err := accountsIter(
278 db,
279 func(a *common.Account) (bool, error) {
280 accs = append(accs, *a)
281 return true, nil
282 },
283 ); err != nil {
284 return nil, tracerr.Wrap(err)
285 }
286 return accs, nil
287}
288
289// TestGetAccounts returns all the accounts in the db. Use only in tests.
290// Outside tests getting all the accounts is discouraged because it's an
291// expensive operation, but if you must do it, use `LastRead()` method to get a
292// thread-safe and consistent view of the stateDB.
293func (s *StateDB) TestGetAccounts() ([]common.Account, error) {
294 return getAccounts(s.db.DB())
295}
296
297// GetAccountInTreeDB is abstracted from StateDB to be used from StateDB and
298// from ExitTree. GetAccount returns the account for the given Idx
299func GetAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
300 idxBytes, err := idx.Bytes()
301 if err != nil {
302 return nil, tracerr.Wrap(err)
303 }
304 vBytes, err := sto.Get(append(PrefixKeyIdx, idxBytes[:]...))
305 if err != nil {
306 return nil, tracerr.Wrap(err)
307 }
308 accBytes, err := sto.Get(append(PrefixKeyAccHash, vBytes...))
309 if err != nil {
310 return nil, tracerr.Wrap(err)
311 }
312 var b [32 * common.NLeafElems]byte
313 copy(b[:], accBytes)
314 account, err := common.AccountFromBytes(b)
315 if err != nil {
316 return nil, tracerr.Wrap(err)
317 }
318 account.Idx = idx
319 return account, nil
320}
321
322// CreateAccount creates a new Account in the StateDB for the given Idx. If
323// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
324// MerkleTree, returning a CircomProcessorProof.
325func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
326 cpp, err := CreateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
327 if err != nil {
328 return cpp, tracerr.Wrap(err)
329 }
330 // store idx by EthAddr & BJJ
331 err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.BJJ, account.TokenID)
332 return cpp, tracerr.Wrap(err)
333}
334
335// CreateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
336// from ExitTree. Creates a new Account in the StateDB for the given Idx. If
337// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
338// MerkleTree, returning a CircomProcessorProof.
339func CreateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
340 // store at the DB the key: v, and value: leaf.Bytes()
341 v, err := account.HashValue()
342 if err != nil {
343 return nil, tracerr.Wrap(err)
344 }
345 accountBytes, err := account.Bytes()
346 if err != nil {
347 return nil, tracerr.Wrap(err)
348 }
349
350 // store the Leaf value
351 tx, err := sto.NewTx()
352 if err != nil {
353 return nil, tracerr.Wrap(err)
354 }
355
356 idxBytes, err := idx.Bytes()
357 if err != nil {
358 return nil, tracerr.Wrap(err)
359 }
360 _, err = tx.Get(append(PrefixKeyIdx, idxBytes[:]...))
361 if tracerr.Unwrap(err) != db.ErrNotFound {
362 return nil, tracerr.Wrap(ErrAccountAlreadyExists)
363 }
364
365 err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
366 if err != nil {
367 return nil, tracerr.Wrap(err)
368 }
369 err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
370 if err != nil {
371 return nil, tracerr.Wrap(err)
372 }
373
374 if err := tx.Commit(); err != nil {
375 return nil, tracerr.Wrap(err)
376 }
377
378 if mt != nil {
379 return mt.AddAndGetCircomProof(idx.BigInt(), v)
380 }
381
382 return nil, nil
383}
384
385// UpdateAccount updates the Account in the StateDB for the given Idx. If
386// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
387// MerkleTree, returning a CircomProcessorProof.
388func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
389 return UpdateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
390}
391
392// UpdateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
393// from ExitTree. Updates the Account in the StateDB for the given Idx. If
394// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
395// MerkleTree, returning a CircomProcessorProof.
396func UpdateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
397 // store at the DB the key: v, and value: account.Bytes()
398 v, err := account.HashValue()
399 if err != nil {
400 return nil, tracerr.Wrap(err)
401 }
402 accountBytes, err := account.Bytes()
403 if err != nil {
404 return nil, tracerr.Wrap(err)
405 }
406
407 tx, err := sto.NewTx()
408 if err != nil {
409 return nil, tracerr.Wrap(err)
410 }
411 err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
412 if err != nil {
413 return nil, tracerr.Wrap(err)
414 }
415 idxBytes, err := idx.Bytes()
416 if err != nil {
417 return nil, tracerr.Wrap(err)
418 }
419 err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
420 if err != nil {
421 return nil, tracerr.Wrap(err)
422 }
423
424 if err := tx.Commit(); err != nil {
425 return nil, tracerr.Wrap(err)
426 }
427
428 if mt != nil {
429 proof, err := mt.Update(idx.BigInt(), v)
430 return proof, tracerr.Wrap(err)
431 }
432 return nil, nil
433}
434
435// MTGetProof returns the CircomVerifierProof for a given Idx
436func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
437 if s.MT == nil {
438 return nil, tracerr.Wrap(ErrStateDBWithoutMT)
439 }
440 p, err := s.MT.GenerateSCVerifierProof(idx.BigInt(), s.MT.Root())
441 if err != nil {
442 return nil, tracerr.Wrap(err)
443 }
444 return p, nil
445}
446
447// Close the StateDB
448func (s *StateDB) Close() {
449 s.db.Close()
450}
451
452// LocalStateDB represents the local StateDB which allows to make copies from
453// the synchronizer StateDB, and is used by the tx-selector and the
454// batch-builder. LocalStateDB is an in-memory storage.
455type LocalStateDB struct {
456 *StateDB
457 synchronizerStateDB *StateDB
458}
459
460// NewLocalStateDB returns a new LocalStateDB connected to the given
461// synchronizerDB. Checkpoints older than the value defined by `keep` will be
462// deleted.
463func NewLocalStateDB(path string, keep int, synchronizerDB *StateDB, typ TypeStateDB,
464 nLevels int) (*LocalStateDB, error) {
465 s, err := NewStateDB(path, keep, typ, nLevels)
466 if err != nil {
467 return nil, tracerr.Wrap(err)
468 }
469 return &LocalStateDB{
470 s,
471 synchronizerDB,
472 }, nil
473}
474
475// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
476// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum.
477// If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
478func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
479 if fromSynchronizer {
480 if err := l.db.ResetFromSynchronizer(batchNum, l.synchronizerStateDB.db); err != nil {
481 return tracerr.Wrap(err)
482 }
483 // open the MT for the current s.db
484 if l.MT != nil {
485 mt, err := merkletree.NewMerkleTree(l.db.StorageWithPrefix(PrefixKeyMT),
486 l.MT.MaxLevels())
487 if err != nil {
488 return tracerr.Wrap(err)
489 }
490 l.MT = mt
491 }
492 return nil
493 }
494 // use checkpoint from LocalStateDB
495 return l.StateDB.Reset(batchNum)
496}