diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go index 7c06459..904e1c2 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -37,6 +37,7 @@ type ProcessTxOutput struct { ExitInfos []common.ExitInfo CreatedAccounts []common.Account CoordinatorIdxsMap map[common.TokenID]common.Idx + CollectedFees map[common.TokenID]*big.Int } // ProcessTxs process the given L1Txs & L2Txs applying the needed updates to @@ -66,6 +67,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs ExitInfos: nil, CreatedAccounts: nil, CoordinatorIdxsMap: nil, + CollectedFees: nil, }, nil } exits := make([]processedExit, nTx) @@ -76,6 +78,14 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs return nil, err } + var collectedFees map[common.TokenID]*big.Int + if s.typ == TypeSynchronizer { + collectedFees = make(map[common.TokenID]*big.Int) + for tokenID := range coordIdxsMap { + collectedFees[tokenID] = big.NewInt(0) + } + } + if s.typ == TypeBatchBuilder { s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function, taken from config file/coordinator call s.zki.OldLastIdx = (s.idx - 1).BigInt() @@ -138,7 +148,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } } for i := 0; i < len(l2txs); i++ { - exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, exitTree, &l2txs[i]) + exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, collectedFees, exitTree, &l2txs[i]) if err != nil { return nil, err } @@ -211,6 +221,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs ExitInfos: exitInfos, CreatedAccounts: createdAccounts, CoordinatorIdxsMap: coordIdxsMap, + CollectedFees: collectedFees, }, nil } @@ -234,6 +245,7 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs ExitInfos: nil, CreatedAccounts: nil, CoordinatorIdxsMap: coordIdxsMap, + CollectedFees: nil, }, nil } @@ -296,7 +308,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) // coordIdxsMap is 'nil', as at L1Txs there is no L2 fees // 0 for the parameter toIdx, as at L1Tx ToIdx can only be 0 in the Deposit type case. - err := s.applyTransfer(nil, tx.Tx(), 0) + err := s.applyTransfer(nil, nil, tx.Tx(), 0) if err != nil { log.Error(err) return nil, nil, false, nil, err @@ -347,7 +359,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) case common.TxTypeForceExit: // execute exit flow // coordIdxsMap is 'nil', as at L1Txs there is no L2 fees - exitAccount, newExit, err := s.applyExit(nil, exitTree, tx.Tx()) + exitAccount, newExit, err := s.applyExit(nil, nil, exitTree, tx.Tx()) if err != nil { log.Error(err) return nil, nil, false, nil, err @@ -373,7 +385,8 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) // 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) processL2Tx(coordIdxsMap map[common.TokenID]common.Idx, exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) { +func (s *StateDB) processL2Tx(coordIdxsMap map[common.TokenID]common.Idx, collectedFees map[common.TokenID]*big.Int, + exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) { var err error // if tx.ToIdx==0, get toIdx by ToEthAddr or ToBJJ if tx.ToIdx == common.Idx(0) && tx.AuxToIdx == common.Idx(0) { @@ -440,14 +453,14 @@ func (s *StateDB) processL2Tx(coordIdxsMap map[common.TokenID]common.Idx, exitTr case common.TxTypeTransfer, common.TxTypeTransferToEthAddr, common.TxTypeTransferToBJJ: // go to the MT account of sender and receiver, and update // balance & nonce - err = s.applyTransfer(coordIdxsMap, tx.Tx(), tx.AuxToIdx) + err = s.applyTransfer(coordIdxsMap, collectedFees, tx.Tx(), tx.AuxToIdx) if err != nil { log.Error(err) return nil, nil, false, err } case common.TxTypeExit: // execute exit flow - exitAccount, newExit, err := s.applyExit(coordIdxsMap, exitTree, tx.Tx()) + exitAccount, newExit, err := s.applyExit(coordIdxsMap, collectedFees, exitTree, tx.Tx()) if err != nil { log.Error(err) return nil, nil, false, err @@ -565,7 +578,8 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { // tx.ToIdx==0, then toIdx!=0, and will be used the toIdx parameter as Idx of // the receiver. This parameter is used when the tx.ToIdx is not specified and // the real ToIdx is found trhrough the ToEthAddr or ToBJJ. -func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, tx common.Tx, auxToIdx common.Idx) error { +func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, collectedFees map[common.TokenID]*big.Int, + tx common.Tx, auxToIdx common.Idx) error { if auxToIdx == common.Idx(0) { auxToIdx = tx.ToIdx } @@ -600,6 +614,10 @@ func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, tx c log.Error(err) return err } + if s.typ == TypeSynchronizer { + collected := collectedFees[tx.TokenID] + collected.Add(collected, fee) + } } } @@ -709,7 +727,8 @@ func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error { // It returns the ExitAccount and a boolean determining if the Exit created a // new Leaf in the ExitTree. -func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, exitTree *merkletree.MerkleTree, tx common.Tx) (*common.Account, bool, error) { +func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, collectedFees map[common.TokenID]*big.Int, + exitTree *merkletree.MerkleTree, tx common.Tx) (*common.Account, bool, error) { // 0. subtract tx.Amount from current Account in StateMT // add the tx.Amount into the Account (tx.FromIdx) in the ExitMT acc, err := s.GetAccount(tx.FromIdx) @@ -734,6 +753,10 @@ func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, exitTree log.Error(err) return nil, false, err } + if s.typ == TypeSynchronizer { + collected := collectedFees[tx.TokenID] + collected.Add(collected, fee) + } } } diff --git a/db/statedb/txprocessors_test.go b/db/statedb/txprocessors_test.go index 70869c0..8a78e0e 100644 --- a/db/statedb/txprocessors_test.go +++ b/db/statedb/txprocessors_test.go @@ -13,6 +13,8 @@ import ( "github.com/stretchr/testify/require" ) +// TODO: Test CollectedFees output + func TestProcessTxsSynchronizer(t *testing.T) { // TODO once TTGL is updated, use the blockchain L2Tx (not PoolL2Tx) for // the Synchronizer tests diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index c0f9498..a2ed3bf 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -296,7 +296,6 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { blockNum := ethBlock.EthBlockNum var rollupData = newRollupData() // var forgeL1TxsNum int64 - var numAccounts int // Get rollup events in the block, and make sure the block hash matches // the expected one. @@ -393,20 +392,19 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { // processed. poolL2Txs := common.L2TxsToPoolL2Txs(forgeBatchArgs.L2TxsData) // TODO: This is a big ugly, find a better way - // TODO: Get CollectedFees from ProcessTxs() - // TODO: Pass forgeBatchArgs.FeeIdxCoordinator to ProcessTxs() - // ProcessTxs updates poolL2Txs adding: Nonce, TokenID - ptOut, err := s.stateDB.ProcessTxs([]common.Idx{}, l1UserTxs, batchData.L1CoordinatorTxs, poolL2Txs) + // ProcessTxs updates poolL2Txs adding: Nonce (and also TokenID, but we don't use it). + processTxsOut, err := s.stateDB.ProcessTxs(forgeBatchArgs.FeeIdxCoordinator, l1UserTxs, + batchData.L1CoordinatorTxs, poolL2Txs) if err != nil { return nil, err } // Set batchNum in exits - for i := range ptOut.ExitInfos { - exit := &ptOut.ExitInfos[i] + for i := range processTxsOut.ExitInfos { + exit := &processTxsOut.ExitInfos[i] exit.BatchNum = batchNum } - batchData.ExitTree = ptOut.ExitInfos + batchData.ExitTree = processTxsOut.ExitInfos l2Txs, err := common.PoolL2TxsToL2Txs(poolL2Txs) // TODO: This is a big uggly, find a better way if err != nil { @@ -427,11 +425,11 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { position++ } - for i := range ptOut.CreatedAccounts { - createdAccount := &ptOut.CreatedAccounts[i] + for i := range processTxsOut.CreatedAccounts { + createdAccount := &processTxsOut.CreatedAccounts[i] createdAccount.BatchNum = batchNum } - batchData.CreatedAccounts = ptOut.CreatedAccounts + batchData.CreatedAccounts = processTxsOut.CreatedAccounts slotNum := int64(0) if ethBlock.EthBlockNum >= s.auctionConstants.GenesisBlockNum { @@ -441,15 +439,15 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { // Get Batch information batch := common.Batch{ - BatchNum: batchNum, - EthBlockNum: blockNum, - ForgerAddr: *sender, - // CollectedFees: , TODO: Clarify where to get them if they are still needed - StateRoot: forgeBatchArgs.NewStRoot, - NumAccounts: numAccounts, // TODO: Calculate this value - LastIdx: forgeBatchArgs.NewLastIdx, - ExitRoot: forgeBatchArgs.NewExitRoot, - SlotNum: slotNum, + BatchNum: batchNum, + EthBlockNum: blockNum, + ForgerAddr: *sender, + CollectedFees: processTxsOut.CollectedFees, + StateRoot: forgeBatchArgs.NewStRoot, + NumAccounts: len(batchData.CreatedAccounts), + LastIdx: forgeBatchArgs.NewLastIdx, + ExitRoot: forgeBatchArgs.NewExitRoot, + SlotNum: slotNum, } nextForgeL1TxsNumCpy := nextForgeL1TxsNum if forgeBatchArgs.L1Batch { @@ -471,7 +469,6 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*rollupData, error) { if consts, err := s.ethClient.EthERC20Consts(evtAddToken.TokenAddress); err != nil { log.Warnw("Error retreiving ERC20 token constants", "addr", evtAddToken.TokenAddress) - // TODO: Add external information consulting SC about it using Address token.Name = "ERC20_ETH_ERROR" token.Symbol = "ERROR" token.Decimals = 1 diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 8ff1a0b..313251f 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -123,6 +123,7 @@ func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBloc // has a TotalFeesUSD inserted by the HistoryDB batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD batch.CreatedAccounts = syncBatch.CreatedAccounts // til doesn't output CreatedAccounts + batch.Batch.NumAccounts = len(batch.CreatedAccounts) // Test field by field to facilitate debugging of errors assert.Equal(t, batch.L1CoordinatorTxs, syncBatch.L1CoordinatorTxs) @@ -135,6 +136,12 @@ func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBloc assert.Equal(t, exit.Balance, syncBatch.ExitTree[j].Balance) *exit = syncBatch.ExitTree[j] } + // We are collecting fees after blockNum=2 in 2 idxs + if block.Block.EthBlockNum > 2 { + // fmt.Printf("DBG collectedFees: %+v\n", syncBatch.Batch.CollectedFees) + assert.Equal(t, 2, len(syncBatch.Batch.CollectedFees)) + } + batch.Batch.CollectedFees = syncBatch.Batch.CollectedFees assert.Equal(t, batch.Batch, syncBatch.Batch) assert.Equal(t, batch, syncBatch) assert.Equal(t, &batch.Batch, dbBatch) //nolint:gosec @@ -254,10 +261,6 @@ func TestSync(t *testing.T) { // Generate blockchain and smart contract data, and fill the test smart contracts // - // TODO: Test CreateAccountDepositTransfer - // TODO: Test ForceTransfer - // TODO: Test ForceExit - // Generate blockchain data with til set1 := ` Type: Blockchain @@ -266,23 +269,28 @@ func TestSync(t *testing.T) { AddToken(2) AddToken(3) - CreateAccountDeposit(1) C: 20 // Idx=256+2=258 - CreateAccountDeposit(2) A: 20 // Idx=256+3=259 - CreateAccountDeposit(1) D: 5 // Idx=256+4=260 - CreateAccountDeposit(2) B: 5 // Idx=256+5=261 - CreateAccountDeposit(2) C: 5 // Idx=256+6=262 + CreateAccountDeposit(1) C: 2000 // Idx=256+2=258 + CreateAccountDeposit(2) A: 2000 // Idx=256+3=259 + CreateAccountDeposit(1) D: 500 // Idx=256+4=260 + CreateAccountDeposit(2) B: 500 // Idx=256+5=261 + CreateAccountDeposit(2) C: 500 // Idx=256+6=262 CreateAccountCoordinator(1) A // Idx=256+0=256 CreateAccountCoordinator(1) B // Idx=256+1=257 - > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs - > batchL1 // forge defined L1UserTxs, freeze L1UserTxs{nil} + > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{5} + > batchL1 // forge defined L1UserTxs{5}, freeze L1UserTxs{nil} > block // blockNum=2 - Transfer(1) C-A: 10 (10) - Exit(1) D: 3 (5) + CreateAccountDepositTransfer(1) E-A: 1000, 200 // Idx=256+7=263 + ForceExit(1) A: 100 + ForceTransfer(1) A-D: 100 + + Transfer(1) C-A: 100 (200) + Exit(1) D: 30 (200) - > batch + > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{2} + > batchL1 // forge L1UserTxs{2}, freeze defined L1UserTxs{nil} > block // blockNum=3 ` @@ -301,7 +309,8 @@ func TestSync(t *testing.T) { // blocks 1 (blockNum=3) i = 1 require.Equal(t, 3, int(blocks[i].Block.EthBlockNum)) - require.Equal(t, 1, len(blocks[i].Batches)) + require.Equal(t, 3, len(blocks[i].L1UserTxs)) + require.Equal(t, 2, len(blocks[i].Batches)) require.Equal(t, 2, len(blocks[i].Batches[0].L2Txs)) // Generate extra required data @@ -330,6 +339,12 @@ func TestSync(t *testing.T) { require.Nil(t, err) } client.CtlSetAddr(bootCoordAddr) + feeIdxCoordinator := []common.Idx{} + if block.Block.EthBlockNum > 2 { + // After blockNum=2 we have some accounts, use them as + // coordinator owned to receive fees. + feeIdxCoordinator = []common.Idx{common.Idx(256), common.Idx(259)} + } for _, batch := range block.Batches { _, err := client.RollupForgeBatch(ð.RollupForgeBatchArgs{ NewLastIdx: batch.Batch.LastIdx, @@ -338,7 +353,7 @@ func TestSync(t *testing.T) { L1CoordinatorTxs: batch.L1CoordinatorTxs, L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty L2TxsData: batch.L2Txs, - FeeIdxCoordinator: []common.Idx{}, // TODO + FeeIdxCoordinator: feeIdxCoordinator, // Circuit selector VerifierIdx: 0, // Intentionally empty L1Batch: batch.L1Batch, diff --git a/test/til/txs.go b/test/til/txs.go index d404421..c7a6e6d 100644 --- a/test/til/txs.go +++ b/test/til/txs.go @@ -257,6 +257,7 @@ func (tc *Context) GenerateBlocks(set string) ([]common.BlockData, error) { } tx := common.L2Tx{ ToIdx: common.Idx(1), // as is an Exit + Fee: common.FeeSelector(inst.fee), Amount: big.NewInt(int64(inst.amount)), Type: common.TxTypeExit, EthBlockNum: tc.blockNum,