arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
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// CheckpointExists returns true if the checkpoint exists
502func (l *LocalStateDB) CheckpointExists(batchNum common.BatchNum) (bool, error) {
503	return l.db.CheckpointExists(batchNum)
504}
505
506// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
507// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum.
508// If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
509func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
510	if fromSynchronizer {
511		log.Debugw("Making StateDB ResetFromSynchronizer", "batch", batchNum, "type", l.cfg.Type)
512		if err := l.db.ResetFromSynchronizer(batchNum, l.synchronizerStateDB.db); err != nil {
513			return tracerr.Wrap(err)
514		}
515		// open the MT for the current s.db
516		if l.MT != nil {
517			mt, err := merkletree.NewMerkleTree(l.db.StorageWithPrefix(PrefixKeyMT),
518				l.MT.MaxLevels())
519			if err != nil {
520				return tracerr.Wrap(err)
521			}
522			l.MT = mt
523		}
524		return nil
525	}
526	// use checkpoint from LocalStateDB
527	return l.StateDB.Reset(batchNum)
528}