diff --git a/batchbuilder/batchbuilder.go b/batchbuilder/batchbuilder.go index 6d8a130..c2f0539 100644 --- a/batchbuilder/batchbuilder.go +++ b/batchbuilder/batchbuilder.go @@ -1,18 +1,11 @@ package batchbuilder import ( - "encoding/binary" - "math/big" - ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" "github.com/hermeznetwork/hermez-node/db/statedb" - "github.com/iden3/go-merkletree/db" ) -// KEYIDX is used as key in the db to store the current Idx -var KEYIDX = []byte("idx") - // ConfigCircuit contains the circuit configuration type ConfigCircuit struct { TxsMax uint64 @@ -23,8 +16,6 @@ type ConfigCircuit struct { // BatchBuilder implements the batch builder type, which contains the // functionalities type BatchBuilder struct { - // idx holds the current Idx that the BatchBuilder is using - idx uint64 localStateDB *statedb.LocalStateDB configCircuits []ConfigCircuit } @@ -56,209 +47,29 @@ func NewBatchBuilder(dbpath string, synchronizerStateDB *statedb.StateDB, config // copy of the rollup state from the Synchronizer at that `batchNum`, otherwise // it can just roll back the internal copy. func (bb *BatchBuilder) Reset(batchNum uint64, fromSynchronizer bool) error { - if batchNum == 0 { - bb.idx = 0 - return nil - } - err := bb.localStateDB.Reset(batchNum, fromSynchronizer) - if err != nil { - return err - } - // idx is obtained from the statedb reset - bb.idx, err = bb.getIdx() - return err + return bb.localStateDB.Reset(batchNum, fromSynchronizer) } // BuildBatch takes the transactions and returns the common.ZKInputs of the next batch -func (bb *BatchBuilder) BuildBatch(configBatch ConfigBatch, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { +func (bb *BatchBuilder) BuildBatch(configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { for _, tx := range l1usertxs { - err := bb.processL1Tx(tx) + err := bb.localStateDB.ProcessL1Tx(tx) if err != nil { return nil, err } } for _, tx := range l1coordinatortxs { - err := bb.processL1Tx(tx) + err := bb.localStateDB.ProcessL1Tx(tx) if err != nil { return nil, err } } for _, tx := range l2txs { - switch tx.Type { - case common.TxTypeTransfer: - // go to the MT account of sender and receiver, and update - // balance & nonce - err := bb.applyTransfer(tx.Tx()) - if err != nil { - return nil, err - } - case common.TxTypeExit: - // execute exit flow - default: - } - } - - return nil, nil -} - -func (bb *BatchBuilder) processL1Tx(tx *common.L1Tx) error { - switch tx.Type { - case common.TxTypeForceTransfer, common.TxTypeTransfer: - // go to the MT account of sender and receiver, and update balance - // & nonce - err := bb.applyTransfer(tx.Tx()) - if err != nil { - return err - } - case common.TxTypeCreateAccountDeposit: - // add new account to the MT, update balance of the MT account - err := bb.applyCreateAccount(tx) - if err != nil { - return err - } - case common.TxTypeDeposit: // TODO check if this type will ever exist, or will be TxTypeDepositAndTransfer with transfer 0 value - // update balance of the MT account - err := bb.applyDeposit(tx, false) - if err != nil { - return err - } - case common.TxTypeDepositAndTransfer: - // update balance in MT account, update balance & nonce of sender - // & receiver - err := bb.applyDeposit(tx, true) + err := bb.localStateDB.ProcessPoolL2Tx(tx) if err != nil { - return err - } - case common.TxTypeCreateAccountDepositAndTransfer: - // add new account to the merkletree, update balance in MT account, - // update balance & nonce of sender & receiver - err := bb.applyCreateAccount(tx) - if err != nil { - return err - } - err = bb.applyTransfer(tx.Tx()) - if err != nil { - return err - } - case common.TxTypeExit: - // execute exit flow - default: - } - - return nil -} - -// applyCreateAccount creates a new account in the account of the depositer, it -// stores the deposit value -func (bb *BatchBuilder) applyCreateAccount(tx *common.L1Tx) error { - account := &common.Account{ - TokenID: tx.TokenID, - Nonce: 0, - Balance: tx.LoadAmount, - PublicKey: tx.FromBJJ, - EthAddr: tx.FromEthAddr, - } - - err := bb.localStateDB.CreateAccount(common.Idx(bb.idx+1), account) - if err != nil { - return err - } - - bb.idx = bb.idx + 1 - return bb.setIdx(bb.idx) -} - -// applyDeposit updates the balance in the account of the depositer, if -// andTransfer parameter is set to true, the method will also apply the -// Transfer of the L1Tx/DepositAndTransfer -func (bb *BatchBuilder) applyDeposit(tx *common.L1Tx, transfer bool) error { - // deposit the tx.LoadAmount into the sender account - accSender, err := bb.localStateDB.GetAccount(tx.FromIdx) - if err != nil { - return err - } - accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) - - // in case that the tx is a L1Tx>DepositAndTransfer - if transfer { - accReceiver, err := bb.localStateDB.GetAccount(tx.ToIdx) - if err != nil { - return err - } - // substract amount to the sender - accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) - // add amount to the receiver - accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) - // update receiver account in localStateDB - err = bb.localStateDB.UpdateAccount(tx.ToIdx, accReceiver) - if err != nil { - return err + return nil, err } } - // update sender account in localStateDB - err = bb.localStateDB.UpdateAccount(tx.FromIdx, accSender) - if err != nil { - return err - } - return nil -} - -// applyTransfer updates the balance & nonce in the account of the sender, and -// the balance in the account of the receiver -func (bb *BatchBuilder) applyTransfer(tx *common.Tx) error { - // get sender and receiver accounts from localStateDB - accSender, err := bb.localStateDB.GetAccount(tx.FromIdx) - if err != nil { - return err - } - accReceiver, err := bb.localStateDB.GetAccount(tx.ToIdx) - if err != nil { - return err - } - - // substract amount to the sender - accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) - // add amount to the receiver - accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) - - // update receiver account in localStateDB - err = bb.localStateDB.UpdateAccount(tx.ToIdx, accReceiver) - if err != nil { - return err - } - // update sender account in localStateDB - err = bb.localStateDB.UpdateAccount(tx.FromIdx, accSender) - if err != nil { - return err - } - - return nil -} - -// getIdx returns the stored Idx from the localStateDB, which is the last Idx used for an Account in the localStateDB. -func (bb *BatchBuilder) getIdx() (uint64, error) { - idxBytes, err := bb.localStateDB.DB().Get(KEYIDX) - if err == db.ErrNotFound { - return 0, nil - } - if err != nil { - return 0, err - } - idx := binary.LittleEndian.Uint64(idxBytes[:8]) - return idx, nil -} -// setIdx stores Idx in the localStateDB -func (bb *BatchBuilder) setIdx(idx uint64) error { - tx, err := bb.localStateDB.DB().NewTx() - if err != nil { - return err - } - var idxBytes [8]byte - binary.LittleEndian.PutUint64(idxBytes[:], idx) - tx.Put(KEYIDX, idxBytes[:]) - if err := tx.Commit(); err != nil { - return err - } - return nil + return nil, nil } diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go index 1df7154..3113982 100644 --- a/db/statedb/statedb.go +++ b/db/statedb/statedb.go @@ -34,6 +34,8 @@ type StateDB struct { currentBatch uint64 db *pebble.PebbleStorage mt *merkletree.MerkleTree + // idx holds the current Idx that the BatchBuilder is using + idx uint64 } // NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk @@ -147,6 +149,11 @@ func (s *StateDB) DeleteCheckpoint(batchNum uint64) error { // those checkpoints will remain in the storage, and eventually will be // deleted when MakeCheckpoint overwrites them. func (s *StateDB) Reset(batchNum uint64) error { + if batchNum == 0 { + s.idx = 0 + return nil + } + checkpointPath := s.path + PATHBATCHNUM + strconv.Itoa(int(batchNum)) currentPath := s.path + PATHCURRENT @@ -174,7 +181,9 @@ func (s *StateDB) Reset(batchNum uint64) error { if err != nil { return err } - return nil + // idx is obtained from the statedb reset + s.idx, err = s.getIdx() + return err } // GetAccount returns the account for the given Idx @@ -315,6 +324,10 @@ func NewLocalStateDB(path string, synchronizerDB *StateDB, withMT bool, nLevels // Reset performs a reset in the LocaStateDB. If fromSynchronizer is true, it // gets the state from LocalStateDB.synchronizerStateDB for the given batchNum. If fromSynchronizer is false, get the state from LocalStateDB checkpoints. func (l *LocalStateDB) Reset(batchNum uint64, fromSynchronizer bool) error { + if batchNum == 0 { + l.idx = 0 + return nil + } synchronizerCheckpointPath := l.synchronizerStateDB.path + PATHBATCHNUM + strconv.Itoa(int(batchNum)) checkpointPath := l.path + PATHBATCHNUM + strconv.Itoa(int(batchNum)) diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go new file mode 100644 index 0000000..a0148ad --- /dev/null +++ b/db/statedb/txprocessors.go @@ -0,0 +1,195 @@ +package statedb + +import ( + "encoding/binary" + "math/big" + + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-merkletree/db" +) + +// KEYIDX is used as key in the db to store the current Idx +var KEYIDX = []byte("idx") + +// ProcessPoolL2Tx process the given PoolL2Tx applying the needed updates to +// the StateDB depending on the transaction Type. +func (s *StateDB) ProcessPoolL2Tx(tx *common.PoolL2Tx) error { + switch tx.Type { + case common.TxTypeTransfer: + // go to the MT account of sender and receiver, and update + // balance & nonce + err := s.applyTransfer(tx.Tx()) + if err != nil { + return err + } + case common.TxTypeExit: + // execute exit flow + default: + } + return nil +} + +// ProcessL1Tx process the given L1Tx applying the needed updates to the +// StateDB depending on the transaction Type. +func (s *StateDB) ProcessL1Tx(tx *common.L1Tx) error { + switch tx.Type { + case common.TxTypeForceTransfer, common.TxTypeTransfer: + // go to the MT account of sender and receiver, and update balance + // & nonce + err := s.applyTransfer(tx.Tx()) + if err != nil { + return err + } + case common.TxTypeCreateAccountDeposit: + // add new account to the MT, update balance of the MT account + err := s.applyCreateAccount(tx) + if err != nil { + return err + } + case common.TxTypeDeposit: + // update balance of the MT account + err := s.applyDeposit(tx, false) + if err != nil { + return err + } + case common.TxTypeDepositTransfer: + // update balance in MT account, update balance & nonce of sender + // & receiver + err := s.applyDeposit(tx, true) + if err != nil { + return err + } + case common.TxTypeCreateAccountDepositTransfer: + // add new account to the merkletree, update balance in MT account, + // update balance & nonce of sender & receiver + err := s.applyCreateAccount(tx) + if err != nil { + return err + } + err = s.applyTransfer(tx.Tx()) + if err != nil { + return err + } + case common.TxTypeExit: + // execute exit flow + default: + } + + return nil +} + +// applyCreateAccount creates a new account in the account of the depositer, it +// stores the deposit value +func (s *StateDB) applyCreateAccount(tx *common.L1Tx) error { + account := &common.Account{ + TokenID: tx.TokenID, + Nonce: 0, + Balance: tx.LoadAmount, + PublicKey: tx.FromBJJ, + EthAddr: tx.FromEthAddr, + } + + err := s.CreateAccount(common.Idx(s.idx+1), account) + if err != nil { + return err + } + + s.idx = s.idx + 1 + return s.setIdx(s.idx) +} + +// applyDeposit updates the balance in the account of the depositer, if +// andTransfer parameter is set to true, the method will also apply the +// Transfer of the L1Tx/DepositTransfer +func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { + // deposit the tx.LoadAmount into the sender account + accSender, err := s.GetAccount(tx.FromIdx) + if err != nil { + return err + } + accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) + + // in case that the tx is a L1Tx>DepositTransfer + if transfer { + accReceiver, err := s.GetAccount(tx.ToIdx) + if err != nil { + return err + } + // substract amount to the sender + accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) + // add amount to the receiver + accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) + // update receiver account in localStateDB + err = s.UpdateAccount(tx.ToIdx, accReceiver) + if err != nil { + return err + } + } + // update sender account in localStateDB + err = s.UpdateAccount(tx.FromIdx, accSender) + if err != nil { + return err + } + return nil +} + +// applyTransfer updates the balance & nonce in the account of the sender, and +// the balance in the account of the receiver +func (s *StateDB) applyTransfer(tx *common.Tx) error { + // get sender and receiver accounts from localStateDB + accSender, err := s.GetAccount(tx.FromIdx) + if err != nil { + return err + } + accReceiver, err := s.GetAccount(tx.ToIdx) + if err != nil { + return err + } + + // substract amount to the sender + accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) + // add amount to the receiver + accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) + + // update receiver account in localStateDB + err = s.UpdateAccount(tx.ToIdx, accReceiver) + if err != nil { + return err + } + // update sender account in localStateDB + err = s.UpdateAccount(tx.FromIdx, accSender) + if err != nil { + return err + } + + return nil +} + +// getIdx returns the stored Idx from the localStateDB, which is the last Idx +// used for an Account in the localStateDB. +func (s *StateDB) getIdx() (uint64, error) { + idxBytes, err := s.DB().Get(KEYIDX) + if err == db.ErrNotFound { + return 0, nil + } + if err != nil { + return 0, err + } + idx := binary.LittleEndian.Uint64(idxBytes[:8]) + return idx, nil +} + +// setIdx stores Idx in the localStateDB +func (s *StateDB) setIdx(idx uint64) error { + tx, err := s.DB().NewTx() + if err != nil { + return err + } + var idxBytes [8]byte + binary.LittleEndian.PutUint64(idxBytes[:], idx) + tx.Put(KEYIDX, idxBytes[:]) + if err := tx.Commit(); err != nil { + return err + } + return nil +}