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	"os"
  8	"strconv"
  9
 10	"github.com/hermeznetwork/hermez-node/common"
 11	"github.com/hermeznetwork/hermez-node/log"
 12	"github.com/hermeznetwork/tracerr"
 13	"github.com/iden3/go-merkletree"
 14	"github.com/iden3/go-merkletree/db"
 15	"github.com/iden3/go-merkletree/db/pebble"
 16)
 17
 18// TODO(Edu): Document here how StateDB is kept consistent
 19
 20var (
 21	// ErrStateDBWithoutMT is used when a method that requires a MerkleTree
 22	// is called in a StateDB that does not have a MerkleTree defined
 23	ErrStateDBWithoutMT = errors.New("Can not call method to use MerkleTree in a StateDB without MerkleTree")
 24
 25	// ErrAccountAlreadyExists is used when CreateAccount is called and the
 26	// Account already exists
 27	ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
 28
 29	// ErrToIdxNotFound is used when trying to get the ToIdx from ToEthAddr
 30	// or ToEthAddr&ToBJJ
 31	ErrToIdxNotFound = errors.New("ToIdx can not be found")
 32	// ErrGetIdxNoCase is used when trying to get the Idx from EthAddr &
 33	// BJJ with not compatible combination
 34	ErrGetIdxNoCase = errors.New("Can not get Idx due unexpected combination of ethereum Address & BabyJubJub PublicKey")
 35
 36	// KeyCurrentBatch is used as key in the db to store the current BatchNum
 37	KeyCurrentBatch = []byte("k:currentbatch")
 38
 39	// PrefixKeyIdx is the key prefix for idx in the db
 40	PrefixKeyIdx = []byte("i:")
 41	// PrefixKeyAccHash is the key prefix for account hash in the db
 42	PrefixKeyAccHash = []byte("h:")
 43	// PrefixKeyMT is the key prefix for merkle tree in the db
 44	PrefixKeyMT = []byte("m:")
 45	// PrefixKeyAddr is the key prefix for address in the db
 46	PrefixKeyAddr = []byte("a:")
 47	// PrefixKeyAddrBJJ is the key prefix for address-babyjubjub in the db
 48	PrefixKeyAddrBJJ = []byte("ab:")
 49)
 50
 51const (
 52	// PathBatchNum defines the subpath of the Batch Checkpoint in the
 53	// subpath of the StateDB
 54	PathBatchNum = "/BatchNum"
 55	// PathCurrent defines the subpath of the current Batch in the subpath
 56	// of the StateDB
 57	PathCurrent = "/current"
 58	// TypeSynchronizer defines a StateDB used by the Synchronizer, that
 59	// generates the ExitTree when processing the txs
 60	TypeSynchronizer = "synchronizer"
 61	// TypeTxSelector defines a StateDB used by the TxSelector, without
 62	// computing ExitTree neither the ZKInputs
 63	TypeTxSelector = "txselector"
 64	// TypeBatchBuilder defines a StateDB used by the BatchBuilder, that
 65	// generates the ExitTree and the ZKInput when processing the txs
 66	TypeBatchBuilder = "batchbuilder"
 67)
 68
 69// TypeStateDB determines the type of StateDB
 70type TypeStateDB string
 71
 72// StateDB represents the StateDB object
 73type StateDB struct {
 74	path         string
 75	currentBatch common.BatchNum
 76	db           *pebble.PebbleStorage
 77	mt           *merkletree.MerkleTree
 78	typ          TypeStateDB
 79	chainID      uint16
 80	// idx holds the current Idx that the BatchBuilder is using
 81	idx common.Idx
 82	zki *common.ZKInputs
 83	// i is the current transaction index in the ZKInputs generation (zki)
 84	i int
 85	// AccumulatedFees contains the accumulated fees for each token (Coord
 86	// Idx) in the processed batch
 87	AccumulatedFees map[common.Idx]*big.Int
 88}
 89
 90// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
 91// storage
 92func NewStateDB(path string, typ TypeStateDB, nLevels int, chainID uint16) (*StateDB, error) {
 93	var sto *pebble.PebbleStorage
 94	var err error
 95	sto, err = pebble.NewPebbleStorage(path+PathCurrent, false)
 96	if err != nil {
 97		return nil, tracerr.Wrap(err)
 98	}
 99
100	var mt *merkletree.MerkleTree = nil
101	if typ == TypeSynchronizer || typ == TypeBatchBuilder {
102		mt, err = merkletree.NewMerkleTree(sto.WithPrefix(PrefixKeyMT), nLevels)
103		if err != nil {
104			return nil, tracerr.Wrap(err)
105		}
106	}
107	if typ == TypeTxSelector && nLevels != 0 {
108		return nil, tracerr.Wrap(fmt.Errorf("invalid StateDB parameters: StateDB type==TypeStateDB can not have nLevels!=0"))
109	}
110
111	sdb := &StateDB{
112		path:    path,
113		db:      sto,
114		mt:      mt,
115		typ:     typ,
116		chainID: chainID,
117	}
118
119	// load currentBatch
120	sdb.currentBatch, err = sdb.GetCurrentBatch()
121	if err != nil {
122		return nil, tracerr.Wrap(err)
123	}
124
125	// make reset (get checkpoint) at currentBatch
126	err = sdb.reset(sdb.currentBatch, false)
127	if err != nil {
128		return nil, tracerr.Wrap(err)
129	}
130
131	return sdb, nil
132}
133
134// DB returns the *pebble.PebbleStorage from the StateDB
135func (s *StateDB) DB() *pebble.PebbleStorage {
136	return s.db
137}
138
139// GetCurrentBatch returns the current BatchNum stored in the StateDB
140func (s *StateDB) GetCurrentBatch() (common.BatchNum, error) {
141	cbBytes, err := s.db.Get(KeyCurrentBatch)
142	if tracerr.Unwrap(err) == db.ErrNotFound {
143		return 0, nil
144	}
145	if err != nil {
146		return 0, tracerr.Wrap(err)
147	}
148	return common.BatchNumFromBytes(cbBytes)
149}
150
151// setCurrentBatch stores the current BatchNum in the StateDB
152func (s *StateDB) setCurrentBatch() error {
153	tx, err := s.db.NewTx()
154	if err != nil {
155		return tracerr.Wrap(err)
156	}
157	err = tx.Put(KeyCurrentBatch, s.currentBatch.Bytes())
158	if err != nil {
159		return tracerr.Wrap(err)
160	}
161	if err := tx.Commit(); err != nil {
162		return tracerr.Wrap(err)
163	}
164	return nil
165}
166
167// MakeCheckpoint does a checkpoint at the given batchNum in the defined path. Internally this advances & stores the current BatchNum, and then stores a Checkpoint of the current state of the StateDB.
168func (s *StateDB) MakeCheckpoint() error {
169	// advance currentBatch
170	s.currentBatch++
171	log.Debugw("Making StateDB checkpoint", "batch", s.currentBatch, "type", s.typ)
172
173	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(s.currentBatch))
174
175	err := s.setCurrentBatch()
176	if err != nil {
177		return tracerr.Wrap(err)
178	}
179
180	// if checkpoint BatchNum already exist in disk, delete it
181	if _, err := os.Stat(checkpointPath); !os.IsNotExist(err) {
182		err := os.RemoveAll(checkpointPath)
183		if err != nil {
184			return tracerr.Wrap(err)
185		}
186	} else if err != nil && !os.IsNotExist(err) {
187		return tracerr.Wrap(err)
188	}
189
190	// execute Checkpoint
191	err = s.db.Pebble().Checkpoint(checkpointPath)
192	if err != nil {
193		return tracerr.Wrap(err)
194	}
195
196	return nil
197}
198
199// DeleteCheckpoint removes if exist the checkpoint of the given batchNum
200func (s *StateDB) DeleteCheckpoint(batchNum common.BatchNum) error {
201	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(batchNum))
202
203	if _, err := os.Stat(checkpointPath); os.IsNotExist(err) {
204		return tracerr.Wrap(fmt.Errorf("Checkpoint with batchNum %d does not exist in DB", batchNum))
205	}
206
207	return os.RemoveAll(checkpointPath)
208}
209
210func pebbleMakeCheckpoint(source, dest string) error {
211	// Remove dest folder (if it exists) before doing the checkpoint
212	if _, err := os.Stat(dest); !os.IsNotExist(err) {
213		err := os.RemoveAll(dest)
214		if err != nil {
215			return tracerr.Wrap(err)
216		}
217	} else if err != nil && !os.IsNotExist(err) {
218		return tracerr.Wrap(err)
219	}
220
221	sto, err := pebble.NewPebbleStorage(source, false)
222	if err != nil {
223		return tracerr.Wrap(err)
224	}
225	defer func() {
226		errClose := sto.Pebble().Close()
227		if errClose != nil {
228			log.Errorw("Pebble.Close", "err", errClose)
229		}
230	}()
231
232	// execute Checkpoint
233	err = sto.Pebble().Checkpoint(dest)
234	if err != nil {
235		return tracerr.Wrap(err)
236	}
237
238	return nil
239}
240
241// Reset resets the StateDB to the checkpoint at the given batchNum. Reset
242// does not delete the checkpoints between old current and the new current,
243// those checkpoints will remain in the storage, and eventually will be
244// deleted when MakeCheckpoint overwrites them.
245func (s *StateDB) Reset(batchNum common.BatchNum) error {
246	return s.reset(batchNum, true)
247}
248
249// reset resets the StateDB to the checkpoint at the given batchNum. Reset
250// does not delete the checkpoints between old current and the new current,
251// those checkpoints will remain in the storage, and eventually will be
252// deleted when MakeCheckpoint overwrites them.  `closeCurrent` will close the
253// currently opened db before doing the reset.
254func (s *StateDB) reset(batchNum common.BatchNum, closeCurrent bool) error {
255	currentPath := s.path + PathCurrent
256
257	if closeCurrent {
258		if err := s.db.Pebble().Close(); err != nil {
259			return tracerr.Wrap(err)
260		}
261	}
262	// remove 'current'
263	err := os.RemoveAll(currentPath)
264	if err != nil {
265		return tracerr.Wrap(err)
266	}
267	if batchNum == 0 {
268		// if batchNum == 0, open the new fresh 'current'
269		sto, err := pebble.NewPebbleStorage(currentPath, false)
270		if err != nil {
271			return tracerr.Wrap(err)
272		}
273		s.db = sto
274		s.idx = 255
275		s.currentBatch = batchNum
276
277		if s.mt != nil {
278			// open the MT for the current s.db
279			mt, err := merkletree.NewMerkleTree(s.db.WithPrefix(PrefixKeyMT), s.mt.MaxLevels())
280			if err != nil {
281				return tracerr.Wrap(err)
282			}
283			s.mt = mt
284		}
285		return nil
286	}
287
288	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(batchNum))
289	// copy 'BatchNumX' to 'current'
290	err = pebbleMakeCheckpoint(checkpointPath, currentPath)
291	if err != nil {
292		return tracerr.Wrap(err)
293	}
294
295	// open the new 'current'
296	sto, err := pebble.NewPebbleStorage(currentPath, false)
297	if err != nil {
298		return tracerr.Wrap(err)
299	}
300	s.db = sto
301
302	// get currentBatch num
303	s.currentBatch, err = s.GetCurrentBatch()
304	if err != nil {
305		return tracerr.Wrap(err)
306	}
307	// idx is obtained from the statedb reset
308	s.idx, err = s.GetIdx()
309	if err != nil {
310		return tracerr.Wrap(err)
311	}
312
313	if s.mt != nil {
314		// open the MT for the current s.db
315		mt, err := merkletree.NewMerkleTree(s.db.WithPrefix(PrefixKeyMT), s.mt.MaxLevels())
316		if err != nil {
317			return tracerr.Wrap(err)
318		}
319		s.mt = mt
320	}
321
322	return nil
323}
324
325// GetAccount returns the account for the given Idx
326func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
327	return getAccountInTreeDB(s.db, idx)
328}
329
330// GetAccounts returns all the accounts in the db.  Use for debugging pruposes
331// only.
332func (s *StateDB) GetAccounts() ([]common.Account, error) {
333	idxDB := s.db.WithPrefix(PrefixKeyIdx)
334	idxs := []common.Idx{}
335	// NOTE: Current implementation of Iterate in the pebble interface is
336	// not efficient, as it iterates over all keys.  Improve it following
337	// this example: https://github.com/cockroachdb/pebble/pull/923/files
338	if err := idxDB.Iterate(func(k []byte, v []byte) (bool, error) {
339		idx, err := common.IdxFromBytes(k)
340		if err != nil {
341			return false, tracerr.Wrap(err)
342		}
343		idxs = append(idxs, idx)
344		return true, nil
345	}); err != nil {
346		return nil, tracerr.Wrap(err)
347	}
348	accs := []common.Account{}
349	for i := range idxs {
350		acc, err := s.GetAccount(idxs[i])
351		if err != nil {
352			return nil, tracerr.Wrap(err)
353		}
354		accs = append(accs, *acc)
355	}
356	return accs, nil
357}
358
359// getAccountInTreeDB is abstracted from StateDB to be used from StateDB and
360// from ExitTree.  GetAccount returns the account for the given Idx
361func getAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
362	idxBytes, err := idx.Bytes()
363	if err != nil {
364		return nil, tracerr.Wrap(err)
365	}
366	vBytes, err := sto.Get(append(PrefixKeyIdx, idxBytes[:]...))
367	if err != nil {
368		return nil, tracerr.Wrap(err)
369	}
370	accBytes, err := sto.Get(append(PrefixKeyAccHash, vBytes...))
371	if err != nil {
372		return nil, tracerr.Wrap(err)
373	}
374	var b [32 * common.NLeafElems]byte
375	copy(b[:], accBytes)
376	account, err := common.AccountFromBytes(b)
377	if err != nil {
378		return nil, tracerr.Wrap(err)
379	}
380	account.Idx = idx
381	return account, nil
382}
383
384// CreateAccount creates a new Account in the StateDB for the given Idx.  If
385// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
386// MerkleTree, returning a CircomProcessorProof.
387func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
388	cpp, err := createAccountInTreeDB(s.db, s.mt, idx, account)
389	if err != nil {
390		return cpp, tracerr.Wrap(err)
391	}
392	// store idx by EthAddr & BJJ
393	err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.PublicKey, account.TokenID)
394	return cpp, tracerr.Wrap(err)
395}
396
397// createAccountInTreeDB is abstracted from StateDB to be used from StateDB and
398// from ExitTree.  Creates a new Account in the StateDB for the given Idx.  If
399// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
400// MerkleTree, returning a CircomProcessorProof.
401func createAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
402	// store at the DB the key: v, and value: leaf.Bytes()
403	v, err := account.HashValue()
404	if err != nil {
405		return nil, tracerr.Wrap(err)
406	}
407	accountBytes, err := account.Bytes()
408	if err != nil {
409		return nil, tracerr.Wrap(err)
410	}
411
412	// store the Leaf value
413	tx, err := sto.NewTx()
414	if err != nil {
415		return nil, tracerr.Wrap(err)
416	}
417
418	idxBytes, err := idx.Bytes()
419	if err != nil {
420		return nil, tracerr.Wrap(err)
421	}
422	_, err = tx.Get(append(PrefixKeyIdx, idxBytes[:]...))
423	if tracerr.Unwrap(err) != db.ErrNotFound {
424		return nil, tracerr.Wrap(ErrAccountAlreadyExists)
425	}
426
427	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
428	if err != nil {
429		return nil, tracerr.Wrap(err)
430	}
431	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
432	if err != nil {
433		return nil, tracerr.Wrap(err)
434	}
435
436	if err := tx.Commit(); err != nil {
437		return nil, tracerr.Wrap(err)
438	}
439
440	if mt != nil {
441		return mt.AddAndGetCircomProof(idx.BigInt(), v)
442	}
443
444	return nil, nil
445}
446
447// UpdateAccount updates the Account in the StateDB for the given Idx.  If
448// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
449// MerkleTree, returning a CircomProcessorProof.
450func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
451	return updateAccountInTreeDB(s.db, s.mt, idx, account)
452}
453
454// updateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
455// from ExitTree.  Updates the Account in the StateDB for the given Idx.  If
456// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
457// MerkleTree, returning a CircomProcessorProof.
458func updateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
459	// store at the DB the key: v, and value: account.Bytes()
460	v, err := account.HashValue()
461	if err != nil {
462		return nil, tracerr.Wrap(err)
463	}
464	accountBytes, err := account.Bytes()
465	if err != nil {
466		return nil, tracerr.Wrap(err)
467	}
468
469	tx, err := sto.NewTx()
470	if err != nil {
471		return nil, tracerr.Wrap(err)
472	}
473	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
474	if err != nil {
475		return nil, tracerr.Wrap(err)
476	}
477	idxBytes, err := idx.Bytes()
478	if err != nil {
479		return nil, tracerr.Wrap(err)
480	}
481	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
482	if err != nil {
483		return nil, tracerr.Wrap(err)
484	}
485
486	if err := tx.Commit(); err != nil {
487		return nil, tracerr.Wrap(err)
488	}
489
490	if mt != nil {
491		proof, err := mt.Update(idx.BigInt(), v)
492		return proof, tracerr.Wrap(err)
493	}
494	return nil, nil
495}
496
497// MTGetProof returns the CircomVerifierProof for a given Idx
498func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
499	if s.mt == nil {
500		return nil, tracerr.Wrap(ErrStateDBWithoutMT)
501	}
502	return s.mt.GenerateCircomVerifierProof(idx.BigInt(), s.mt.Root())
503}
504
505// MTGetRoot returns the current root of the underlying Merkle Tree
506func (s *StateDB) MTGetRoot() *big.Int {
507	return s.mt.Root().BigInt()
508}
509
510// MerkleTree returns the underlying StateDB merkle tree.  It can be nil.
511func (s *StateDB) MerkleTree() *merkletree.MerkleTree {
512	return s.mt
513}
514
515// LocalStateDB represents the local StateDB which allows to make copies from
516// the synchronizer StateDB, and is used by the tx-selector and the
517// batch-builder. LocalStateDB is an in-memory storage.
518type LocalStateDB struct {
519	*StateDB
520	synchronizerStateDB *StateDB
521}
522
523// NewLocalStateDB returns a new LocalStateDB connected to the given
524// synchronizerDB
525func NewLocalStateDB(path string, synchronizerDB *StateDB, typ TypeStateDB, nLevels int) (*LocalStateDB, error) {
526	s, err := NewStateDB(path, typ, nLevels, synchronizerDB.chainID)
527	if err != nil {
528		return nil, tracerr.Wrap(err)
529	}
530	return &LocalStateDB{
531		s,
532		synchronizerDB,
533	}, nil
534}
535
536// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
537// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
538func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
539	if batchNum == 0 {
540		l.idx = 0
541		return nil
542	}
543
544	synchronizerCheckpointPath := l.synchronizerStateDB.path + PathBatchNum + strconv.Itoa(int(batchNum))
545	checkpointPath := l.path + PathBatchNum + strconv.Itoa(int(batchNum))
546	currentPath := l.path + PathCurrent
547
548	if fromSynchronizer {
549		// use checkpoint from SynchronizerStateDB
550		if _, err := os.Stat(synchronizerCheckpointPath); os.IsNotExist(err) {
551			// if synchronizerStateDB does not have checkpoint at batchNum, return err
552			return tracerr.Wrap(fmt.Errorf("Checkpoint \"%v\" not exist in Synchronizer",
553				synchronizerCheckpointPath))
554		}
555
556		if err := l.db.Pebble().Close(); err != nil {
557			return tracerr.Wrap(err)
558		}
559		// remove 'current'
560		err := os.RemoveAll(currentPath)
561		if err != nil {
562			return tracerr.Wrap(err)
563		}
564		// copy synchronizer'BatchNumX' to 'current'
565		err = pebbleMakeCheckpoint(synchronizerCheckpointPath, currentPath)
566		if err != nil {
567			return tracerr.Wrap(err)
568		}
569		// copy synchronizer'BatchNumX' to 'BatchNumX'
570		err = pebbleMakeCheckpoint(synchronizerCheckpointPath, checkpointPath)
571		if err != nil {
572			return tracerr.Wrap(err)
573		}
574
575		// open the new 'current'
576		sto, err := pebble.NewPebbleStorage(currentPath, false)
577		if err != nil {
578			return tracerr.Wrap(err)
579		}
580		l.db = sto
581
582		// get currentBatch num
583		l.currentBatch, err = l.GetCurrentBatch()
584		if err != nil {
585			return tracerr.Wrap(err)
586		}
587		// open the MT for the current s.db
588		if l.mt != nil {
589			mt, err := merkletree.NewMerkleTree(l.db.WithPrefix(PrefixKeyMT), l.mt.MaxLevels())
590			if err != nil {
591				return tracerr.Wrap(err)
592			}
593			l.mt = mt
594		}
595
596		return nil
597	}
598	// use checkpoint from LocalStateDB
599	return l.StateDB.reset(batchNum, true)
600}