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
  8	"github.com/hermeznetwork/hermez-node/common"
  9	"github.com/hermeznetwork/hermez-node/db/kvdb"
 10	"github.com/hermeznetwork/hermez-node/log"
 11	"github.com/hermeznetwork/tracerr"
 12	"github.com/iden3/go-merkletree"
 13	"github.com/iden3/go-merkletree/db"
 14	"github.com/iden3/go-merkletree/db/pebble"
 15)
 16
 17var (
 18	// ErrStateDBWithoutMT is used when a method that requires a MerkleTree
 19	// is called in a StateDB that does not have a MerkleTree defined
 20	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
 23	// Account already exists
 24	ErrAccountAlreadyExists = errors.New("Can not CreateAccount because Account already exists")
 25
 26	// ErrIdxNotFound is used when trying to get the Idx from EthAddr or
 27	// EthAddr&ToBJJ
 28	ErrIdxNotFound = errors.New("Idx can not be found")
 29	// ErrGetIdxNoCase is used when trying to get the Idx from EthAddr &
 30	// BJJ with not compatible combination
 31	ErrGetIdxNoCase = errors.New("Can not get Idx due unexpected combination of ethereum Address & BabyJubJub PublicKey")
 32
 33	// PrefixKeyIdx is the key prefix for idx in the db
 34	PrefixKeyIdx = []byte("i:")
 35	// PrefixKeyAccHash is the key prefix for account hash in the db
 36	PrefixKeyAccHash = []byte("h:")
 37	// PrefixKeyMT is the key prefix for merkle tree in the db
 38	PrefixKeyMT = []byte("m:")
 39	// PrefixKeyAddr is the key prefix for address in the db
 40	PrefixKeyAddr = []byte("a:")
 41	// PrefixKeyAddrBJJ is the key prefix for address-babyjubjub in the db
 42	PrefixKeyAddrBJJ = []byte("ab:")
 43)
 44
 45const (
 46	// TypeSynchronizer defines a StateDB used by the Synchronizer, that
 47	// generates the ExitTree when processing the txs
 48	TypeSynchronizer = "synchronizer"
 49	// TypeTxSelector defines a StateDB used by the TxSelector, without
 50	// computing ExitTree neither the ZKInputs
 51	TypeTxSelector = "txselector"
 52	// TypeBatchBuilder defines a StateDB used by the BatchBuilder, that
 53	// generates the ExitTree and the ZKInput when processing the txs
 54	TypeBatchBuilder = "batchbuilder"
 55)
 56
 57// TypeStateDB determines the type of StateDB
 58type TypeStateDB string
 59
 60// StateDB represents the StateDB object
 61type StateDB struct {
 62	path    string
 63	Typ     TypeStateDB
 64	db      *kvdb.KVDB
 65	nLevels int
 66	MT      *merkletree.MerkleTree
 67	keep    int
 68}
 69
 70// Last offers a subset of view methods of the StateDB that can be
 71// called via the LastRead method of StateDB in a thread-safe manner to obtain
 72// a consistent view to the last batch of the StateDB.
 73type Last struct {
 74	db db.Storage
 75}
 76
 77// GetAccount returns the account for the given Idx
 78func (s *Last) GetAccount(idx common.Idx) (*common.Account, error) {
 79	return GetAccountInTreeDB(s.db, idx)
 80}
 81
 82// GetCurrentBatch returns the current BatchNum stored in Last.db
 83func (s *Last) GetCurrentBatch() (common.BatchNum, error) {
 84	cbBytes, err := s.db.Get(kvdb.KeyCurrentBatch)
 85	if tracerr.Unwrap(err) == db.ErrNotFound {
 86		return 0, nil
 87	} else if err != nil {
 88		return 0, tracerr.Wrap(err)
 89	}
 90	return common.BatchNumFromBytes(cbBytes)
 91}
 92
 93// DB returns the underlying storage of Last
 94func (s *Last) DB() db.Storage {
 95	return s.db
 96}
 97
 98// GetAccounts returns all the accounts in the db.  Use for debugging pruposes
 99// only.
