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	"io/ioutil"
  7	"math/big"
  8	"os"
  9	"path"
 10	"sort"
 11	"strings"
 12
 13	"github.com/hermeznetwork/hermez-node/common"
 14	"github.com/hermeznetwork/hermez-node/log"
 15	"github.com/hermeznetwork/tracerr"
 16	"github.com/iden3/go-merkletree"
 17	"github.com/iden3/go-merkletree/db"
 18	"github.com/iden3/go-merkletree/db/pebble"
 19)
 20
 21// TODO(Edu): Document here how StateDB is kept consistent
 22
 23var (
 24	// ErrStateDBWithoutMT is used when a method that requires a MerkleTree
 25	// is called in a StateDB that does not have a MerkleTree defined
 26	ErrStateDBWithoutMT = errors.New("Can not call method to use MerkleTree in a StateDB without MerkleTree")
 27
 28	// ErrAccountAlreadyExists is used when CreateAccount is called and the
 29	// Account already exists
 30	ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
 31
 32	// ErrToIdxNotFound is used when trying to get the ToIdx from ToEthAddr
 33	// or ToEthAddr&ToBJJ
 34	ErrToIdxNotFound = errors.New("ToIdx can not be found")
 35	// ErrGetIdxNoCase is used when trying to get the Idx from EthAddr &
 36	// BJJ with not compatible combination
 37	ErrGetIdxNoCase = errors.New("Can not get Idx due unexpected combination of ethereum Address & BabyJubJub PublicKey")
 38
 39	// KeyCurrentBatch is used as key in the db to store the current BatchNum
 40	KeyCurrentBatch = []byte("k:currentbatch")
 41
 42	// PrefixKeyIdx is the key prefix for idx in the db
 43	PrefixKeyIdx = []byte("i:")
 44	// PrefixKeyAccHash is the key prefix for account hash in the db
 45	PrefixKeyAccHash = []byte("h:")
 46	// PrefixKeyMT is the key prefix for merkle tree in the db
 47	PrefixKeyMT = []byte("m:")
 48	// PrefixKeyAddr is the key prefix for address in the db
 49	PrefixKeyAddr = []byte("a:")
 50	// PrefixKeyAddrBJJ is the key prefix for address-babyjubjub in the db
 51	PrefixKeyAddrBJJ = []byte("ab:")
 52	// keyidx is used as key in the db to store the current Idx
 53	keyidx = []byte("k:idx")
 54)
 55
 56const (
 57	// PathBatchNum defines the subpath of the Batch Checkpoint in the
 58	// subpath of the StateDB
 59	PathBatchNum = "BatchNum"
 60	// PathCurrent defines the subpath of the current Batch in the subpath
 61	// of the StateDB
 62	PathCurrent = "current"
 63	// TypeSynchronizer defines a StateDB used by the Synchronizer, that
 64	// generates the ExitTree when processing the txs
 65	TypeSynchronizer = "synchronizer"
 66	// TypeTxSelector defines a StateDB used by the TxSelector, without
 67	// computing ExitTree neither the ZKInputs
 68	TypeTxSelector = "txselector"
 69	// TypeBatchBuilder defines a StateDB used by the BatchBuilder, that
 70	// generates the ExitTree and the ZKInput when processing the txs
 71	TypeBatchBuilder = "batchbuilder"
 72)
 73
 74// TypeStateDB determines the type of StateDB
 75type TypeStateDB string
 76
 77// StateDB represents the StateDB object
 78type StateDB struct {
 79	path string
 80	Typ  TypeStateDB
 81	// CurrentIdx holds the current Idx that the BatchBuilder is using
 82	CurrentIdx   common.Idx
 83	CurrentBatch common.BatchNum
 84	db           *pebble.Storage
 85	MT           *merkletree.MerkleTree
 86	keep         int
 87}
 88
 89// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
 90// storage.  Checkpoints older than the value defined by `keep` will be
 91// deleted.
 92func NewStateDB(pathDB string, keep int, typ TypeStateDB, nLevels int) (*StateDB, error) {
 93	var sto *pebble.Storage
 94	var err error
 95	sto, err = pebble.NewPebbleStorage(path.Join(pathDB, 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: pathDB,
113		db:   sto,
114		MT:   mt,
115		Typ:  typ,
116		keep: keep,
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.Storage from the StateDB
135func (s *StateDB) DB() *pebble.Storage {
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 := path.Join(s.path, fmt.Sprintf("%s%d", PathBatchNum, s.CurrentBatch))
174
175	if err := s.setCurrentBatch(); err != nil {
176		return tracerr.Wrap(err)
177	}
178
179	// if checkpoint BatchNum already exist in disk, delete it
180	if _, err := os.Stat(checkpointPath); !os.IsNotExist(err) {
181		err := os.RemoveAll(checkpointPath)
182		if err != nil {
183			return tracerr.Wrap(err)
184		}
185	} else if err != nil && !os.IsNotExist(err) {
186		return tracerr.Wrap(err)
187	}
188
189	// execute Checkpoint
190	if err := s.db.Pebble().Checkpoint(checkpointPath); err != nil {
191		return tracerr.Wrap(err)
192	}
193	// delete old checkpoints
194	if err := s.deleteOldCheckpoints(); err != nil {
195		return tracerr.Wrap(err)
196	}
197
198	return nil
199}
200
201// DeleteCheckpoint removes if exist the checkpoint of the given batchNum
202func (s *StateDB) DeleteCheckpoint(batchNum common.BatchNum) error {
203	checkpointPath := path.Join(s.path, fmt.Sprintf("%s%d", PathBatchNum, batchNum))
204
205	if _, err := os.Stat(checkpointPath); os.IsNotExist(err) {
206		return tracerr.Wrap(fmt.Errorf("Checkpoint with batchNum %d does not exist in DB", batchNum))
207	}
208
209	return os.RemoveAll(checkpointPath)
210}
211
212// listCheckpoints returns the list of batchNums of the checkpoints, sorted.
213// If there's a gap between the list of checkpoints, an error is returned.
214func (s *StateDB) listCheckpoints() ([]int, error) {
215	files, err := ioutil.ReadDir(s.path)
216	if err != nil {
217		return nil, tracerr.Wrap(err)
218	}
219	checkpoints := []int{}
220	var checkpoint int
221	pattern := fmt.Sprintf("%s%%d", PathBatchNum)
222	for _, file := range files {
223		fileName := file.Name()
224		if file.IsDir() && strings.HasPrefix(fileName, PathBatchNum) {
225			if _, err := fmt.Sscanf(fileName, pattern, &checkpoint); err != nil {
226				return nil, tracerr.Wrap(err)
227			}
228			checkpoints = append(checkpoints, checkpoint)
229		}
230	}
231	sort.Ints(checkpoints)
232	if len(checkpoints) > 0 {
233		first := checkpoints[0]
234		for _, checkpoint := range checkpoints[1:] {
235			first++
236			if checkpoint != first {
237				return nil, tracerr.Wrap(fmt.Errorf("checkpoint gap at %v", checkpoint))
238			}
239		}
240	}
241	return checkpoints, nil
242}
243
244// deleteOldCheckpoints deletes old checkpoints when there are more than
245// `s.keep` checkpoints
246func (s *StateDB) deleteOldCheckpoints() error {
247	list, err := s.listCheckpoints()
248	if err != nil {
249		return tracerr.Wrap(err)
250	}
251	if len(list) > s.keep {
252		for _, checkpoint := range list[:len(list)-s.keep] {
253			if err := s.DeleteCheckpoint(common.BatchNum(checkpoint)); err != nil {
254				return tracerr.Wrap(err)
255			}
256		}
257	}
258	return nil
259}
260
261func pebbleMakeCheckpoint(source, dest string) error {
262	// Remove dest folder (if it exists) before doing the checkpoint
263	if _, err := os.Stat(dest); !os.IsNotExist(err) {
264		err := os.RemoveAll(dest)
265		if err != nil {
266			return tracerr.Wrap(err)
267		}
268	} else if err != nil && !os.IsNotExist(err) {
269		return tracerr.Wrap(err)
270	}
271
272	sto, err := pebble.NewPebbleStorage(source, false)
273	if err != nil {
274		return tracerr.Wrap(err)
275	}
276	defer func() {
277		errClose := sto.Pebble().Close()
278		if errClose != nil {
279			log.Errorw("Pebble.Close", "err", errClose)
280		}
281	}()
282
283	// execute Checkpoint
284	err = sto.Pebble().Checkpoint(dest)
285	if err != nil {
286		return tracerr.Wrap(err)
287	}
288
289	return nil
290}
291
292// Reset resets the StateDB to the checkpoint at the given batchNum. Reset
293// does not delete the checkpoints between old current and the new current,
294// those checkpoints will remain in the storage, and eventually will be
295// deleted when MakeCheckpoint overwrites them.
296func (s *StateDB) Reset(batchNum common.BatchNum) error {
297	return s.reset(batchNum, true)
298}
299
300// reset resets the StateDB to the checkpoint at the given batchNum. Reset
301// does not delete the checkpoints between old current and the new current,
302// those checkpoints will remain in the storage, and eventually will be
303// deleted when MakeCheckpoint overwrites them.  `closeCurrent` will close the
304// currently opened db before doing the reset.
305func (s *StateDB) reset(batchNum common.BatchNum, closeCurrent bool) error {
306	currentPath := path.Join(s.path, PathCurrent)
307
308	if closeCurrent {
309		if err := s.db.Pebble().Close(); err != nil {
310			return tracerr.Wrap(err)
311		}
312	}
313	// remove 'current'
314	err := os.RemoveAll(currentPath)
315	if err != nil {
316		return tracerr.Wrap(err)
317	}
318	// remove all checkpoints > batchNum
319	for i := batchNum + 1; i <= s.CurrentBatch; i++ {
320		if err := s.DeleteCheckpoint(i); err != nil {
321			return tracerr.Wrap(err)
322		}
323	}
324	if batchNum == 0 {
325		// if batchNum == 0, open the new fresh 'current'
326		sto, err := pebble.NewPebbleStorage(currentPath, false)
327		if err != nil {
328			return tracerr.Wrap(err)
329		}
330		s.db = sto
331		s.CurrentIdx = 255
332		s.CurrentBatch = batchNum
333
334		if s.MT != nil {
335			// open the MT for the current s.db
336			mt, err := merkletree.NewMerkleTree(s.db.WithPrefix(PrefixKeyMT), s.MT.MaxLevels())
337			if err != nil {
338				return tracerr.Wrap(err)
339			}
340			s.MT = mt
341		}
342		return nil
343	}
344
345	checkpointPath := path.Join(s.path, fmt.Sprintf("%s%d", PathBatchNum, batchNum))
346	// copy 'BatchNumX' to 'current'
347	err = pebbleMakeCheckpoint(checkpointPath, currentPath)
348	if err != nil {
349		return tracerr.Wrap(err)
350	}
351
352	// open the new 'current'
353	sto, err := pebble.NewPebbleStorage(currentPath, false)
354	if err != nil {
355		return tracerr.Wrap(err)
356	}
357	s.db = sto
358
359	// get currentBatch num
360	s.CurrentBatch, err = s.GetCurrentBatch()
361	if err != nil {
362		return tracerr.Wrap(err)
363	}
364	// idx is obtained from the statedb reset
365	s.CurrentIdx, err = s.GetIdx()
366	if err != nil {
367		return tracerr.Wrap(err)
368	}
369
370	if s.MT != nil {
371		// open the MT for the current s.db
372		mt, err := merkletree.NewMerkleTree(s.db.WithPrefix(PrefixKeyMT), s.MT.MaxLevels())
373		if err != nil {
374			return tracerr.Wrap(err)
375		}
376		s.MT = mt
377	}
378
379	return nil
380}
381
382// GetIdx returns the stored Idx from the localStateDB, which is the last Idx
383// used for an Account in the localStateDB.
384func (s *StateDB) GetIdx() (common.Idx, error) {
385	idxBytes, err := s.DB().Get(keyidx)
386	if tracerr.Unwrap(err) == db.ErrNotFound {
387		return 0, nil
388	}
389	if err != nil {
390		return 0, tracerr.Wrap(err)
391	}
392	return common.IdxFromBytes(idxBytes[:])
393}
394
395// SetIdx stores Idx in the localStateDB
396func (s *StateDB) SetIdx(idx common.Idx) error {
397	s.CurrentIdx = idx
398
399	tx, err := s.DB().NewTx()
400	if err != nil {
401		return tracerr.Wrap(err)
402	}
403	idxBytes, err := idx.Bytes()
404	if err != nil {
405		return tracerr.Wrap(err)
406	}
407	err = tx.Put(keyidx, idxBytes[:])
408	if err != nil {
409		return tracerr.Wrap(err)
410	}
411	if err := tx.Commit(); err != nil {
412		return tracerr.Wrap(err)
413	}
414	return nil
415}
416
417// GetAccount returns the account for the given Idx
418func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
419	return GetAccountInTreeDB(s.db, idx)
420}
421
422// GetAccounts returns all the accounts in the db.  Use for debugging pruposes
423// only.
424func (s *StateDB) GetAccounts() ([]common.Account, error) {
425	idxDB := s.db.WithPrefix(PrefixKeyIdx)
426	idxs := []common.Idx{}
427	// NOTE: Current implementation of Iterate in the pebble interface is
428	// not efficient, as it iterates over all keys.  Improve it following
429	// this example: https://github.com/cockroachdb/pebble/pull/923/files
430	if err := idxDB.Iterate(func(k []byte, v []byte) (bool, error) {
431		idx, err := common.IdxFromBytes(k)
432		if err != nil {
433			return false, tracerr.Wrap(err)
434		}
435		idxs = append(idxs, idx)
436		return true, nil
437	}); err != nil {
438		return nil, tracerr.Wrap(err)
439	}
440	accs := []common.Account{}
441	for i := range idxs {
442		acc, err := s.GetAccount(idxs[i])
443		if err != nil {
444			return nil, tracerr.Wrap(err)
445		}
446		accs = append(accs, *acc)
447	}
448	return accs, nil
449}
450
451// GetAccountInTreeDB is abstracted from StateDB to be used from StateDB and
452// from ExitTree.  GetAccount returns the account for the given Idx
453func GetAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
454	idxBytes, err := idx.Bytes()
455	if err != nil {
456		return nil, tracerr.Wrap(err)
457	}
458	vBytes, err := sto.Get(append(PrefixKeyIdx, idxBytes[:]...))
459	if err != nil {
460		return nil, tracerr.Wrap(err)
461	}
462	accBytes, err := sto.Get(append(PrefixKeyAccHash, vBytes...))
463	if err != nil {
464		return nil, tracerr.Wrap(err)
465	}
466	var b [32 * common.NLeafElems]byte
467	copy(b[:], accBytes)
468	account, err := common.AccountFromBytes(b)
469	if err != nil {
470		return nil, tracerr.Wrap(err)
471	}
472	account.Idx = idx
473	return account, nil
474}
475
476// CreateAccount creates a new Account in the StateDB for the given Idx.  If
477// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
478// MerkleTree, returning a CircomProcessorProof.
479func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
480	cpp, err := CreateAccountInTreeDB(s.db, s.MT, idx, account)
481	if err != nil {
482		return cpp, tracerr.Wrap(err)
483	}
484	// store idx by EthAddr & BJJ
485	err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.PublicKey, account.TokenID)
486	return cpp, tracerr.Wrap(err)
487}
488
489// CreateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
490// from ExitTree.  Creates a new Account in the StateDB for the given Idx.  If
491// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
492// MerkleTree, returning a CircomProcessorProof.
493func CreateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
494	// store at the DB the key: v, and value: leaf.Bytes()
495	v, err := account.HashValue()
496	if err != nil {
497		return nil, tracerr.Wrap(err)
498	}
499	accountBytes, err := account.Bytes()
500	if err != nil {
501		return nil, tracerr.Wrap(err)
502	}
503
504	// store the Leaf value
505	tx, err := sto.NewTx()
506	if err != nil {
507		return nil, tracerr.Wrap(err)
508	}
509
510	idxBytes, err := idx.Bytes()
511	if err != nil {
512		return nil, tracerr.Wrap(err)
513	}
514	_, err = tx.Get(append(PrefixKeyIdx, idxBytes[:]...))
515	if tracerr.Unwrap(err) != db.ErrNotFound {
516		return nil, tracerr.Wrap(ErrAccountAlreadyExists)
517	}
518
519	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
520	if err != nil {
521		return nil, tracerr.Wrap(err)
522	}
523	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
524	if err != nil {
525		return nil, tracerr.Wrap(err)
526	}
527
528	if err := tx.Commit(); err != nil {
529		return nil, tracerr.Wrap(err)
530	}
531
532	if mt != nil {
533		return mt.AddAndGetCircomProof(idx.BigInt(), v)
534	}
535
536	return nil, nil
537}
538
539// UpdateAccount updates the Account in the StateDB for the given Idx.  If
540// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
541// MerkleTree, returning a CircomProcessorProof.
542func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
543	return UpdateAccountInTreeDB(s.db, s.MT, idx, account)
544}
545
546// UpdateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
547// from ExitTree.  Updates the Account in the StateDB for the given Idx.  If
548// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
549// MerkleTree, returning a CircomProcessorProof.
550func UpdateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
551	// store at the DB the key: v, and value: account.Bytes()
552	v, err := account.HashValue()
553	if err != nil {
554		return nil, tracerr.Wrap(err)
555	}
556	accountBytes, err := account.Bytes()
557	if err != nil {
558		return nil, tracerr.Wrap(err)
559	}
560
561	tx, err := sto.NewTx()
562	if err != nil {
563		return nil, tracerr.Wrap(err)
564	}
565	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
566	if err != nil {
567		return nil, tracerr.Wrap(err)
568	}
569	idxBytes, err := idx.Bytes()
570	if err != nil {
571		return nil, tracerr.Wrap(err)
572	}
573	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
574	if err != nil {
575		return nil, tracerr.Wrap(err)
576	}
577
578	if err := tx.Commit(); err != nil {
579		return nil, tracerr.Wrap(err)
580	}
581
582	if mt != nil {
583		proof, err := mt.Update(idx.BigInt(), v)
584		return proof, tracerr.Wrap(err)
585	}
586	return nil, nil
587}
588
589// MTGetProof returns the CircomVerifierProof for a given Idx
590func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
591	if s.MT == nil {
592		return nil, tracerr.Wrap(ErrStateDBWithoutMT)
593	}
594	return s.MT.GenerateCircomVerifierProof(idx.BigInt(), s.MT.Root())
595}
596
597// MTGetRoot returns the current root of the underlying Merkle Tree
598func (s *StateDB) MTGetRoot() *big.Int {
599	return s.MT.Root().BigInt()
600}
601
602// LocalStateDB represents the local StateDB which allows to make copies from
603// the synchronizer StateDB, and is used by the tx-selector and the
604// batch-builder. LocalStateDB is an in-memory storage.
605type LocalStateDB struct {
606	*StateDB
607	synchronizerStateDB *StateDB
608}
609
610// NewLocalStateDB returns a new LocalStateDB connected to the given
611// synchronizerDB.  Checkpoints older than the value defined by `keep` will be
612// deleted.
613func NewLocalStateDB(path string, keep int, synchronizerDB *StateDB, typ TypeStateDB,
614	nLevels int) (*LocalStateDB, error) {
615	s, err := NewStateDB(path, keep, typ, nLevels)
616	if err != nil {
617		return nil, tracerr.Wrap(err)
618	}
619	return &LocalStateDB{
620		s,
621		synchronizerDB,
622	}, nil
623}
624
625// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
626// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
627func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
628	if batchNum == 0 {
629		l.CurrentIdx = 0
630		return nil
631	}
632
633	synchronizerCheckpointPath := path.Join(l.synchronizerStateDB.path,
634		fmt.Sprintf("%s%d", PathBatchNum, batchNum))
635	checkpointPath := path.Join(l.path, fmt.Sprintf("%s%d", PathBatchNum, batchNum))
636	currentPath := path.Join(l.path, PathCurrent)
637
638	if fromSynchronizer {
639		// use checkpoint from SynchronizerStateDB
640		if _, err := os.Stat(synchronizerCheckpointPath); os.IsNotExist(err) {
641			// if synchronizerStateDB does not have checkpoint at batchNum, return err
642			return tracerr.Wrap(fmt.Errorf("Checkpoint \"%v\" not exist in Synchronizer",
643				synchronizerCheckpointPath))
644		}
645
646		if err := l.db.Pebble().Close(); err != nil {
647			return tracerr.Wrap(err)
648		}
649		// remove 'current'
650		err := os.RemoveAll(currentPath)
651		if err != nil {
652			return tracerr.Wrap(err)
653		}
654		// copy synchronizer'BatchNumX' to 'current'
655		err = pebbleMakeCheckpoint(synchronizerCheckpointPath, currentPath)
656		if err != nil {
657			return tracerr.Wrap(err)
658		}
659		// copy synchronizer'BatchNumX' to 'BatchNumX'
660		err = pebbleMakeCheckpoint(synchronizerCheckpointPath, checkpointPath)
661		if err != nil {
662			return tracerr.Wrap(err)
663		}
664
665		// open the new 'current'
666		sto, err := pebble.NewPebbleStorage(currentPath, false)
667		if err != nil {
668			return tracerr.Wrap(err)
669		}
670		l.db = sto
671
672		// get currentBatch num
673		l.CurrentBatch, err = l.GetCurrentBatch()
674		if err != nil {
675			return tracerr.Wrap(err)
676		}
677		// open the MT for the current s.db
678		if l.MT != nil {
679			mt, err := merkletree.NewMerkleTree(l.db.WithPrefix(PrefixKeyMT), l.MT.MaxLevels())
680			if err != nil {
681				return tracerr.Wrap(err)
682			}
683			l.MT = mt
684		}
685
686		return nil
687	}
688	// use checkpoint from LocalStateDB
689	return l.StateDB.reset(batchNum, true)
690}