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}