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