From 0ff3fb5ae78ee34a0579c77925e6ab1a955a0fbe Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 27 Oct 2020 12:50:16 +0100 Subject: [PATCH] ProcessTxs return an array of created accounts --- batchbuilder/batchbuilder.go | 2 +- db/statedb/txprocessors.go | 88 +++++++++++++++++++-------------- db/statedb/txprocessors_test.go | 15 ++++-- synchronizer/synchronizer.go | 2 +- txselector/txselector.go | 4 +- 5 files changed, 64 insertions(+), 47 deletions(-) diff --git a/batchbuilder/batchbuilder.go b/batchbuilder/batchbuilder.go index d54db54..7d28f5d 100644 --- a/batchbuilder/batchbuilder.go +++ b/batchbuilder/batchbuilder.go @@ -52,7 +52,7 @@ func (bb *BatchBuilder) Reset(batchNum common.BatchNum, fromSynchronizer bool) e // BuildBatch takes the transactions and returns the common.ZKInputs of the next batch func (bb *BatchBuilder) BuildBatch(coordIdxs []common.Idx, configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []common.L1Tx, pooll2txs []common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { - zkInputs, _, err := bb.localStateDB.ProcessTxs(coordIdxs, l1usertxs, l1coordinatortxs, pooll2txs) + zkInputs, _, _, err := bb.localStateDB.ProcessTxs(coordIdxs, l1usertxs, l1coordinatortxs, pooll2txs) if err != nil { return nil, err } diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go index 6565ed3..26a3b3c 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -38,26 +38,29 @@ type processedExit struct { // type==TypeSynchronizer, assumes that the call is done from the Synchronizer, // returns common.ExitTreeLeaf that is later used by the Synchronizer to update // the HistoryDB, and adds Nonce & TokenID to the L2Txs. -func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, error) { +// And if TypeSynchronizer returns an array of common.Account with all the +// created accounts. +func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) (*common.ZKInputs, []common.ExitInfo, []common.Account, error) { var err error var exitTree *merkletree.MerkleTree + var createdAccounts []common.Account if s.zki != nil { - return nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong and it's not empty") + return nil, nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong and it's not empty") } defer s.resetZKInputs() nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs) if nTx == 0 { // TODO return ZKInputs of batch without txs - return nil, nil, nil + return nil, nil, nil, nil } exits := make([]processedExit, nTx) // get TokenIDs of coordIdxs coordIdxsMap, err := s.getTokenIDsFromIdxs(coordIdxs) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if s.typ == TypeBatchBuilder { @@ -71,7 +74,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { tmpDir, err := ioutil.TempDir("", "hermez-statedb-exittree") if err != nil { - return nil, nil, err + return nil, nil, nil, err } defer func() { if err := os.RemoveAll(tmpDir); err != nil { @@ -80,19 +83,19 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs }() sto, err := pebble.NewPebbleStorage(tmpDir, false) if err != nil { - return nil, nil, err + return nil, nil, nil, err } exitTree, err = merkletree.NewMerkleTree(sto, s.mt.MaxLevels()) if err != nil { - return nil, nil, err + return nil, nil, nil, err } } // assumption: l1usertx are sorted by L1Tx.Position for i := 0; i < len(l1usertxs); i++ { - exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, &l1usertxs[i]) + exitIdx, exitAccount, newExit, createdAccount, err := s.processL1Tx(exitTree, &l1usertxs[i]) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { if exitIdx != nil && exitTree != nil { @@ -105,31 +108,26 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } s.i++ } + if s.typ == TypeSynchronizer && createdAccount != nil { + createdAccounts = append(createdAccounts, *createdAccount) + } } for i := 0; i < len(l1coordinatortxs); i++ { - exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, &l1coordinatortxs[i]) + exitIdx, _, _, createdAccount, err := s.processL1Tx(exitTree, &l1coordinatortxs[i]) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if exitIdx != nil { log.Error("Unexpected Exit in L1CoordinatorTx") } - if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { - if exitIdx != nil && exitTree != nil { - exits[s.i] = processedExit{ - exit: true, - newExit: newExit, - idx: *exitIdx, - acc: *exitAccount, - } - } - s.i++ + if s.typ == TypeSynchronizer && createdAccount != nil { + createdAccounts = append(createdAccounts, *createdAccount) } } for i := 0; i < len(l2txs); i++ { exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, exitTree, &l2txs[i]) if err != nil { - return nil, nil, err + return nil, nil, nil, err } if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { if exitIdx != nil && exitTree != nil { @@ -145,7 +143,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } if s.typ == TypeTxSelector { - return nil, nil, nil + return nil, nil, nil, nil } // once all txs processed (exitTree root frozen), for each Exit, @@ -161,7 +159,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs // 0. generate MerkleProof p, err := exitTree.GenerateCircomVerifierProof(exitIdx.BigInt(), nil) if err != nil { - return nil, nil, err + return nil, nil, nil, err } // 1. generate common.ExitInfo ei := common.ExitInfo{ @@ -192,7 +190,9 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } } if s.typ == TypeSynchronizer { - return nil, exitInfos, nil + // return exitInfos and createdAccounts, so Synchronizer will be able + // to store it into HistoryDB for the concrete BatchNum + return nil, exitInfos, createdAccounts, nil } // compute last ZKInputs parameters @@ -200,7 +200,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs // zki.FeeIdxs = ? // TODO, this will be get from the config file tokenIDs, err := s.getTokenIDsBigInt(l1usertxs, l1coordinatortxs, l2txs) if err != nil { - return nil, nil, err + return nil, nil, nil, err } s.zki.FeePlanTokens = tokenIDs @@ -209,9 +209,8 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs // TODO once the Node Config sets the Accounts where to send the Fees // compute fees & update ZKInputs - // return exitInfos, so Synchronizer will be able to store it into - // HistoryDB for the concrete BatchNum - return s.zki, exitInfos, nil + // return ZKInputs as the BatchBuilder will return it to forge the Batch + return s.zki, nil, nil, nil } // getTokenIDsBigInt returns the list of TokenIDs in *big.Int format @@ -243,7 +242,10 @@ func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []common.L1Tx, l // StateDB depending on the transaction Type. It returns the 3 parameters // related to the Exit (in case of): Idx, ExitAccount, boolean determining if // the Exit created a new Leaf in the ExitTree. -func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, bool, error) { +// And another *common.Account parameter which contains the created account in +// case that has been a new created account and that the StateDB is of type +// TypeSynchronizer. +func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, bool, *common.Account, error) { // ZKInputs if s.zki != nil { // Txs @@ -273,14 +275,14 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) err := s.applyTransfer(nil, tx.Tx(), 0) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } case common.TxTypeCreateAccountDeposit: // add new account to the MT, update balance of the MT account err := s.applyCreateAccount(tx) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } // TODO applyCreateAccount will return the created account, // which in the case type==TypeSynchronizer will be added to an @@ -295,7 +297,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) err := s.applyDeposit(tx, false) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } case common.TxTypeDepositTransfer: // update balance in MT account, update balance & nonce of sender @@ -303,7 +305,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) err := s.applyDeposit(tx, true) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } case common.TxTypeCreateAccountDepositTransfer: // add new account to the merkletree, update balance in MT account, @@ -311,7 +313,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) err := s.applyCreateAccountDepositTransfer(tx) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } if s.zki != nil { @@ -324,13 +326,23 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) exitAccount, newExit, err := s.applyExit(nil, exitTree, tx.Tx()) if err != nil { log.Error(err) - return nil, nil, false, err + return nil, nil, false, nil, err } - return &tx.FromIdx, exitAccount, newExit, nil + return &tx.FromIdx, exitAccount, newExit, nil, nil default: } - return nil, nil, false, nil + var createdAccount *common.Account + if s.typ == TypeSynchronizer && (tx.Type == common.TxTypeCreateAccountDeposit || tx.Type == common.TxTypeCreateAccountDepositTransfer) { + var err error + createdAccount, err = s.GetAccount(s.idx) + if err != nil { + log.Error(err) + return nil, nil, false, nil, err + } + } + + return nil, nil, false, createdAccount, nil } // processL2Tx process the given L2Tx applying the needed updates to the diff --git a/db/statedb/txprocessors_test.go b/db/statedb/txprocessors_test.go index 93474f3..c87390f 100644 --- a/db/statedb/txprocessors_test.go +++ b/db/statedb/txprocessors_test.go @@ -45,45 +45,50 @@ func TestProcessTxsSynchronizer(t *testing.T) { idxA1 := tc.Users["A"].Accounts[common.TokenID(1)].Idx log.Debug("1st batch, 1st block, only L1CoordinatorTxs") - _, _, err = sdb.ProcessTxs(nil, nil, blocks[0].Batches[0].L1CoordinatorTxs, nil) + _, _, createdAccounts, err := sdb.ProcessTxs(nil, nil, blocks[0].Batches[0].L1CoordinatorTxs, nil) require.Nil(t, err) + assert.Equal(t, 4, len(createdAccounts)) log.Debug("2nd batch, 1st block") l2Txs := common.L2TxsToPoolL2Txs(blocks[0].Batches[1].L2Txs) - _, exitInfos, err := sdb.ProcessTxs(coordIdxs, blocks[0].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs) + _, exitInfos, createdAccounts, err := sdb.ProcessTxs(coordIdxs, blocks[0].L1UserTxs, blocks[0].Batches[1].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 0, len(exitInfos)) + assert.Equal(t, 31, len(createdAccounts)) acc, err := sdb.GetAccount(idxA1) require.Nil(t, err) assert.Equal(t, "50", acc.Balance.String()) log.Debug("3rd batch, 1st block") l2Txs = common.L2TxsToPoolL2Txs(blocks[0].Batches[2].L2Txs) - _, exitInfos, err = sdb.ProcessTxs(coordIdxs, nil, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs) + _, exitInfos, createdAccounts, err = sdb.ProcessTxs(coordIdxs, nil, blocks[0].Batches[2].L1CoordinatorTxs, l2Txs) require.Nil(t, err) // TODO once TTGL is updated, add a check that a input poolL2Tx with // Nonce & TokenID =0, after ProcessTxs call has the expected value assert.Equal(t, 0, len(exitInfos)) + assert.Equal(t, 0, len(createdAccounts)) acc, err = sdb.GetAccount(idxA1) require.Nil(t, err) assert.Equal(t, "28", acc.Balance.String()) log.Debug("1st batch, 2nd block") l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Batches[0].L2Txs) - _, exitInfos, err = sdb.ProcessTxs(coordIdxs, nil, blocks[1].Batches[0].L1CoordinatorTxs, l2Txs) + _, exitInfos, createdAccounts, err = sdb.ProcessTxs(coordIdxs, nil, blocks[1].Batches[0].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 4, len(exitInfos)) // the 'ForceExit(1)' is not computed yet, as the batch is without L1UserTxs + assert.Equal(t, 1, len(createdAccounts)) acc, err = sdb.GetAccount(idxA1) require.Nil(t, err) assert.Equal(t, "53", acc.Balance.String()) log.Debug("2nd batch, 2nd block") l2Txs = common.L2TxsToPoolL2Txs(blocks[1].Batches[1].L2Txs) - _, exitInfos, err = sdb.ProcessTxs(coordIdxs, blocks[1].L1UserTxs, blocks[1].Batches[1].L1CoordinatorTxs, l2Txs) + _, exitInfos, createdAccounts, err = sdb.ProcessTxs(coordIdxs, blocks[1].L1UserTxs, blocks[1].Batches[1].L1CoordinatorTxs, l2Txs) require.Nil(t, err) assert.Equal(t, 2, len(exitInfos)) // 2, as previous batch was without L1UserTxs, and has pending the 'ForceExit(1) A: 5' + assert.Equal(t, 1, len(createdAccounts)) acc, err = sdb.GetAccount(idxA1) assert.Nil(t, err) assert.Equal(t, "78", acc.Balance.String()) diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index 3cb3532..61e8d43 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -405,7 +405,7 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { // TODO: Get CollectedFees from ProcessTxs() // TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs() // ProcessTxs updates poolL2Txs adding: Nonce, TokenID - _, exitInfo, err := s.stateDB.ProcessTxs([]common.Idx{}, l1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs) + _, exitInfo, _, err := s.stateDB.ProcessTxs([]common.Idx{}, l1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs) if err != nil { return nil, err } diff --git a/txselector/txselector.go b/txselector/txselector.go index ced653a..d11f683 100644 --- a/txselector/txselector.go +++ b/txselector/txselector.go @@ -89,7 +89,7 @@ func (txsel *TxSelector) GetL2TxSelection(coordIdxs []common.Idx, batchNum commo txs := txsel.getL2Profitable(validTxs, txsel.MaxTxs) // process the txs in the local AccountsDB - _, _, err = txsel.localAccountsDB.ProcessTxs(coordIdxs, nil, nil, txs) + _, _, _, err = txsel.localAccountsDB.ProcessTxs(coordIdxs, nil, nil, txs) if err != nil { return nil, err } @@ -238,7 +238,7 @@ func (txsel *TxSelector) GetL1L2TxSelection(coordIdxs []common.Idx, batchNum com l2Txs := txsel.getL2Profitable(validTxs, maxL2Txs) // process the txs in the local AccountsDB - _, _, err = txsel.localAccountsDB.ProcessTxs(coordIdxs, l1Txs, l1CoordinatorTxs, l2Txs) + _, _, _, err = txsel.localAccountsDB.ProcessTxs(coordIdxs, l1Txs, l1CoordinatorTxs, l2Txs) if err != nil { return nil, nil, nil, err }