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