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	"os"
  7	"os/exec"
  8	"strconv"
  9
 10	"github.com/hermeznetwork/hermez-node/common"
 11	"github.com/iden3/go-merkletree"
 12	"github.com/iden3/go-merkletree/db"
 13	"github.com/iden3/go-merkletree/db/pebble"
 14)
 15
 16// TODO(Edu): Document here how StateDB is kept consistent
 17
 18// ErrStateDBWithoutMT is used when a method that requires a MerkleTree is
 19// called in a StateDB that does not have a MerkleTree defined
 20var 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 Account
 23// already exists
 24var ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
 25
 26// ErrToIdxNotFound is used when trying to get the ToIdx from ToEthAddr or
 27// ToEthAddr&ToBJJ
 28var ErrToIdxNotFound = errors.New("ToIdx can not be found")
 29
 30// KeyCurrentBatch is used as key in the db to store the current BatchNum
 31var KeyCurrentBatch = []byte("currentbatch")
 32
 33// PathStateDB defines the subpath of the StateDB
 34const PathStateDB = "/statedb"
 35
 36// PathBatchNum defines the subpath of the Batch Checkpoint in the subpath of
 37// the StateDB
 38const PathBatchNum = "/BatchNum"
 39
 40// PathCurrent defines the subpath of the current Batch in the subpath of the
 41// StateDB
 42const PathCurrent = "/current"
 43
 44// StateDB represents the StateDB object
 45type StateDB struct {
 46	path         string
 47	currentBatch common.BatchNum
 48	db           *pebble.PebbleStorage
 49	mt           *merkletree.MerkleTree
 50	// idx holds the current Idx that the BatchBuilder is using
 51	idx common.Idx
 52	zki *common.ZKInputs
 53	i   int // i is the current transaction index in the ZKInputs generation (zki)
 54}
 55
 56// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
 57// storage
 58func NewStateDB(path string, withMT bool, nLevels int) (*StateDB, error) {
 59	var sto *pebble.PebbleStorage
 60	var err error
 61	sto, err = pebble.NewPebbleStorage(path+PathStateDB+PathCurrent, false)
 62	if err != nil {
 63		return nil, err
 64	}
 65
 66	var mt *merkletree.MerkleTree = nil
 67	if withMT {
 68		mt, err = merkletree.NewMerkleTree(sto, nLevels)
 69		if err != nil {
 70			return nil, err
 71		}
 72	}
 73
 74	sdb := &StateDB{
 75		path: path + PathStateDB,
 76		db:   sto,
 77		mt:   mt,
 78	}
 79
 80	// load currentBatch
 81	sdb.currentBatch, err = sdb.GetCurrentBatch()
 82	if err != nil {
 83		return nil, err
 84	}
 85
 86	// make reset (get checkpoint) at currentBatch
 87	err = sdb.Reset(sdb.currentBatch)
 88	if err != nil {
 89		return nil, err
 90	}
 91
 92	return sdb, nil
 93}
 94
 95// DB returns the *pebble.PebbleStorage from the StateDB
 96func (s *StateDB) DB() *pebble.PebbleStorage {
 97	return s.db
 98}
 99
