From e55ce04bbdbe5f63d7ada60c894ce95a8ee59dcc Mon Sep 17 00:00:00 2001 From: arnaucube Date: Thu, 4 Feb 2021 13:24:33 +0100 Subject: [PATCH] Update ZKInputs to incl Accumulated Fees of val 0 Fixes the case where there is a PoolTxs of type Exit with fee 0 for a TokenID. Before this commit the Coordinator was not sending the accumulated fee (which has value 0) to the Coordinator account for that TokenID, with this commit the Coordinator always sends the accumulated fee even when the value is 0 to match the hermez circom circuits behaviour. Also added a test to check the values and that also sends the proof to a real proof server to check that can generate a valid proof. --- test/zkproof/flows_test.go | 87 +++++++++++++++++++++++++++++++++++++- txprocessor/txprocessor.go | 55 ++++++++++++------------ 2 files changed, 113 insertions(+), 29 deletions(-) diff --git a/test/zkproof/flows_test.go b/test/zkproof/flows_test.go index 5348cce..255407a 100644 --- a/test/zkproof/flows_test.go +++ b/test/zkproof/flows_test.go @@ -111,7 +111,7 @@ func initTxSelector(t *testing.T, chainID uint16, hermezContractAddr ethCommon.A return txsel, l2DB, syncStateDB } -func TestTxSelectorBatchBuilderZKInputs(t *testing.T) { +func TestTxSelectorBatchBuilderZKInputsMinimumFlow0(t *testing.T) { tc := til.NewContext(ChainID, common.RollupConstMaxL1UserTx) // generate test transactions, the L1CoordinatorTxs generated by Til // will be ignored at this test, as will be the TxSelector who @@ -253,3 +253,88 @@ func TestTxSelectorBatchBuilderZKInputs(t *testing.T) { err = l2DBTxSel.UpdateTxsInfo(discardedL2Txs) require.NoError(t, err) } + +// TestZKInputsExitWithFee0 checks the case where there is a PoolTxs of type +// Exit with fee 0 for a TokenID that the Coordinator does not have it +// registered yet +func TestZKInputsExitWithFee0(t *testing.T) { + tc := til.NewContext(ChainID, common.RollupConstMaxL1UserTx) + + var set = ` + Type: Blockchain + AddToken(1) + + CreateAccountDeposit(1) A: 1000 + CreateAccountDeposit(1) B: 1000 + CreateAccountDeposit(1) C: 1000 + > batchL1 + > batchL1 + + CreateAccountCoordinator(1) Coord + > batch + > block + ` + blocks, err := tc.GenerateBlocks(set) + require.NoError(t, err) + + hermezContractAddr := ethCommon.HexToAddress("0xc344E203a046Da13b0B4467EB7B3629D0C99F6E6") + txsel, l2DBTxSel, syncStateDB := initTxSelector(t, ChainID, hermezContractAddr, tc.Users["Coord"]) + + bbDir, err := ioutil.TempDir("", "tmpBatchBuilderDB") + require.NoError(t, err) + bb, err := batchbuilder.NewBatchBuilder(bbDir, syncStateDB, 0, NLevels) + require.NoError(t, err) + + // restart nonces of TilContext, as will be set by generating directly + // the PoolL2Txs for each specific batch with tc.GeneratePoolL2Txs + tc.RestartNonces() + // add tokens to HistoryDB to avoid breaking FK constrains + addTokens(t, tc, l2DBTxSel.DB()) + + selectionConfig := &txselector.SelectionConfig{ + MaxL1UserTxs: 100, + TxProcessorConfig: txprocConfig, + } + configBatch := &batchbuilder.ConfigBatch{ + TxProcessorConfig: txprocConfig, + } + + // batch2 + // TxSelector select the transactions for the next Batch + l1UserTxs := til.L1TxsToCommonL1Txs(tc.Queues[*blocks[0].Rollup.Batches[1].Batch.ForgeL1TxsNum]) + coordIdxs, _, oL1UserTxs, oL1CoordTxs, oL2Txs, _, err := txsel.GetL1L2TxSelection(selectionConfig, l1UserTxs) + require.NoError(t, err) + // BatchBuilder build Batch + zki, err := bb.BuildBatch(coordIdxs, configBatch, oL1UserTxs, oL1CoordTxs, oL2Txs) + require.NoError(t, err) + assert.Equal(t, "8737171572459172806192626402462788826264011087579491137542380589998149683116", bb.LocalStateDB().MT.Root().BigInt().String()) + h, err := zki.HashGlobalData() + require.NoError(t, err) + assert.Equal(t, "9971598169768987067017223790214537222850903267980994716992761290793474746117", h.String()) + sendProofAndCheckResp(t, zki) + + // batch3 + batchPoolL2 := ` + Type: PoolL2 + PoolExit(1) A: 100 (0)` + l2Txs, err := tc.GeneratePoolL2Txs(batchPoolL2) + require.NoError(t, err) + addL2Txs(t, l2DBTxSel, l2Txs) // Add L2s to TxSelector.L2DB + coordIdxs, _, oL1UserTxs, oL1CoordTxs, oL2Txs, discardedL2Txs, err := txsel.GetL1L2TxSelection(selectionConfig, nil) + require.NoError(t, err) + assert.Equal(t, 1, len(coordIdxs)) + assert.Equal(t, 0, len(oL1UserTxs)) + assert.Equal(t, 1, len(oL1CoordTxs)) + assert.Equal(t, 1, len(oL2Txs)) + assert.Equal(t, 0, len(discardedL2Txs)) + // BatchBuilder build Batch + zki, err = bb.BuildBatch(coordIdxs, configBatch, oL1UserTxs, oL1CoordTxs, oL2Txs) + require.NoError(t, err) + assert.Equal(t, "18306761925365215381387147754881756804475668085493847010988306480531520370130", bb.LocalStateDB().MT.Root().BigInt().String()) + h, err = zki.HashGlobalData() + require.NoError(t, err) + assert.Equal(t, "7992262236065691439683036344554725221924027193771770363772735722054938818364", h.String()) + assert.Equal(t, common.EthAddrToBigInt(tc.Users["Coord"].Addr), zki.EthAddr3[0]) + assert.Equal(t, "0", zki.EthAddr3[1].String()) + sendProofAndCheckResp(t, zki) +} diff --git a/txprocessor/txprocessor.go b/txprocessor/txprocessor.go index 51337dc..beaa474 100644 --- a/txprocessor/txprocessor.go +++ b/txprocessor/txprocessor.go @@ -360,35 +360,34 @@ func (tp *TxProcessor) ProcessTxs(coordIdxs []common.Idx, l1usertxs, l1coordinat for _, idx := range coordIdxs { accumulatedFee := tp.AccumulatedFees[idx] - cmp := accumulatedFee.Cmp(big.NewInt(0)) - if cmp == 1 { // accumulatedFee>0 - // send the fee to the Idx of the Coordinator for the TokenID - accCoord, err := tp.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, tracerr.Wrap(err) - } - if tp.zki != nil { - tp.zki.TokenID3[iFee] = accCoord.TokenID.BigInt() - tp.zki.Nonce3[iFee] = accCoord.Nonce.BigInt() - coordBJJSign, coordBJJY := babyjub.UnpackSignY(accCoord.BJJ) - if coordBJJSign { - tp.zki.Sign3[iFee] = big.NewInt(1) - } - tp.zki.Ay3[iFee] = coordBJJY - tp.zki.Balance3[iFee] = accCoord.Balance - tp.zki.EthAddr3[iFee] = common.EthAddrToBigInt(accCoord.EthAddr) - } - accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee) - pFee, err := tp.s.UpdateAccount(idx, accCoord) - if err != nil { - log.Error(err) - return nil, tracerr.Wrap(err) - } - if tp.zki != nil { - tp.zki.Siblings3[iFee] = siblingsToZKInputFormat(pFee.Siblings) - tp.zki.ISStateRootFee[iFee] = tp.s.MT.Root().BigInt() + // send the fee to the Idx of the Coordinator for the TokenID + // (even if the AccumulatedFee==0, as is how the zk circuit + // works) + accCoord, err := tp.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, tracerr.Wrap(err) + } + if tp.zki != nil { + tp.zki.TokenID3[iFee] = accCoord.TokenID.BigInt() + tp.zki.Nonce3[iFee] = accCoord.Nonce.BigInt() + coordBJJSign, coordBJJY := babyjub.UnpackSignY(accCoord.BJJ) + if coordBJJSign { + tp.zki.Sign3[iFee] = big.NewInt(1) } + tp.zki.Ay3[iFee] = coordBJJY + tp.zki.Balance3[iFee] = accCoord.Balance + tp.zki.EthAddr3[iFee] = common.EthAddrToBigInt(accCoord.EthAddr) + } + accCoord.Balance = new(big.Int).Add(accCoord.Balance, accumulatedFee) + pFee, err := tp.s.UpdateAccount(idx, accCoord) + if err != nil { + log.Error(err) + return nil, tracerr.Wrap(err) + } + if tp.zki != nil { + tp.zki.Siblings3[iFee] = siblingsToZKInputFormat(pFee.Siblings) + tp.zki.ISStateRootFee[iFee] = tp.s.MT.Root().BigInt() } iFee++ }