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