100// GetCurrentBatch returns the current BatchNum stored in the StateDB
101func (s *StateDB) GetCurrentBatch() (common.BatchNum, error) {
102	cbBytes, err := s.db.Get(KeyCurrentBatch)
103	if err == db.ErrNotFound {
104		return 0, nil
105	}
106	if err != nil {
107		return 0, err
108	}
109	return common.BatchNumFromBytes(cbBytes)
110}
111
112// setCurrentBatch stores the current BatchNum in the StateDB
113func (s *StateDB) setCurrentBatch() error {
114	tx, err := s.db.NewTx()
115	if err != nil {
116		return err
117	}
118	err = tx.Put(KeyCurrentBatch, s.currentBatch.Bytes())
119	if err != nil {
120		return err
121	}
122	if err := tx.Commit(); err != nil {
123		return err
124	}
125	return nil
126}
127
128// 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.
129func (s *StateDB) MakeCheckpoint() error {
130	// advance currentBatch
131	s.currentBatch++
132
133	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(s.currentBatch))
134
135	err := s.setCurrentBatch()
136	if err != nil {
137		return err
138	}
139
140	// if checkpoint BatchNum already exist in disk, delete it
141	if _, err := os.Stat(checkpointPath); !os.IsNotExist(err) {
142		err := os.RemoveAll(checkpointPath)
143		if err != nil {
144			return err
145		}
146	}
147
148	// execute Checkpoint
149	err = s.db.Pebble().Checkpoint(checkpointPath)
150	if err != nil {
151		return err
152	}
153
154	return nil
155}
156
157// DeleteCheckpoint removes if exist the checkpoint of the given batchNum
158func (s *StateDB) DeleteCheckpoint(batchNum common.BatchNum) error {
159	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(batchNum))
160
161	if _, err := os.Stat(checkpointPath); os.IsNotExist(err) {
162		return fmt.Errorf("Checkpoint with batchNum %d does not exist in DB", batchNum)
163	}
164
165	return os.RemoveAll(checkpointPath)
166}
167
168// Reset resets the StateDB to the checkpoint at the given batchNum. Reset
169// does not delete the checkpoints between old current and the new current,
170// those checkpoints will remain in the storage, and eventually will be
171// deleted when MakeCheckpoint overwrites them.
172func (s *StateDB) Reset(batchNum common.BatchNum) error {
173	checkpointPath := s.path + PathBatchNum + strconv.Itoa(int(batchNum))
174	currentPath := s.path + PathCurrent
175
176	// remove 'current'
177	err := os.RemoveAll(currentPath)
178	if err != nil {
179		return err
180	}
181	if batchNum == 0 {
182		// if batchNum == 0, open the new fresh 'current'
183		sto, err := pebble.NewPebbleStorage(currentPath, false)
184		if err != nil {
185			return err
186		}
187		s.db = sto
188		s.idx = 0
189		s.currentBatch = batchNum
190		return nil
191	}
192
193	// copy 'BatchNumX' to 'current'
194	cmd := exec.Command("cp", "-r", checkpointPath, currentPath) //nolint:gosec
195	err = cmd.Run()
196	if err != nil {
197		return err
198	}
199
200	// open the new 'current'
201	sto, err := pebble.NewPebbleStorage(currentPath, false)
202	if err != nil {
203		return err
204	}
205	s.db = sto
206
207	// get currentBatch num
208	s.currentBatch, err = s.GetCurrentBatch()
209	if err != nil {
210		return err
211	}
212	// idx is obtained from the statedb reset
213	s.idx, err = s.getIdx()
214	if err != nil {
215		return err
216	}
217
218	if s.mt != nil {
219		// open the MT for the current s.db
220		mt, err := merkletree.NewMerkleTree(s.db, s.mt.MaxLevels())
221		if err != nil {
222			return err
223		}
224		s.mt = mt
225	}
226
227	return nil
228}
229
230// GetAccount returns the account for the given Idx
231func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
232	return getAccountInTreeDB(s.db, idx)
233}
234
235// getAccountInTreeDB is abstracted from StateDB to be used from StateDB and
236// from ExitTree.  GetAccount returns the account for the given Idx
237func getAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
238	vBytes, err := sto.Get(idx.Bytes())
239	if err != nil {
240		return nil, err
241	}
242	accBytes, err := sto.Get(vBytes)
243	if err != nil {
244		return nil, err
245	}
246	var b [32 * common.NLeafElems]byte
247	copy(b[:], accBytes)
248	return common.AccountFromBytes(b)
249}
250
251// CreateAccount creates a new Account in the StateDB for the given Idx.  If
252// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
253// MerkleTree, returning a CircomProcessorProof.
254func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
255	cpp, err := createAccountInTreeDB(s.db, s.mt, idx, account)
256	if err != nil {
257		return cpp, err
258	}
259	// store idx by EthAddr & BJJ
260	err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.PublicKey)
261	return cpp, err
262}
263
264// createAccountInTreeDB is abstracted from StateDB to be used from StateDB and
265// from ExitTree.  Creates a new Account in the StateDB for the given Idx.  If
266// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
267// MerkleTree, returning a CircomProcessorProof.
268func createAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
269	// store at the DB the key: v, and value: leaf.Bytes()
270	v, err := account.HashValue()
271	if err != nil {
272		return nil, err
273	}
274	accountBytes, err := account.Bytes()
275	if err != nil {
276		return nil, err
277	}
278
279	// store the Leaf value
280	tx, err := sto.NewTx()
281	if err != nil {
282		return nil, err
283	}
284
285	_, err = tx.Get(idx.Bytes())
286	if err != db.ErrNotFound {
287		return nil, ErrAccountAlreadyExists
288	}
289
290	err = tx.Put(v.Bytes(), accountBytes[:])
291	if err != nil {
292		return nil, err
293	}
294	err = tx.Put(idx.Bytes(), v.Bytes())
295	if err != nil {
296		return nil, err
297	}
298
299	if err := tx.Commit(); err != nil {
300		return nil, err
301	}
302
303	if mt != nil {
304		return mt.AddAndGetCircomProof(idx.BigInt(), v)
305	}
306
307	return nil, nil
308}
309
310// UpdateAccount updates the Account in the StateDB for the given Idx.  If
311// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
312// MerkleTree, returning a CircomProcessorProof.
313func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
314	return updateAccountInTreeDB(s.db, s.mt, idx, account)
315}
316
317// updateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
318// from ExitTree.  Updates the Account in the StateDB for the given Idx.  If
319// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
320// MerkleTree, returning a CircomProcessorProof.
321func updateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
322	// store at the DB the key: v, and value: account.Bytes()
323	v, err := account.HashValue()
324	if err != nil {
325		return nil, err
326	}
327	accountBytes, err := account.Bytes()
328	if err != nil {
329		return nil, err
330	}
331
332	tx, err := sto.NewTx()
333	if err != nil {
334		return nil, err
335	}
336	err = tx.Put(v.Bytes(), accountBytes[:])
337	if err != nil {
338		return nil, err
339	}
340	err = tx.Put(idx.Bytes(), v.Bytes())
341	if err != nil {
342		return nil, err
343	}
344
345	if err := tx.Commit(); err != nil {
346		return nil, err
347	}
348
349	if mt != nil {
350		return mt.Update(idx.BigInt(), v)
351	}
352	return nil, nil
353}
354
355// MTGetProof returns the CircomVerifierProof for a given Idx
356func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
357	if s.mt == nil {
358		return nil, ErrStateDBWithoutMT
359	}
360	return s.mt.GenerateCircomVerifierProof(idx.BigInt(), s.mt.Root())
361}
362
363// LocalStateDB represents the local StateDB which allows to make copies from
364// the synchronizer StateDB, and is used by the tx-selector and the
365// batch-builder. LocalStateDB is an in-memory storage.
366type LocalStateDB struct {
367	*StateDB
368	synchronizerStateDB *StateDB
369}
370
371// NewLocalStateDB returns a new LocalStateDB connected to the given
372// synchronizerDB
373func NewLocalStateDB(path string, synchronizerDB *StateDB, withMT bool, nLevels int) (*LocalStateDB, error) {
374	s, err := NewStateDB(path, withMT, nLevels)
375	if err != nil {
376		return nil, err
377	}
378	return &LocalStateDB{
379		s,
380		synchronizerDB,
381	}, nil
382}
383
384// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
385// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
386func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
387	if batchNum == 0 {
388		l.idx = 0
389		return nil
390	}
391
392	synchronizerCheckpointPath := l.synchronizerStateDB.path + PathBatchNum + strconv.Itoa(int(batchNum))
393	checkpointPath := l.path + PathBatchNum + strconv.Itoa(int(batchNum))
394	currentPath := l.path + PathCurrent
395
396	if fromSynchronizer {
397		// use checkpoint from SynchronizerStateDB
398		if _, err := os.Stat(synchronizerCheckpointPath); os.IsNotExist(err) {
399			// if synchronizerStateDB does not have checkpoint at batchNum, return err
400			return fmt.Errorf("Checkpoint not exist in Synchronizer")
401		}
402
403		// remove 'current'
404		err := os.RemoveAll(currentPath)
405		if err != nil {
406			return err
407		}
408		// copy synchronizer'BatchNumX' to 'current'
409		cmd := exec.Command("cp", "-r", synchronizerCheckpointPath, currentPath) //nolint:gosec
410		err = cmd.Run()
411		if err != nil {
412			return err
413		}
414		// copy synchronizer-'BatchNumX' to 'BatchNumX'
415		cmd = exec.Command("cp", "-r", synchronizerCheckpointPath, checkpointPath) //nolint:gosec
416		err = cmd.Run()
417		if err != nil {
418			return err
419		}
420
421		// open the new 'current'
422		sto, err := pebble.NewPebbleStorage(currentPath, false)
423		if err != nil {
424			return err
425		}
426		l.db = sto
427
428		// get currentBatch num
429		l.currentBatch, err = l.GetCurrentBatch()
430		if err != nil {
431			return err
432		}
433		// open the MT for the current s.db
434		mt, err := merkletree.NewMerkleTree(l.db, l.mt.MaxLevels())
435		if err != nil {
436			return err
437		}
438		l.mt = mt
439
440		return nil
441	}
442	// use checkpoint from LocalStateDB
443	return l.StateDB.Reset(batchNum)
444}