diff --git a/common/batch.go b/common/batch.go index 8a76a93..e8d7d1f 100644 --- a/common/batch.go +++ b/common/batch.go @@ -12,17 +12,18 @@ const batchNumBytesLen = 8 // Batch is a struct that represents Hermez network batch type Batch struct { - BatchNum BatchNum `meddler:"batch_num"` - EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum block in which the batch is forged - ForgerAddr ethCommon.Address `meddler:"forger_addr"` - CollectedFees map[TokenID]*big.Int `meddler:"fees_collected,json"` - StateRoot *big.Int `meddler:"state_root,bigint"` - NumAccounts int `meddler:"num_accounts"` - LastIdx int64 `meddler:"last_idx"` - ExitRoot *big.Int `meddler:"exit_root,bigint"` - ForgeL1TxsNum *int64 `meddler:"forge_l1_txs_num"` // optional, Only when the batch forges L1 txs. Identifier that corresponds to the group of L1 txs forged in the current batch. - SlotNum int64 `meddler:"slot_num"` // Slot in which the batch is forged - TotalFeesUSD *float64 `meddler:"total_fees_usd"` + BatchNum BatchNum `meddler:"batch_num"` + EthBlockNum int64 `meddler:"eth_block_num"` // Ethereum block in which the batch is forged + ForgerAddr ethCommon.Address `meddler:"forger_addr"` + CollectedFees map[TokenID]*big.Int `meddler:"fees_collected,json"` + FeeIdxsCoordinator []Idx `meddler:"fee_idxs_coordinator,json"` + StateRoot *big.Int `meddler:"state_root,bigint"` + NumAccounts int `meddler:"num_accounts"` + LastIdx int64 `meddler:"last_idx"` + ExitRoot *big.Int `meddler:"exit_root,bigint"` + ForgeL1TxsNum *int64 `meddler:"forge_l1_txs_num"` // optional, Only when the batch forges L1 txs. Identifier that corresponds to the group of L1 txs forged in the current batch. + SlotNum int64 `meddler:"slot_num"` // Slot in which the batch is forged + TotalFeesUSD *float64 `meddler:"total_fees_usd"` } // BatchNum identifies a batch diff --git a/db/historydb/historydb.go b/db/historydb/historydb.go index 5542395..1e9e4ab 100644 --- a/db/historydb/historydb.go +++ b/db/historydb/historydb.go @@ -279,7 +279,7 @@ func (hdb *HistoryDB) GetAllBatches() ([]common.Batch, error) { err := meddler.QueryAll( hdb.db, &batches, `SELECT batch.batch_num, batch.eth_block_num, batch.forger_addr, batch.fees_collected, - batch.state_root, batch.num_accounts, batch.last_idx, batch.exit_root, + batch.fee_idxs_coordinator, batch.state_root, batch.num_accounts, batch.last_idx, batch.exit_root, batch.forge_l1_txs_num, batch.slot_num, batch.total_fees_usd FROM batch;`, ) return db.SlicePtrsToSlice(batches).([]common.Batch), err diff --git a/db/migrations/0001.sql b/db/migrations/0001.sql index f7332de..789dab9 100644 --- a/db/migrations/0001.sql +++ b/db/migrations/0001.sql @@ -21,6 +21,7 @@ CREATE TABLE batch ( eth_block_num BIGINT NOT NULL REFERENCES block (eth_block_num) ON DELETE CASCADE, forger_addr BYTEA NOT NULL, -- fake foreign key for coordinator fees_collected BYTEA NOT NULL, + fee_idxs_coordinator BYTEA NOT NULL, state_root BYTEA NOT NULL, num_accounts BIGINT NOT NULL, last_idx BIGINT NOT NULL, diff --git a/synchronizer/synchronizer.go b/synchronizer/synchronizer.go index ba7c5e7..bc2df8b 100644 --- a/synchronizer/synchronizer.go +++ b/synchronizer/synchronizer.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "fmt" + "math/big" "github.com/ethereum/go-ethereum" "github.com/hermeznetwork/hermez-node/common" @@ -467,6 +468,8 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*common.RollupData, e for i := range processTxsOut.CreatedAccounts { createdAccount := &processTxsOut.CreatedAccounts[i] + createdAccount.Nonce = 0 + createdAccount.Balance = big.NewInt(0) createdAccount.BatchNum = batchNum } batchData.CreatedAccounts = processTxsOut.CreatedAccounts @@ -478,16 +481,18 @@ func (s *Synchronizer) rollupSync(ethBlock *common.Block) (*common.RollupData, e } // Get Batch information + // fmt.Printf("DBG: %#v\n", forgeBatchArgs.FeeIdxCoordinator) batch := common.Batch{ - BatchNum: batchNum, - EthBlockNum: blockNum, - ForgerAddr: *sender, - CollectedFees: processTxsOut.CollectedFees, - StateRoot: forgeBatchArgs.NewStRoot, - NumAccounts: len(batchData.CreatedAccounts), - LastIdx: forgeBatchArgs.NewLastIdx, - ExitRoot: forgeBatchArgs.NewExitRoot, - SlotNum: slotNum, + BatchNum: batchNum, + EthBlockNum: blockNum, + ForgerAddr: *sender, + CollectedFees: processTxsOut.CollectedFees, + FeeIdxsCoordinator: forgeBatchArgs.FeeIdxCoordinator, + StateRoot: forgeBatchArgs.NewStRoot, + NumAccounts: len(batchData.CreatedAccounts), + LastIdx: forgeBatchArgs.NewLastIdx, + ExitRoot: forgeBatchArgs.NewExitRoot, + SlotNum: slotNum, } nextForgeL1TxsNumCpy := nextForgeL1TxsNum if forgeBatchArgs.L1Batch { diff --git a/synchronizer/synchronizer_test.go b/synchronizer/synchronizer_test.go index 4fc195c..9743490 100644 --- a/synchronizer/synchronizer_test.go +++ b/synchronizer/synchronizer_test.go @@ -23,8 +23,6 @@ import ( ) var tokenConsts = map[common.TokenID]eth.ERC20Consts{} -var forceExits = map[int64][]common.ExitInfo{} // ForgeL1TxsNum -> []exit -var nonces = map[common.Idx]common.Nonce{} type timer struct { time int64 @@ -103,10 +101,8 @@ func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBloc dbL1CoordinatorTxs, err := s.historyDB.GetAllL1CoordinatorTxs() require.Nil(t, err) - // fmt.Printf("DBG dbL1CoordinatorTxs: %+v\n", dbL1CoordinatorTxs) dbL2Txs, err := s.historyDB.GetAllL2Txs() require.Nil(t, err) - // fmt.Printf("DBG dbL2Txs: %+v\n", dbL2Txs) dbExits, err := s.historyDB.GetAllExits() require.Nil(t, err) // dbL1CoordinatorTxs := []common.L1Tx{} @@ -125,7 +121,7 @@ func checkSyncBlock(t *testing.T, s *Synchronizer, blockNum int, block, syncBloc // We don't care about TotalFeesUSD. Use the syncBatch that // has a TotalFeesUSD inserted by the HistoryDB batch.Batch.TotalFeesUSD = syncBatch.Batch.TotalFeesUSD - batch.CreatedAccounts = syncBatch.CreatedAccounts // til doesn't output CreatedAccounts + assert.Equal(t, batch.CreatedAccounts, syncBatch.CreatedAccounts) batch.Batch.NumAccounts = len(batch.CreatedAccounts) // Test field by field to facilitate debugging of errors @@ -139,12 +135,6 @@ 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 @@ -305,9 +295,12 @@ func TestSync(t *testing.T) { > batchL1 // forge L1UserTxs{nil}, freeze defined L1UserTxs{2} > batchL1 // forge L1UserTxs{2}, freeze defined L1UserTxs{nil} > block // blockNum=3 - ` tc := til.NewContext(common.RollupConstMaxL1UserTx) + tilCfgExtra := til.ConfigExtra{ + BootCoordAddr: bootCoordAddr, + CoordUser: "A", + } blocks, err := tc.GenerateBlocks(set1) require.Nil(t, err) // Sanity check @@ -339,6 +332,9 @@ func TestSync(t *testing.T) { } } + err = tc.FillBlocksExtra(blocks, &tilCfgExtra) + assert.Nil(t, err) + // Add block data to the smart contracts for _, block := range blocks { for _, token := range block.Rollup.AddedTokens { @@ -352,12 +348,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)} - } + // 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.Rollup.Batches { _, err := client.RollupForgeBatch(ð.RollupForgeBatchArgs{ NewLastIdx: batch.Batch.LastIdx, @@ -366,7 +362,7 @@ func TestSync(t *testing.T) { L1CoordinatorTxs: batch.L1CoordinatorTxs, L1CoordinatorTxsAuths: [][]byte{}, // Intentionally empty L2TxsData: batch.L2Txs, - FeeIdxCoordinator: feeIdxCoordinator, + FeeIdxCoordinator: batch.Batch.FeeIdxsCoordinator, // Circuit selector VerifierIdx: 0, // Intentionally empty L1Batch: batch.L1Batch, @@ -380,113 +376,6 @@ func TestSync(t *testing.T) { client.CtlMineBlock() } - // Fill extra fields not generated by til in til block - openToForge := int64(0) - toForgeL1TxsNum := int64(0) - l1UserTxsLen := map[int64]int{} // ForgeL1TxsNum -> len(L1UserTxs) - for i := range blocks { - block := &blocks[i] - // Count number of L1UserTxs in each queue, to figure out later - // position of L1CoordinatorTxs and L2Txs - for j := range block.Rollup.L1UserTxs { - tx := &block.Rollup.L1UserTxs[j] - l1UserTxsLen[*tx.ToForgeL1TxsNum]++ - if tx.Type == common.TxTypeForceExit { - forceExits[*tx.ToForgeL1TxsNum] = append(forceExits[*tx.ToForgeL1TxsNum], - common.ExitInfo{ - AccountIdx: tx.FromIdx, - Balance: tx.Amount, - }) - } - } - for j := range block.Rollup.Batches { - batch := &block.Rollup.Batches[j] - if batch.L1Batch { - // Set BatchNum for forged L1UserTxs to til blocks - bn := batch.Batch.BatchNum - for k := range blocks { - block := &blocks[k] - for l := range block.Rollup.L1UserTxs { - tx := &block.Rollup.L1UserTxs[l] - if *tx.ToForgeL1TxsNum == openToForge { - tx.BatchNum = &bn - } - } - } - openToForge++ - } - - batch.Batch.EthBlockNum = block.Block.EthBlockNum - batch.Batch.ForgerAddr = bootCoordAddr // til doesn't fill the batch forger addr - if batch.L1Batch { - toForgeL1TxsNumCpy := toForgeL1TxsNum - batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy // til doesn't fill the ForgeL1TxsNum - toForgeL1TxsNum++ - } - - batchNum := batch.Batch.BatchNum - for k := range batch.L1CoordinatorTxs { - tx := &batch.L1CoordinatorTxs[k] - tx.BatchNum = &batchNum - tx.EthBlockNum = batch.Batch.EthBlockNum - } - } - } - - // Fill expected positions in L1CoordinatorTxs and L2Txs - for i := range blocks { - block := &blocks[i] - for j := range block.Rollup.Batches { - batch := &block.Rollup.Batches[j] - position := 0 - if batch.L1Batch { - position = l1UserTxsLen[*batch.Batch.ForgeL1TxsNum] - } - for k := range batch.L1CoordinatorTxs { - tx := &batch.L1CoordinatorTxs[k] - tx.Position = position - position++ - nTx, err := common.NewL1Tx(tx) - require.Nil(t, err) - *tx = *nTx - } - for k := range batch.L2Txs { - tx := &batch.L2Txs[k] - tx.Position = position - position++ - nonces[tx.FromIdx]++ - tx.Nonce = nonces[tx.FromIdx] - nTx, err := common.NewL2Tx(tx) - require.Nil(t, err) - *tx = *nTx - } - } - } - - // Fill ExitTree (only AccountIdx and Balance) - for i := range blocks { - block := &blocks[i] - for j := range block.Rollup.Batches { - batch := &block.Rollup.Batches[j] - if batch.L1Batch { - for forgeL1TxsNum, exits := range forceExits { - if forgeL1TxsNum == *batch.Batch.ForgeL1TxsNum { - batch.ExitTree = append(batch.ExitTree, exits...) - } - } - } - for k := range batch.L2Txs { - tx := &batch.L2Txs[k] - if tx.Type == common.TxTypeExit { - batch.ExitTree = append(batch.ExitTree, common.ExitInfo{ - AccountIdx: tx.FromIdx, - Balance: tx.Amount, - }) - } - } - } - } - // // Sync to synchronize the current state from the test smart contracts, // and check the outcome diff --git a/test/til/txs.go b/test/til/txs.go index b7c2ac0..4365c2c 100644 --- a/test/til/txs.go +++ b/test/til/txs.go @@ -21,7 +21,10 @@ func newBatchData(batchNum int) common.BatchData { L2Txs: []common.L2Tx{}, Batch: common.Batch{ BatchNum: common.BatchNum(batchNum), - StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0)}, + StateRoot: big.NewInt(0), ExitRoot: big.NewInt(0), + FeeIdxsCoordinator: make([]common.Idx, 0), + CollectedFees: make(map[common.TokenID]*big.Int), + }, } } @@ -36,13 +39,22 @@ func newBlock(blockNum int64) common.BlockData { } } +type contextExtra struct { + openToForge int64 + toForgeL1TxsNum int64 + nonces map[common.Idx]common.Nonce + idx int +} + // Context contains the data of the test type Context struct { Instructions []instruction userNames []string - Users map[string]*User + Users map[string]*User // Name -> *User + usersByIdx map[int]*User + accountsByIdx map[int]*Account LastRegisteredTokenID common.TokenID - l1CreatedAccounts map[string]*Account + l1CreatedAccounts map[string]*Account // (Name, TokenID) -> *Account // rollupConstMaxL1UserTx Maximum L1-user transactions allowed to be queued in a batch rollupConstMaxL1UserTx int @@ -59,6 +71,8 @@ type Context struct { l2Txs []L2Tx } blockNum int64 + + extra contextExtra } // NewContext returns a new Context @@ -67,6 +81,8 @@ func NewContext(rollupConstMaxL1UserTx int) *Context { return &Context{ Users: make(map[string]*User), l1CreatedAccounts: make(map[string]*Account), + usersByIdx: make(map[int]*User), + accountsByIdx: make(map[int]*Account), LastRegisteredTokenID: 0, rollupConstMaxL1UserTx: rollupConstMaxL1UserTx, @@ -82,17 +98,26 @@ func NewContext(rollupConstMaxL1UserTx int) *Context { openToForge: 1, //nolint:gomnd blockNum: 2, // rollup genesis blockNum + extra: contextExtra{ + openToForge: 0, + toForgeL1TxsNum: 0, + nonces: make(map[common.Idx]common.Nonce), + idx: common.UserThreshold, + }, } } // Account contains the data related to the account for a specific TokenID of a User type Account struct { - Idx common.Idx - Nonce common.Nonce + Idx common.Idx + TokenID common.TokenID + Nonce common.Nonce + BatchNum int } // User contains the data related to a testing user type User struct { + Name string BJJ *babyjub.PrivateKey Addr ethCommon.Address Accounts map[common.TokenID]*Account @@ -361,10 +386,14 @@ func (tc *Context) calculateIdxForL1Txs(isCoordinatorTxs bool, txs []L1Tx) error return fmt.Errorf("Can not create same account twice (same User (%s) & same TokenID (%d)) (this is a design property of Til)", tx.fromIdxName, tx.L1Tx.TokenID) } tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] = &Account{ - Idx: common.Idx(tc.idx), - Nonce: common.Nonce(0), + Idx: common.Idx(tc.idx), + TokenID: tx.L1Tx.TokenID, + Nonce: common.Nonce(0), + BatchNum: tc.currBatchNum, } tc.l1CreatedAccounts[idxTokenIDToString(tx.fromIdxName, tx.L1Tx.TokenID)] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] + tc.accountsByIdx[tc.idx] = tc.Users[tx.fromIdxName].Accounts[tx.L1Tx.TokenID] + tc.usersByIdx[tc.idx] = tc.Users[tx.fromIdxName] tc.idx++ } if isCoordinatorTxs { @@ -606,6 +635,7 @@ func (tc *Context) generateKeys(userNames []string) { addr := ethCrypto.PubkeyToAddress(key.PublicKey) u := User{ + Name: userNames[i-1], BJJ: &sk, Addr: addr, Accounts: make(map[common.TokenID]*Account), @@ -622,3 +652,205 @@ func L1TxsToCommonL1Txs(l1 []L1Tx) []common.L1Tx { } return r } + +// ConfigExtra is the configuration used in FillBlocksExtra to extend the +// blocks returned by til. +type ConfigExtra struct { + // Address to set as forger for each batch + BootCoordAddr ethCommon.Address + // Coordinator user name used to select the corresponding accounts to + // collect coordinator fees + CoordUser string +} + +// FillBlocksExtra fills extra fields not generated by til in each block, so +// that the blockData is closer to what the HistoryDB stores. The filled fields are: +// - blocks[].Rollup.L1UserTxs[].BatchNum +// - blocks[].Rollup.Batch.EthBlockNum +// - blocks[].Rollup.Batch.ForgerAddr +// - blocks[].Rollup.Batch.ForgeL1TxsNum +// - blocks[].Rollup.Batch.L1CoordinatorTxs[].TxID +// - blocks[].Rollup.Batch.L1CoordinatorTxs[].BatchNum +// - blocks[].Rollup.Batch.L1CoordinatorTxs[].EthBlockNum +// - blocks[].Rollup.Batch.L1CoordinatorTxs[].Position +// - blocks[].Rollup.Batch.L2Txs[].TxID +// - blocks[].Rollup.Batch.L2Txs[].Position +// - blocks[].Rollup.Batch.L2Txs[].Nonce +// - blocks[].Rollup.Batch.ExitTree +// - blocks[].Rollup.Batch.CreatedAccounts +// - blocks[].Rollup.Batch.FeeIdxCoordinator +// - blocks[].Rollup.Batch.CollectedFees +func (tc *Context) FillBlocksExtra(blocks []common.BlockData, cfg *ConfigExtra) error { + // Fill extra fields not generated by til in til block + for i := range blocks { + block := &blocks[i] + for j := range block.Rollup.Batches { + batch := &block.Rollup.Batches[j] + if batch.L1Batch { + // Set BatchNum for forged L1UserTxs to til blocks + bn := batch.Batch.BatchNum + for k := range blocks { + block := &blocks[k] + for l := range block.Rollup.L1UserTxs { + tx := &block.Rollup.L1UserTxs[l] + if *tx.ToForgeL1TxsNum == tc.extra.openToForge { + tx.BatchNum = &bn + } + } + } + tc.extra.openToForge++ + } + + batch.Batch.EthBlockNum = block.Block.EthBlockNum + // til doesn't fill the batch forger addr + batch.Batch.ForgerAddr = cfg.BootCoordAddr + if batch.L1Batch { + toForgeL1TxsNumCpy := tc.extra.toForgeL1TxsNum + // til doesn't fill the ForgeL1TxsNum + batch.Batch.ForgeL1TxsNum = &toForgeL1TxsNumCpy + tc.extra.toForgeL1TxsNum++ + } + + batchNum := batch.Batch.BatchNum + for k := range batch.L1CoordinatorTxs { + tx := &batch.L1CoordinatorTxs[k] + tx.BatchNum = &batchNum + tx.EthBlockNum = batch.Batch.EthBlockNum + } + } + } + + // Fill CreatedAccounts + for i := range blocks { + block := &blocks[i] + for j := range block.Rollup.Batches { + batch := &block.Rollup.Batches[j] + l1Txs := []common.L1Tx{} + if batch.L1Batch { + for _, tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] { + l1Txs = append(l1Txs, tx.L1Tx) + } + } + l1Txs = append(l1Txs, batch.L1CoordinatorTxs...) + for k := range l1Txs { + tx := &l1Txs[k] + if tx.Type == common.TxTypeCreateAccountDeposit || + tx.Type == common.TxTypeCreateAccountDepositTransfer { + user, ok := tc.usersByIdx[tc.extra.idx] + if !ok { + return fmt.Errorf("Created account with idx: %v not found", tc.extra.idx) + } + batch.CreatedAccounts = append(batch.CreatedAccounts, + common.Account{ + Idx: common.Idx(tc.extra.idx), + TokenID: tx.TokenID, + BatchNum: batch.Batch.BatchNum, + PublicKey: user.BJJ.Public(), + EthAddr: user.Addr, + Nonce: 0, + Balance: big.NewInt(0), + }) + tc.extra.idx++ + } + } + } + } + + // Fill expected positions in L1CoordinatorTxs and L2Txs + for i := range blocks { + block := &blocks[i] + for j := range block.Rollup.Batches { + batch := &block.Rollup.Batches[j] + position := 0 + if batch.L1Batch { + position = len(tc.Queues[*batch.Batch.ForgeL1TxsNum]) + } + for k := range batch.L1CoordinatorTxs { + tx := &batch.L1CoordinatorTxs[k] + tx.Position = position + position++ + nTx, err := common.NewL1Tx(tx) + if err != nil { + return err + } + *tx = *nTx + } + for k := range batch.L2Txs { + tx := &batch.L2Txs[k] + tx.Position = position + position++ + tc.extra.nonces[tx.FromIdx]++ + tx.Nonce = tc.extra.nonces[tx.FromIdx] + nTx, err := common.NewL2Tx(tx) + if err != nil { + return err + } + *tx = *nTx + } + } + } + + // Fill ExitTree (only AccountIdx and Balance) + for i := range blocks { + block := &blocks[i] + for j := range block.Rollup.Batches { + batch := &block.Rollup.Batches[j] + if batch.L1Batch { + for _, _tx := range tc.Queues[*batch.Batch.ForgeL1TxsNum] { + tx := _tx.L1Tx + if tx.Type == common.TxTypeForceExit { + batch.ExitTree = + append(batch.ExitTree, + common.ExitInfo{ + AccountIdx: tx.FromIdx, + Balance: tx.Amount, + }) + } + } + } + for k := range batch.L2Txs { + tx := &batch.L2Txs[k] + if tx.Type == common.TxTypeExit { + batch.ExitTree = append(batch.ExitTree, common.ExitInfo{ + AccountIdx: tx.FromIdx, + Balance: tx.Amount, + }) + } + fee, err := common.CalcFeeAmount(tx.Amount, tx.Fee) + if err != nil { + return err + } + + // Find the TokenID of the tx + fromAcc, ok := tc.accountsByIdx[int(tx.FromIdx)] + if !ok { + return fmt.Errorf("L2tx.FromIdx idx: %v not found", tx.FromIdx) + } + + // Find the idx of the CoordUser for the + // TokenID, and if it exists, add the fee to + // the collectedFees. Only consider the + // coordinator account to receive fee if it was + // created in this or a previous batch + if acc, ok := tc.l1CreatedAccounts[idxTokenIDToString(cfg.CoordUser, fromAcc.TokenID)]; ok && + common.BatchNum(acc.BatchNum) <= batch.Batch.BatchNum { + found := false + for _, idx := range batch.Batch.FeeIdxsCoordinator { + if idx == common.Idx(acc.Idx) { + found = true + break + } + } + if !found { + batch.Batch.FeeIdxsCoordinator = append(batch.Batch.FeeIdxsCoordinator, + common.Idx(acc.Idx)) + batch.Batch.CollectedFees[fromAcc.TokenID] = big.NewInt(0) + } + collected := batch.Batch.CollectedFees[fromAcc.TokenID] + collected.Add(collected, fee) + } + } + } + } + return nil +}