100func (s *Last) GetAccounts() ([]common.Account, error) {
101	return getAccounts(s.db)
102}
103
104// NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk
105// storage.  Checkpoints older than the value defined by `keep` will be
106// deleted.
107func NewStateDB(pathDB string, keep int, typ TypeStateDB, nLevels int) (*StateDB, error) {
108	var kv *kvdb.KVDB
109	var err error
110
111	kv, err = kvdb.NewKVDB(pathDB, keep)
112	if err != nil {
113		return nil, tracerr.Wrap(err)
114	}
115
116	var mt *merkletree.MerkleTree = nil
117	if typ == TypeSynchronizer || typ == TypeBatchBuilder {
118		mt, err = merkletree.NewMerkleTree(kv.StorageWithPrefix(PrefixKeyMT), nLevels)
119		if err != nil {
120			return nil, tracerr.Wrap(err)
121		}
122	}
123	if typ == TypeTxSelector && nLevels != 0 {
124		return nil, tracerr.Wrap(fmt.Errorf("invalid StateDB parameters: StateDB type==TypeStateDB can not have nLevels!=0"))
125	}
126
127	return &StateDB{
128		path:    pathDB,
129		db:      kv,
130		nLevels: nLevels,
131		MT:      mt,
132		Typ:     typ,
133		keep:    keep,
134	}, nil
135}
136
137// LastRead is a thread-safe method to query the last checkpoint of the StateDB
138// via the Last type methods
139func (s *StateDB) LastRead(fn func(sdbLast *Last) error) error {
140	return s.db.LastRead(
141		func(db *pebble.Storage) error {
142			return fn(&Last{
143				db: db,
144			})
145		},
146	)
147}
148
149// LastGetAccount is a thread-safe method to query an account in the last
150// checkpoint of the StateDB.
151func (s *StateDB) LastGetAccount(idx common.Idx) (*common.Account, error) {
152	var account *common.Account
153	if err := s.LastRead(func(sdb *Last) error {
154		var err error
155		account, err = sdb.GetAccount(idx)
156		return err
157	}); err != nil {
158		return nil, tracerr.Wrap(err)
159	}
160	return account, nil
161}
162
163// LastGetCurrentBatch is a thread-safe method to get the current BatchNum in
164// the last checkpoint of the StateDB.
165func (s *StateDB) LastGetCurrentBatch() (common.BatchNum, error) {
166	var batchNum common.BatchNum
167	if err := s.LastRead(func(sdb *Last) error {
168		var err error
169		batchNum, err = sdb.GetCurrentBatch()
170		return err
171	}); err != nil {
172		return 0, tracerr.Wrap(err)
173	}
174	return batchNum, nil
175}
176
177// LastMTGetRoot returns the root of the underlying Merkle Tree in the last
178// checkpoint of the StateDB.
179func (s *StateDB) LastMTGetRoot() (*big.Int, error) {
180	var root *big.Int
181	if err := s.LastRead(func(sdb *Last) error {
182		mt, err := merkletree.NewMerkleTree(sdb.DB().WithPrefix(PrefixKeyMT), s.nLevels)
183		if err != nil {
184			return tracerr.Wrap(err)
185		}
186		root = mt.Root().BigInt()
187		return nil
188	}); err != nil {
189		return nil, tracerr.Wrap(err)
190	}
191	return root, nil
192}
193
194// MakeCheckpoint does a checkpoint at the given batchNum in the defined path.
195// Internally this advances & stores the current BatchNum, and then stores a
196// Checkpoint of the current state of the StateDB.
197func (s *StateDB) MakeCheckpoint() error {
198	log.Debugw("Making StateDB checkpoint", "batch", s.CurrentBatch()+1, "type", s.Typ)
199	return s.db.MakeCheckpoint()
200}
201
202// CurrentBatch returns the current in-memory CurrentBatch of the StateDB.db
203func (s *StateDB) CurrentBatch() common.BatchNum {
204	return s.db.CurrentBatch
205}
206
207// CurrentIdx returns the current in-memory CurrentIdx of the StateDB.db
208func (s *StateDB) CurrentIdx() common.Idx {
209	return s.db.CurrentIdx
210}
211
212// getCurrentBatch returns the current BatchNum stored in the StateDB.db
213func (s *StateDB) getCurrentBatch() (common.BatchNum, error) {
214	return s.db.GetCurrentBatch()
215}
216
217// GetCurrentIdx returns the stored Idx from the localStateDB, which is the
218// last Idx used for an Account in the localStateDB.
219func (s *StateDB) GetCurrentIdx() (common.Idx, error) {
220	return s.db.GetCurrentIdx()
221}
222
223// SetCurrentIdx stores Idx in the StateDB
224func (s *StateDB) SetCurrentIdx(idx common.Idx) error {
225	return s.db.SetCurrentIdx(idx)
226}
227
228// Reset resets the StateDB to the checkpoint at the given batchNum. Reset
229// does not delete the checkpoints between old current and the new current,
230// those checkpoints will remain in the storage, and eventually will be
231// deleted when MakeCheckpoint overwrites them.
232func (s *StateDB) Reset(batchNum common.BatchNum) error {
233	log.Debugw("Making StateDB Reset", "batch", batchNum, "type", s.Typ)
234	if err := s.db.Reset(batchNum); err != nil {
235		return tracerr.Wrap(err)
236	}
237	if s.MT != nil {
238		// open the MT for the current s.db
239		mt, err := merkletree.NewMerkleTree(s.db.StorageWithPrefix(PrefixKeyMT), s.MT.MaxLevels())
240		if err != nil {
241			return tracerr.Wrap(err)
242		}
243		s.MT = mt
244	}
245	return nil
246}
247
248// GetAccount returns the account for the given Idx
249func (s *StateDB) GetAccount(idx common.Idx) (*common.Account, error) {
250	return GetAccountInTreeDB(s.db.DB(), idx)
251}
252
253func accountsIter(db db.Storage, fn func(a *common.Account) (bool, error)) error {
254	idxDB := db.WithPrefix(PrefixKeyIdx)
255	if err := idxDB.Iterate(func(k []byte, v []byte) (bool, error) {
256		idx, err := common.IdxFromBytes(k)
257		if err != nil {
258			return false, tracerr.Wrap(err)
259		}
260		acc, err := GetAccountInTreeDB(db, idx)
261		if err != nil {
262			return false, tracerr.Wrap(err)
263		}
264		ok, err := fn(acc)
265		if err != nil {
266			return false, tracerr.Wrap(err)
267		}
268		return ok, nil
269	}); err != nil {
270		return tracerr.Wrap(err)
271	}
272	return nil
273}
274
275func getAccounts(db db.Storage) ([]common.Account, error) {
276	accs := []common.Account{}
277	if err := accountsIter(
278		db,
279		func(a *common.Account) (bool, error) {
280			accs = append(accs, *a)
281			return true, nil
282		},
283	); err != nil {
284		return nil, tracerr.Wrap(err)
285	}
286	return accs, nil
287}
288
289// TestGetAccounts returns all the accounts in the db.  Use only in tests.
290// Outside tests getting all the accounts is discouraged because it's an
291// expensive operation, but if you must do it, use `LastRead()` method to get a
292// thread-safe and consistent view of the stateDB.
293func (s *StateDB) TestGetAccounts() ([]common.Account, error) {
294	return getAccounts(s.db.DB())
295}
296
297// GetAccountInTreeDB is abstracted from StateDB to be used from StateDB and
298// from ExitTree.  GetAccount returns the account for the given Idx
299func GetAccountInTreeDB(sto db.Storage, idx common.Idx) (*common.Account, error) {
300	idxBytes, err := idx.Bytes()
301	if err != nil {
302		return nil, tracerr.Wrap(err)
303	}
304	vBytes, err := sto.Get(append(PrefixKeyIdx, idxBytes[:]...))
305	if err != nil {
306		return nil, tracerr.Wrap(err)
307	}
308	accBytes, err := sto.Get(append(PrefixKeyAccHash, vBytes...))
309	if err != nil {
310		return nil, tracerr.Wrap(err)
311	}
312	var b [32 * common.NLeafElems]byte
313	copy(b[:], accBytes)
314	account, err := common.AccountFromBytes(b)
315	if err != nil {
316		return nil, tracerr.Wrap(err)
317	}
318	account.Idx = idx
319	return account, nil
320}
321
322// CreateAccount creates a new Account in the StateDB for the given Idx.  If
323// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
324// MerkleTree, returning a CircomProcessorProof.
325func (s *StateDB) CreateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
326	cpp, err := CreateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
327	if err != nil {
328		return cpp, tracerr.Wrap(err)
329	}
330	// store idx by EthAddr & BJJ
331	err = s.setIdxByEthAddrBJJ(idx, account.EthAddr, account.BJJ, account.TokenID)
332	return cpp, tracerr.Wrap(err)
333}
334
335// CreateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
336// from ExitTree.  Creates a new Account in the StateDB for the given Idx.  If
337// StateDB.MT==nil, MerkleTree is not affected, otherwise updates the
338// MerkleTree, returning a CircomProcessorProof.
339func CreateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
340	// store at the DB the key: v, and value: leaf.Bytes()
341	v, err := account.HashValue()
342	if err != nil {
343		return nil, tracerr.Wrap(err)
344	}
345	accountBytes, err := account.Bytes()
346	if err != nil {
347		return nil, tracerr.Wrap(err)
348	}
349
350	// store the Leaf value
351	tx, err := sto.NewTx()
352	if err != nil {
353		return nil, tracerr.Wrap(err)
354	}
355
356	idxBytes, err := idx.Bytes()
357	if err != nil {
358		return nil, tracerr.Wrap(err)
359	}
360	_, err = tx.Get(append(PrefixKeyIdx, idxBytes[:]...))
361	if tracerr.Unwrap(err) != db.ErrNotFound {
362		return nil, tracerr.Wrap(ErrAccountAlreadyExists)
363	}
364
365	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
366	if err != nil {
367		return nil, tracerr.Wrap(err)
368	}
369	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
370	if err != nil {
371		return nil, tracerr.Wrap(err)
372	}
373
374	if err := tx.Commit(); err != nil {
375		return nil, tracerr.Wrap(err)
376	}
377
378	if mt != nil {
379		return mt.AddAndGetCircomProof(idx.BigInt(), v)
380	}
381
382	return nil, nil
383}
384
385// UpdateAccount updates the Account in the StateDB for the given Idx.  If
386// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
387// MerkleTree, returning a CircomProcessorProof.
388func (s *StateDB) UpdateAccount(idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
389	return UpdateAccountInTreeDB(s.db.DB(), s.MT, idx, account)
390}
391
392// UpdateAccountInTreeDB is abstracted from StateDB to be used from StateDB and
393// from ExitTree.  Updates the Account in the StateDB for the given Idx.  If
394// StateDB.mt==nil, MerkleTree is not affected, otherwise updates the
395// MerkleTree, returning a CircomProcessorProof.
396func UpdateAccountInTreeDB(sto db.Storage, mt *merkletree.MerkleTree, idx common.Idx, account *common.Account) (*merkletree.CircomProcessorProof, error) {
397	// store at the DB the key: v, and value: account.Bytes()
398	v, err := account.HashValue()
399	if err != nil {
400		return nil, tracerr.Wrap(err)
401	}
402	accountBytes, err := account.Bytes()
403	if err != nil {
404		return nil, tracerr.Wrap(err)
405	}
406
407	tx, err := sto.NewTx()
408	if err != nil {
409		return nil, tracerr.Wrap(err)
410	}
411	err = tx.Put(append(PrefixKeyAccHash, v.Bytes()...), accountBytes[:])
412	if err != nil {
413		return nil, tracerr.Wrap(err)
414	}
415	idxBytes, err := idx.Bytes()
416	if err != nil {
417		return nil, tracerr.Wrap(err)
418	}
419	err = tx.Put(append(PrefixKeyIdx, idxBytes[:]...), v.Bytes())
420	if err != nil {
421		return nil, tracerr.Wrap(err)
422	}
423
424	if err := tx.Commit(); err != nil {
425		return nil, tracerr.Wrap(err)
426	}
427
428	if mt != nil {
429		proof, err := mt.Update(idx.BigInt(), v)
430		return proof, tracerr.Wrap(err)
431	}
432	return nil, nil
433}
434
435// MTGetProof returns the CircomVerifierProof for a given Idx
436func (s *StateDB) MTGetProof(idx common.Idx) (*merkletree.CircomVerifierProof, error) {
437	if s.MT == nil {
438		return nil, tracerr.Wrap(ErrStateDBWithoutMT)
439	}
440	p, err := s.MT.GenerateSCVerifierProof(idx.BigInt(), s.MT.Root())
441	if err != nil {
442		return nil, tracerr.Wrap(err)
443	}
444	return p, nil
445}
446
447// Close the StateDB
448func (s *StateDB) Close() {
449	s.db.Close()
450}
451
452// LocalStateDB represents the local StateDB which allows to make copies from
453// the synchronizer StateDB, and is used by the tx-selector and the
454// batch-builder. LocalStateDB is an in-memory storage.
455type LocalStateDB struct {
456	*StateDB
457	synchronizerStateDB *StateDB
458}
459
460// NewLocalStateDB returns a new LocalStateDB connected to the given
461// synchronizerDB.  Checkpoints older than the value defined by `keep` will be
462// deleted.
463func NewLocalStateDB(path string, keep int, synchronizerDB *StateDB, typ TypeStateDB,
464	nLevels int) (*LocalStateDB, error) {
465	s, err := NewStateDB(path, keep, typ, nLevels)
466	if err != nil {
467		return nil, tracerr.Wrap(err)
468	}
469	return &LocalStateDB{
470		s,
471		synchronizerDB,
472	}, nil
473}
474
475// Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it
476// gets the state from LocalStateDB.synchronizerStateDB for the given batchNum.
477// If fromSynchronizer is false, get the state from LocalStateDB checkpoints.
478func (l *LocalStateDB) Reset(batchNum common.BatchNum, fromSynchronizer bool) error {
479	if fromSynchronizer {
480		if err := l.db.ResetFromSynchronizer(batchNum, l.synchronizerStateDB.db); err != nil {
481			return tracerr.Wrap(err)
482		}
483		// open the MT for the current s.db
484		if l.MT != nil {
485			mt, err := merkletree.NewMerkleTree(l.db.StorageWithPrefix(PrefixKeyMT),
486				l.MT.MaxLevels())
487			if err != nil {
488				return tracerr.Wrap(err)
489			}
490			l.MT = mt
491		}
492		return nil
493	}
494	// use checkpoint from LocalStateDB
495	return l.StateDB.Reset(batchNum)
496}