From fac8577badfae13593cab49cb6975ad9ed95bf26 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 6 Nov 2020 15:24:12 +0100 Subject: [PATCH 1/2] Update tx bytes parsers methods --- common/fee_test.go | 5 ++++- common/l1tx.go | 19 ++++++++++++++++--- common/l1tx_test.go | 1 + common/l2tx.go | 35 ++++++++++++++++++++--------------- 4 files changed, 41 insertions(+), 19 deletions(-) diff --git a/common/fee_test.go b/common/fee_test.go index 63219e8..0b6be7f 100644 --- a/common/fee_test.go +++ b/common/fee_test.go @@ -48,8 +48,11 @@ func TestCalcFeeAmount(t *testing.T) { } func TestFeePrintSQLSwitch(t *testing.T) { + debug := false for i := 0; i < 256; i++ { f := FeeSelector(i).Percentage() - fmt.Printf(" WHEN $1 = %03d THEN %.6e\n", i, f) + if debug { + fmt.Printf(" WHEN $1 = %03d THEN %.6e\n", i, f) + } } } diff --git a/common/l1tx.go b/common/l1tx.go index ceddb31..a98ecdf 100644 --- a/common/l1tx.go +++ b/common/l1tx.go @@ -146,8 +146,11 @@ func (tx L1Tx) Tx() Tx { return genericTx } -// BytesUser encodes a L1Tx into []byte -func (tx *L1Tx) BytesUser() ([]byte, error) { +// BytesGeneric returns the generic representation of a L1Tx. This method is +// used to compute the []byte representation of a L1UserTx, and also to compute +// the L1TxData for the ZKInputs (at the HashGlobalInputs), using this method +// for L1CoordinatorTxs & L1UserTxs (for the ZKInputs case). +func (tx *L1Tx) BytesGeneric() ([]byte, error) { var b [L1UserTxBytesLen]byte copy(b[0:20], tx.FromEthAddr.Bytes()) pkCompL := tx.FromBJJ.Compress() @@ -177,8 +180,19 @@ func (tx *L1Tx) BytesUser() ([]byte, error) { return b[:], nil } +// BytesUser encodes a L1UserTx into []byte +func (tx *L1Tx) BytesUser() ([]byte, error) { + if !tx.UserOrigin { + return nil, fmt.Errorf("Can not calculate BytesUser() for a L1CoordinatorTx") + } + return tx.BytesGeneric() +} + // BytesCoordinatorTx encodes a L1CoordinatorTx into []byte func (tx *L1Tx) BytesCoordinatorTx(compressedSignatureBytes []byte) ([]byte, error) { + if tx.UserOrigin { + return nil, fmt.Errorf("Can not calculate BytesCoordinatorTx() for a L1UserTx") + } var b [L1CoordinatorTxBytesLen]byte v := compressedSignatureBytes[64] s := compressedSignatureBytes[32:64] @@ -210,7 +224,6 @@ func L1UserTxFromBytes(b []byte) (*L1Tx, error) { var pkComp babyjub.PublicKeyComp copy(pkComp[:], pkCompL) tx.FromBJJ, err = pkComp.Decompress() - if err != nil { return nil, err } diff --git a/common/l1tx_test.go b/common/l1tx_test.go index c04b08a..9b952d0 100644 --- a/common/l1tx_test.go +++ b/common/l1tx_test.go @@ -109,6 +109,7 @@ func TestL1TxByteParsersCompatibility(t *testing.T) { FromIdx: Idx(29767899), FromBJJ: pk, FromEthAddr: ethCommon.HexToAddress("0x85dab5b9e2e361d0c208d77be90efcc0439b0a53"), + UserOrigin: true, } expected, err := utils.HexDecode("85dab5b9e2e361d0c208d77be90efcc0439b0a530dd02deb2c81068e7a0f7e327df80b4ab79ee1f41a7def613e73a20c32eece5a000001c638db8be880f00020039c0000053cb88d") diff --git a/common/l2tx.go b/common/l2tx.go index a2bb296..2ce279c 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -105,49 +105,54 @@ func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx { // Bytes encodes a L2Tx into []byte func (tx *L2Tx) Bytes(nLevels int) ([]byte, error) { - fromIdxNumBytes := nLevels / 8 //nolint:gomnd - toIdxNumBytes := nLevels / 8 //nolint:gomnd - var b []byte + idxLen := nLevels / 8 //nolint:gomnd + + b := make([]byte, ((nLevels*2)+16+8)/8) + fromIdxBytes, err := tx.FromIdx.Bytes() if err != nil { return nil, err } - b = append(b, fromIdxBytes[6-fromIdxNumBytes:]...) + copy(b[0:idxLen], fromIdxBytes[6-idxLen:]) // [6-idxLen:] as is BigEndian + toIdxBytes, err := tx.ToIdx.Bytes() if err != nil { return nil, err } - b = append(b, toIdxBytes[6-toIdxNumBytes:]...) + copy(b[idxLen:idxLen*2], toIdxBytes[6-idxLen:]) + amountFloat16, err := NewFloat16(tx.Amount) if err != nil { return nil, err } - b = append(b, amountFloat16.Bytes()...) - b = append(b, byte(tx.Fee)) + + copy(b[idxLen*2:idxLen*2+2], amountFloat16.Bytes()) + b[idxLen*2+2] = byte(tx.Fee) + return b[:], nil } // L2TxFromBytes decodes a L1Tx from []byte func L2TxFromBytes(b []byte, nLevels int) (*L2Tx, error) { - fromIdxNumByte := nLevels / 8 //nolint:gomnd - toIdxNumByte := fromIdxNumByte + nLevels/8 //nolint:gomnd - amountLenBytes := 2 - amountNumByte := toIdxNumByte + amountLenBytes + idxLen := nLevels / 8 //nolint:gomnd tx := &L2Tx{} var err error + var paddedFromIdxBytes [6]byte - copy(paddedFromIdxBytes[6-len(b[0:fromIdxNumByte]):], b[0:fromIdxNumByte]) + copy(paddedFromIdxBytes[6-idxLen:], b[0:idxLen]) tx.FromIdx, err = IdxFromBytes(paddedFromIdxBytes[:]) if err != nil { return nil, err } + var paddedToIdxBytes [6]byte - copy(paddedToIdxBytes[6-len(b[fromIdxNumByte:toIdxNumByte]):6], b[fromIdxNumByte:toIdxNumByte]) + copy(paddedToIdxBytes[6-idxLen:6], b[idxLen:idxLen*2]) tx.ToIdx, err = IdxFromBytes(paddedToIdxBytes[:]) if err != nil { return nil, err } - tx.Amount = Float16FromBytes(b[toIdxNumByte:amountNumByte]).BigInt() - tx.Fee = FeeSelector(b[amountNumByte]) + + tx.Amount = Float16FromBytes(b[idxLen*2 : idxLen*2+2]).BigInt() + tx.Fee = FeeSelector(b[idxLen*2+2]) return tx, nil } From 900494fd8e0ac52fd08a6b5e9ec9bab5893b26ef Mon Sep 17 00:00:00 2001 From: arnaucube Date: Fri, 6 Nov 2020 16:28:57 +0100 Subject: [PATCH 2/2] StateDB group Fee txs and ZKInputs gen of Fee txs --- common/l2tx.go | 2 +- common/zk.go | 89 ++++++++++++++----------- db/statedb/statedb.go | 6 +- db/statedb/txprocessors.go | 132 ++++++++++++++++++++++++++----------- 4 files changed, 150 insertions(+), 79 deletions(-) diff --git a/common/l2tx.go b/common/l2tx.go index 2ce279c..d2a8f31 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -107,7 +107,7 @@ func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx { func (tx *L2Tx) Bytes(nLevels int) ([]byte, error) { idxLen := nLevels / 8 //nolint:gomnd - b := make([]byte, ((nLevels*2)+16+8)/8) + b := make([]byte, ((nLevels*2)+16+8)/8) //nolint:gomnd fromIdxBytes, err := tx.FromIdx.Bytes() if err != nil { diff --git a/common/zk.go b/common/zk.go index a3e965f..17c6afb 100644 --- a/common/zk.go +++ b/common/zk.go @@ -1,6 +1,5 @@ // Package common contains all the common data structures used at the // hermez-node, zk.go contains the zkSnark inputs used to generate the proof -//nolint:deadcode,structcheck,unused package common import ( @@ -11,21 +10,30 @@ import ( "github.com/mitchellh/mapstructure" ) -// circuit parameters -// absolute maximum of L1 or L2 transactions allowed -type nTx uint32 - -// merkle tree depth -type nLevels uint32 - -// absolute maximum of L1 transaction allowed -type maxL1Tx uint32 - -//absolute maximum of fee transactions allowed -type maxFeeTx uint32 +// ZKMetadata contains ZKInputs metadata that is not used directly in the +// ZKInputs result, but to calculate values for Hash check +type ZKMetadata struct { + // Circuit parameters + // absolute maximum of L1 or L2 transactions allowed + NTx uint32 + // merkle tree depth + NLevels uint32 + MaxLevels uint32 + // absolute maximum of L1 transaction allowed + MaxL1Tx uint32 + // Maximum number of Idxs where Fees can be send in a batch (currently + // is constant for all circuits: 64) + MaxFeeIdxs uint32 + + L1TxsData [][]byte + L2TxsData [][]byte + ChainID uint16 +} // ZKInputs represents the inputs that will be used to generate the zkSNARK proof type ZKInputs struct { + Metadata ZKMetadata + // // General // @@ -38,11 +46,11 @@ type ZKInputs struct { // GlobalChainID is the blockchain ID (0 for Ethereum mainnet). This value can be get from the smart contract. GlobalChainID *big.Int `json:"globalChainID"` // uint16 // FeeIdxs is an array of merkle tree indexes where the coordinator will receive the accumulated fees - FeeIdxs []*big.Int `json:"feeIdxs"` // uint64 (max nLevels bits), len: [maxFeeTx] + FeeIdxs []*big.Int `json:"feeIdxs"` // uint64 (max nLevels bits), len: [maxFeeIdxs] // accumulate fees // FeePlanTokens contains all the tokenIDs for which the fees are being accumulated - FeePlanTokens []*big.Int `json:"feePlanTokens"` // uint32 (max 32 bits), len: [maxFeeTx] + FeePlanTokens []*big.Int `json:"feePlanTokens"` // uint32 (max 32 bits), len: [maxFeeIdxs] // // Txs (L1&L2) @@ -142,13 +150,13 @@ type ZKInputs struct { // state 3, value of the account leaf receiver of the Fees // fee tx // State fees - TokenID3 []*big.Int `json:"tokenID3"` // uint32, len: [maxFeeTx] - Nonce3 []*big.Int `json:"nonce3"` // uint64 (max 40 bits), len: [maxFeeTx] - Sign3 []*big.Int `json:"sign3"` // bool, len: [maxFeeTx] - Ay3 []*big.Int `json:"ay3"` // big.Int, len: [maxFeeTx] - Balance3 []*big.Int `json:"balance3"` // big.Int (max 192 bits), len: [maxFeeTx] - EthAddr3 []*big.Int `json:"ethAddr3"` // ethCommon.Address, len: [maxFeeTx] - Siblings3 [][]*big.Int `json:"siblings3"` // Hash, len: [maxFeeTx][nLevels + 1] + TokenID3 []*big.Int `json:"tokenID3"` // uint32, len: [maxFeeIdxs] + Nonce3 []*big.Int `json:"nonce3"` // uint64 (max 40 bits), len: [maxFeeIdxs] + Sign3 []*big.Int `json:"sign3"` // bool, len: [maxFeeIdxs] + Ay3 []*big.Int `json:"ay3"` // big.Int, len: [maxFeeIdxs] + Balance3 []*big.Int `json:"balance3"` // big.Int (max 192 bits), len: [maxFeeIdxs] + EthAddr3 []*big.Int `json:"ethAddr3"` // ethCommon.Address, len: [maxFeeIdxs] + Siblings3 [][]*big.Int `json:"siblings3"` // Hash, len: [maxFeeIdxs][nLevels + 1] // // Intermediate States @@ -174,14 +182,14 @@ type ZKInputs struct { // ISExitTree root at the moment of the Tx the value once the Tx is processed into the exit tree ISExitRoot []*big.Int `json:"imExitRoot"` // Hash, len: [nTx - 1] // ISAccFeeOut accumulated fees once the Tx is processed - ISAccFeeOut [][]*big.Int `json:"imAccFeeOut"` // big.Int, len: [nTx - 1][maxFeeTx] + ISAccFeeOut [][]*big.Int `json:"imAccFeeOut"` // big.Int, len: [nTx - 1][maxFeeIdxs] // fee-tx // ISStateRootFee root at the moment of the Tx, the state root value once the Tx is processed into the state tree - ISStateRootFee []*big.Int `json:"imStateRootFee"` // Hash, len: [maxFeeTx - 1] + ISStateRootFee []*big.Int `json:"imStateRootFee"` // Hash, len: [maxFeeIdxs - 1] // ISInitStateRootFee state root once all L1-L2 tx are processed (before computing the fees-tx) ISInitStateRootFee *big.Int `json:"imInitStateRootFee"` // Hash // ISFinalAccFee final accumulated fees (before computing the fees-tx) - ISFinalAccFee []*big.Int `json:"imFinalAccFee"` // big.Int, len: [maxFeeTx - 1] + ISFinalAccFee []*big.Int `json:"imFinalAccFee"` // big.Int, len: [maxFeeIdxs - 1] } func bigIntsToStrings(v interface{}) interface{} { @@ -212,6 +220,8 @@ func bigIntsToStrings(v interface{}) interface{} { r[i] = bigIntsToStrings(c[i]) } return r + case map[string]interface{}: + // avoid printing a warning when there is a struct type default: log.Warnf("bigIntsToStrings unexpected type: %T\n", v) } @@ -240,15 +250,18 @@ func (z ZKInputs) MarshalJSON() ([]byte, error) { } // NewZKInputs returns a pointer to an initialized struct of ZKInputs -func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { +func NewZKInputs(nTx, maxFeeIdxs, nLevels int) *ZKInputs { zki := &ZKInputs{} + zki.Metadata.NTx = uint32(nTx) + zki.Metadata.MaxFeeIdxs = uint32(maxFeeIdxs) + zki.Metadata.NLevels = uint32(nLevels) // General zki.OldLastIdx = big.NewInt(0) zki.OldStateRoot = big.NewInt(0) zki.GlobalChainID = big.NewInt(0) - zki.FeeIdxs = newSlice(maxFeeTx) - zki.FeePlanTokens = newSlice(maxFeeTx) + zki.FeeIdxs = newSlice(maxFeeIdxs) + zki.FeePlanTokens = newSlice(maxFeeIdxs) // Txs zki.TxCompressedData = newSlice(nTx) @@ -312,13 +325,13 @@ func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { zki.OldKey2 = newSlice(nTx) zki.OldValue2 = newSlice(nTx) - zki.TokenID3 = newSlice(maxFeeTx) - zki.Nonce3 = newSlice(maxFeeTx) - zki.Sign3 = newSlice(maxFeeTx) - zki.Ay3 = newSlice(maxFeeTx) - zki.Balance3 = newSlice(maxFeeTx) - zki.EthAddr3 = newSlice(maxFeeTx) - zki.Siblings3 = make([][]*big.Int, maxFeeTx) + zki.TokenID3 = newSlice(maxFeeIdxs) + zki.Nonce3 = newSlice(maxFeeIdxs) + zki.Sign3 = newSlice(maxFeeIdxs) + zki.Ay3 = newSlice(maxFeeIdxs) + zki.Balance3 = newSlice(maxFeeIdxs) + zki.EthAddr3 = newSlice(maxFeeIdxs) + zki.Siblings3 = make([][]*big.Int, maxFeeIdxs) for i := 0; i < len(zki.Siblings3); i++ { zki.Siblings3[i] = newSlice(nLevels + 1) } @@ -330,11 +343,11 @@ func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { zki.ISExitRoot = newSlice(nTx - 1) zki.ISAccFeeOut = make([][]*big.Int, nTx-1) for i := 0; i < len(zki.ISAccFeeOut); i++ { - zki.ISAccFeeOut[i] = newSlice(maxFeeTx) + zki.ISAccFeeOut[i] = newSlice(maxFeeIdxs) } - zki.ISStateRootFee = newSlice(maxFeeTx - 1) + zki.ISStateRootFee = newSlice(maxFeeIdxs - 1) zki.ISInitStateRootFee = big.NewInt(0) - zki.ISFinalAccFee = newSlice(maxFeeTx - 1) + zki.ISFinalAccFee = newSlice(maxFeeIdxs - 1) return zki } diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go index f4931db..c3c0096 100644 --- a/db/statedb/statedb.go +++ b/db/statedb/statedb.go @@ -78,7 +78,11 @@ type StateDB struct { // idx holds the current Idx that the BatchBuilder is using idx common.Idx zki *common.ZKInputs - i int // i is the current transaction index in the ZKInputs generation (zki) + // i is the current transaction index in the ZKInputs generation (zki) + i int + // accumulatedFees contains the accumulated fees for each token (Coord + // Idx) in the processed batch + accumulatedFees map[common.Idx]*big.Int } // NewStateDB creates a new StateDB, allowing to use an in-memory or in-disk diff --git a/db/statedb/txprocessors.go b/db/statedb/txprocessors.go index 532de30..fbaf1c5 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -64,6 +64,8 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } defer s.resetZKInputs() + s.accumulatedFees = make(map[common.Idx]*big.Int) + nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs) if nTx == 0 { // TODO return ZKInputs of batch without txs @@ -77,22 +79,8 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } exits := make([]processedExit, nTx) - // get TokenIDs of coordIdxs - coordIdxsMap, err := s.getTokenIDsFromIdxs(coordIdxs) - if err != nil { - 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 { - maxFeeTx := 2 // TODO this value will be a parameter + maxFeeTx := 64 // TODO this value will be a parameter s.zki = common.NewZKInputs(nTx, maxFeeTx, s.mt.MaxLevels()) s.zki.OldLastIdx = (s.idx - 1).BigInt() s.zki.OldStateRoot = s.mt.Root().BigInt() @@ -120,8 +108,9 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } } - // assumption: l1usertx are sorted by L1Tx.Position + // Process L1UserTxs for i := 0; i < len(l1usertxs); i++ { + // assumption: l1usertx are sorted by L1Tx.Position exitIdx, exitAccount, newExit, createdAccount, err := s.processL1Tx(exitTree, &l1usertxs[i]) if err != nil { return nil, err @@ -140,7 +129,17 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs if s.typ == TypeSynchronizer && createdAccount != nil { createdAccounts = append(createdAccounts, *createdAccount) } + + if s.zki != nil { + l1TxData, err := l1usertxs[i].BytesGeneric() + if err != nil { + return nil, err + } + s.zki.Metadata.L1TxsData = append(s.zki.Metadata.L1TxsData, l1TxData) + } } + + // Process L1CoordinatorTxs for i := 0; i < len(l1coordinatortxs); i++ { exitIdx, _, _, createdAccount, err := s.processL1Tx(exitTree, &l1coordinatortxs[i]) if err != nil { @@ -152,7 +151,36 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs if s.typ == TypeSynchronizer && createdAccount != nil { createdAccounts = append(createdAccounts, *createdAccount) } + if s.zki != nil { + l1TxData, err := l1coordinatortxs[i].BytesGeneric() + if err != nil { + return nil, err + } + s.zki.Metadata.L1TxsData = append(s.zki.Metadata.L1TxsData, l1TxData) + } + } + + s.accumulatedFees = make(map[common.Idx]*big.Int) + for _, idx := range coordIdxs { + s.accumulatedFees[idx] = big.NewInt(0) + } + + // once L1UserTxs & L1CoordinatorTxs are processed, get TokenIDs of + // coordIdxs. In this way, if a coordIdx uses an Idx that is being + // created in the current batch, at this point the Idx will be created + coordIdxsMap, err := s.getTokenIDsFromIdxs(coordIdxs) + if err != nil { + 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) + } + } + + // Process L2Txs for i := 0; i < len(l2txs); i++ { exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, collectedFees, exitTree, &l2txs[i]) if err != nil { @@ -171,6 +199,39 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs } } + // distribute the AccumulatedFees from the processed L2Txs into the + // Coordinator Idxs + iFee := 0 + for idx, accumulatedFee := range s.accumulatedFees { + // send the fee to the Idx of the Coordinator for the TokenID + accCoord, err := s.GetAccount(idx) + if err != nil { + log.Errorw("Can not distribute accumulated fees to coordinator account: No coord Idx to receive fee", "idx", idx) + return nil, err + } + accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee) + pFee, err := s.UpdateAccount(idx, accCoord) + if err != nil { + log.Error(err) + return nil, err + } + if s.zki != nil { + s.zki.TokenID3[iFee] = accCoord.TokenID.BigInt() + s.zki.Nonce3[iFee] = accCoord.Nonce.BigInt() + if babyjub.PointCoordSign(accCoord.PublicKey.X) { + s.zki.Sign3[iFee] = big.NewInt(1) + } + s.zki.Ay3[iFee] = accCoord.PublicKey.Y + s.zki.Balance3[iFee] = accCoord.Balance + s.zki.EthAddr3[iFee] = common.EthAddrToBigInt(accCoord.EthAddr) + s.zki.Siblings3[iFee] = siblingsToZKInputFormat(pFee.Siblings) + + // add Coord Idx to ZKInputs.FeeTxsData + s.zki.FeeIdxs[iFee] = idx.BigInt() + } + iFee++ + } + if s.typ == TypeTxSelector { return nil, nil } @@ -178,7 +239,6 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs // once all txs processed (exitTree root frozen), for each Exit, // generate common.ExitInfo data var exitInfos []common.ExitInfo - // exitInfos := []common.ExitInfo{} for i := 0; i < nTx; i++ { if !exits[i].exit { continue @@ -245,9 +305,6 @@ func (s *StateDB) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinatortxs // s.zki.ISInitStateRootFee = s.mt.Root().BigInt() - // TODO once the Node Config sets the Accounts where to send the Fees - // compute fees & update ZKInputs - // return ZKInputs as the BatchBuilder will return it to forge the Batch return &ProcessTxOutput{ ZKInputs: s.zki, @@ -606,10 +663,10 @@ func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, coll return err } - // increment nonce - accSender.Nonce++ - if !tx.IsL1 { + // increment nonce + accSender.Nonce++ + // compute fee and subtract it from the accSender fee, err := common.CalcFeeAmount(tx.Amount, *tx.Fee) if err != nil { @@ -618,19 +675,16 @@ func (s *StateDB) applyTransfer(coordIdxsMap map[common.TokenID]common.Idx, coll feeAndAmount := new(big.Int).Add(tx.Amount, fee) accSender.Balance = new(big.Int).Sub(accSender.Balance, feeAndAmount) - // send the fee to the Idx of the Coordinator for the TokenID accCoord, err := s.GetAccount(coordIdxsMap[accSender.TokenID]) if err != nil { log.Debugw("No coord Idx to receive fee", "tx", tx) } else { - accCoord.Balance = new(big.Int).Add(accCoord.Balance, fee) - _, err = s.UpdateAccount(coordIdxsMap[accSender.TokenID], accCoord) - if err != nil { - log.Error(err) - return err - } + // accumulate the fee for the Coord account + accumulated := s.accumulatedFees[accCoord.Idx] + accumulated.Add(accumulated, fee) + if s.typ == TypeSynchronizer { - collected := collectedFees[accSender.TokenID] + collected := collectedFees[accCoord.TokenID] collected.Add(collected, fee) } } @@ -753,6 +807,9 @@ func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, collecte } if !tx.IsL1 { + // increment nonce + acc.Nonce++ + // compute fee and subtract it from the accSender fee, err := common.CalcFeeAmount(tx.Amount, *tx.Fee) if err != nil { @@ -761,19 +818,16 @@ func (s *StateDB) applyExit(coordIdxsMap map[common.TokenID]common.Idx, collecte feeAndAmount := new(big.Int).Add(tx.Amount, fee) acc.Balance = new(big.Int).Sub(acc.Balance, feeAndAmount) - // send the fee to the Idx of the Coordinator for the TokenID accCoord, err := s.GetAccount(coordIdxsMap[acc.TokenID]) if err != nil { log.Debugw("No coord Idx to receive fee", "tx", tx) } else { - accCoord.Balance = new(big.Int).Add(accCoord.Balance, fee) - _, err = s.UpdateAccount(coordIdxsMap[acc.TokenID], accCoord) - if err != nil { - log.Error(err) - return nil, false, err - } + // accumulate the fee for the Coord account + accumulated := s.accumulatedFees[accCoord.Idx] + accumulated.Add(accumulated, fee) + if s.typ == TypeSynchronizer { - collected := collectedFees[acc.TokenID] + collected := collectedFees[accCoord.TokenID] collected.Add(collected, fee) } }