From 504e36ac471d5be14f4d7f28a441de8c152be2ac Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 24 Feb 2021 14:36:07 +0100 Subject: [PATCH] Update ZKI gen to match circuit behaviour for Exit Currently the circuit does not use an Exit Leaf at the Exit MerkleTree when the Amount=0 & EffectiveAmount=0, but it does use an Exit Leaf when the Amount>0 but EffectiveAmount=0 (for example when tx.Amount > sender.Balance). This is a particularity of the approach of the circuit, the idea will be in the future to update the circuit and when Amount>0 but EffectiveAmount=0, to not add the Exit in the Exits MerkleTree, but for the moment the Go code is adapted to the circuit and when an Exit with Amount>0 & EffectiveAmount=0, it will be added to the Leaf of the Exit MerkleTree. --- txprocessor/txprocessor.go | 22 +++++++-- txprocessor/txprocessor_test.go | 32 ++++++------- txprocessor/zkinputsgen_test.go | 80 +++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 20 deletions(-) diff --git a/txprocessor/txprocessor.go b/txprocessor/txprocessor.go index d1dc417..7a457f2 100644 --- a/txprocessor/txprocessor.go +++ b/txprocessor/txprocessor.go @@ -605,7 +605,7 @@ func (tp *TxProcessor) ProcessL1Tx(exitTree *merkletree.MerkleTree, tx *common.L // execute exit flow // coordIdxsMap is 'nil', as at L1Txs there is no L2 fees - exitAccount, newExit, err := tp.applyExit(nil, nil, exitTree, tx.Tx()) + exitAccount, newExit, err := tp.applyExit(nil, nil, exitTree, tx.Tx(), tx.Amount) if err != nil { log.Error(err) return nil, nil, false, nil, tracerr.Wrap(err) @@ -730,7 +730,7 @@ func (tp *TxProcessor) ProcessL2Tx(coordIdxsMap map[common.TokenID]common.Idx, } case common.TxTypeExit: // execute exit flow - exitAccount, newExit, err := tp.applyExit(coordIdxsMap, collectedFees, exitTree, tx.Tx()) + exitAccount, newExit, err := tp.applyExit(coordIdxsMap, collectedFees, exitTree, tx.Tx(), tx.Amount) if err != nil { log.Error(err) return nil, nil, false, tracerr.Wrap(err) @@ -1104,7 +1104,7 @@ func (tp *TxProcessor) applyCreateAccountDepositTransfer(tx *common.L1Tx) error // new Leaf in the ExitTree. func (tp *TxProcessor) applyExit(coordIdxsMap map[common.TokenID]common.Idx, collectedFees map[common.TokenID]*big.Int, exitTree *merkletree.MerkleTree, - tx common.Tx) (*common.Account, bool, error) { + tx common.Tx, originalAmount *big.Int) (*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 := tp.s.GetAccount(tx.FromIdx) @@ -1174,7 +1174,17 @@ func (tp *TxProcessor) applyExit(coordIdxsMap map[common.TokenID]common.Idx, if exitTree == nil { return nil, false, nil } - if tx.Amount.Cmp(big.NewInt(0)) == 0 { // Amount == 0 + + // Do not add the Exit when Amount=0, not EffectiveAmount=0. In + // txprocessor.applyExit function, the tx.Amount is in reality the + // EffectiveAmount, that's why is used here the originalAmount + // parameter, which contains the real value of the tx.Amount (not + // tx.EffectiveAmount). This is a particularity of the approach of the + // circuit, the idea will be in the future to update the circuit and + // when Amount>0 but EffectiveAmount=0, to not add the Exit in the + // Exits MerkleTree, but for the moment the Go code is adapted to the + // circuit. + if originalAmount.Cmp(big.NewInt(0)) == 0 { // Amount == 0 // if the Exit Amount==0, the Exit is not added to the ExitTree return nil, false, nil } @@ -1187,6 +1197,8 @@ func (tp *TxProcessor) applyExit(coordIdxsMap map[common.TokenID]common.Idx, exitAccount := &common.Account{ TokenID: acc.TokenID, Nonce: common.Nonce(0), + // as is a common.Tx, the Amount is already an + // EffectiveAmount Balance: tx.Amount, BJJ: acc.BJJ, EthAddr: acc.EthAddr, @@ -1200,6 +1212,8 @@ func (tp *TxProcessor) applyExit(coordIdxsMap map[common.TokenID]common.Idx, tp.zki.Sign2[tp.i] = big.NewInt(1) } tp.zki.Ay2[tp.i] = accBJJY + // as is a common.Tx, the Amount is already an + // EffectiveAmount tp.zki.Balance2[tp.i] = tx.Amount tp.zki.EthAddr2[tp.i] = common.EthAddrToBigInt(acc.EthAddr) // as Leaf didn't exist in the ExitTree, set NewExit[i]=1 diff --git a/txprocessor/txprocessor_test.go b/txprocessor/txprocessor_test.go index f8825cc..a1e2cc0 100644 --- a/txprocessor/txprocessor_test.go +++ b/txprocessor/txprocessor_test.go @@ -1018,22 +1018,22 @@ func TestUpdatedAccounts(t *testing.T) { assert.NoError(t, err) set := ` -Type: Blockchain -AddToken(1) -CreateAccountCoordinator(0) Coord // 256 -CreateAccountCoordinator(1) Coord // 257 -> batch // 1 -CreateAccountDeposit(0) A: 50 // 258 -CreateAccountDeposit(0) B: 60 // 259 -CreateAccountDeposit(1) A: 70 // 260 -CreateAccountDeposit(1) B: 80 // 261 -> batchL1 // 2 -> batchL1 // 3 -Transfer(0) A-B: 5 (126) -> batch // 4 -Exit(1) B: 5 (126) -> batch // 5 -> block + Type: Blockchain + AddToken(1) + CreateAccountCoordinator(0) Coord // 256 + CreateAccountCoordinator(1) Coord // 257 + > batch // 1 + CreateAccountDeposit(0) A: 50 // 258 + CreateAccountDeposit(0) B: 60 // 259 + CreateAccountDeposit(1) A: 70 // 260 + CreateAccountDeposit(1) B: 80 // 261 + > batchL1 // 2 + > batchL1 // 3 + Transfer(0) A-B: 5 (126) + > batch // 4 + Exit(1) B: 5 (126) + > batch // 5 + > block ` chainID := uint16(0) diff --git a/txprocessor/zkinputsgen_test.go b/txprocessor/zkinputsgen_test.go index 85402a1..830cb62 100644 --- a/txprocessor/zkinputsgen_test.go +++ b/txprocessor/zkinputsgen_test.go @@ -895,3 +895,83 @@ func TestZKInputs6(t *testing.T) { // printZKInputs(t, ptOut.ZKInputs) } + +func TestZKInputsForceExitNotEnoughBalance(t *testing.T) { + // Test to check a ForceExit of more ethers than the existing in the + // Account.Balance. The ExitTree should not be updated, and the + // ZKInputs for that Exit should be updated with EffectiveAmount=0. + + dir, err := ioutil.TempDir("", "tmpdb") + require.NoError(t, err) + defer assert.NoError(t, os.RemoveAll(dir)) + + nLevels := 16 + sdb, err := statedb.NewStateDB(statedb.Config{Path: dir, Keep: 128, + Type: statedb.TypeBatchBuilder, NLevels: nLevels}) + assert.NoError(t, err) + + chainID := uint16(1) + + tc := til.NewContext(chainID, common.RollupConstMaxL1UserTx) + + set := ` + Type: Blockchain + + CreateAccountDeposit(0) A: 100 + + > batchL1 // batch1: freeze L1User{1} + > batchL1 // batch2: forge L1User{1} + + ForceExit(0) A: 110 + + > batchL1 // batch3: freeze L1User{1} + > batchL1 // batch4: forge L1User{1} + > block + ` + blocks, err := tc.GenerateBlocks(set) + require.NoError(t, err) + err = tc.FillBlocksExtra(blocks, &til.ConfigExtra{}) + require.NoError(t, err) + err = tc.FillBlocksForgedL1UserTxs(blocks) + require.NoError(t, err) + + config := Config{ + NLevels: uint32(nLevels), + MaxTx: 3, + MaxL1Tx: 2, + MaxFeeTx: 2, + ChainID: chainID, + } + tp := NewTxProcessor(sdb, config) + + // process Batch2: + ptOut, err := tp.ProcessTxs(nil, blocks[0].Rollup.Batches[1].L1UserTxs, nil, nil) + require.NoError(t, err) + assert.Equal(t, "0", ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String()) + assert.Equal(t, "601907123714740672507543219135850785231457721026220266509483476465720317012", ptOut.ZKInputs.Metadata.NewStateRootRaw.BigInt().String()) + assert.Equal(t, "100", ptOut.ZKInputs.DepositAmountF[0].String()) + + h, err := ptOut.ZKInputs.HashGlobalData() + require.NoError(t, err) + assert.Equal(t, "14174471316940971695000456127661865244788762525073872859531020744210652210162", h.String()) + // printZKInputs(t, ptOut.ZKInputs) + + // process Batch4: + ptOut, err = tp.ProcessTxs(nil, blocks[0].Rollup.Batches[3].L1UserTxs, nil, nil) + require.NoError(t, err) + assert.Equal(t, "8825325486495440273775434090732690734570906747868172587139772577288107480140", ptOut.ZKInputs.Metadata.NewExitRootRaw.BigInt().String()) + assert.Equal(t, "601907123714740672507543219135850785231457721026220266509483476465720317012", ptOut.ZKInputs.Metadata.NewStateRootRaw.BigInt().String()) + assert.Equal(t, "110", ptOut.ZKInputs.AmountF[0].String()) + + h, err = ptOut.ZKInputs.HashGlobalData() + require.NoError(t, err) + assert.Equal(t, "14727836686700990127460705797385474793525483649535922165380216690692980077396", h.String()) + + // printZKInputs(t, ptOut.ZKInputs) + + s, err := json.Marshal(ptOut.ZKInputs) + require.NoError(t, err) + // the 'expected' data has been checked with the circom circuits + expected := `{"amountF":["110","0","0"],"auxFromIdx":["0","0","0"],"auxToIdx":["0","0","0"],"ay1":["14382649545529405976710664157356364657039027681269256663271478725131562622080","0","0"],"ay2":["14382649545529405976710664157356364657039027681269256663271478725131562622080","0","0"],"ay3":["0","0"],"balance1":["100","0","0"],"balance2":["0","0","0"],"balance3":["0","0"],"currentNumBatch":"2","ethAddr1":["721457446580647751014191829380889690493307935711","0","0"],"ethAddr2":["721457446580647751014191829380889690493307935711","0","0"],"ethAddr3":["0","0"],"feeIdxs":["0","0"],"feePlanTokens":["0","0"],"fromBjjCompressed":[["0","0","0","0","0","0","0","1","0","0","1","1","1","0","0","1","1","1","0","0","1","0","0","0","1","1","1","1","0","0","1","0","1","0","1","1","1","0","1","0","1","1","1","1","0","0","1","0","0","0","0","1","1","0","1","1","0","1","1","1","0","1","0","0","0","1","0","1","0","0","1","0","1","1","1","1","1","1","0","0","1","1","0","1","0","1","1","1","1","1","1","0","0","1","1","1","1","1","1","0","0","0","0","1","1","1","1","0","0","1","0","1","1","0","0","0","0","0","1","1","0","0","0","0","1","1","1","0","1","0","0","0","0","1","1","0","1","0","0","0","0","1","0","0","0","0","0","1","1","1","1","1","1","1","1","0","1","1","0","1","0","1","0","1","0","0","1","1","1","0","0","0","1","1","0","0","0","1","0","1","1","0","1","0","1","0","1","1","1","0","0","1","1","1","0","1","1","1","1","1","1","0","0","1","0","0","0","1","0","0","0","1","1","0","0","0","1","1","0","0","1","0","0","1","1","0","0","1","0","0","0","1","0","1","0","1","0","0","1","0","0","0","1","1","0","0","1","1","1","1","1","1","1","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]],"fromEthAddr":["721457446580647751014191829380889690493307935711","0","0"],"fromIdx":["256","0","0"],"globalChainID":"1","imAccFeeOut":[["0","0"],["0","0"]],"imExitRoot":["8825325486495440273775434090732690734570906747868172587139772577288107480140","8825325486495440273775434090732690734570906747868172587139772577288107480140"],"imFinalAccFee":["0","0"],"imInitStateRootFee":"601907123714740672507543219135850785231457721026220266509483476465720317012","imOnChain":["1","0"],"imOutIdx":["256","256"],"imStateRoot":["601907123714740672507543219135850785231457721026220266509483476465720317012","601907123714740672507543219135850785231457721026220266509483476465720317012"],"imStateRootFee":["601907123714740672507543219135850785231457721026220266509483476465720317012"],"isOld0_1":["0","0","0"],"isOld0_2":["1","0","0"],"loadAmountF":["0","0","0"],"maxNumBatch":["0","0","0"],"newAccount":["0","0","0"],"newExit":["1","0","0"],"nonce1":["0","0","0"],"nonce2":["0","0","0"],"nonce3":["0","0"],"oldKey1":["0","0","0"],"oldKey2":["0","0","0"],"oldLastIdx":"256","oldStateRoot":"601907123714740672507543219135850785231457721026220266509483476465720317012","oldValue1":["0","0","0"],"oldValue2":["0","0","0"],"onChain":["1","0","0"],"r8x":["0","0","0"],"r8y":["0","0","0"],"rqOffset":["0","0","0"],"rqToBjjAy":["0","0","0"],"rqToEthAddr":["0","0","0"],"rqTxCompressedDataV2":["0","0","0"],"s":["0","0","0"],"siblings1":[["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]],"siblings2":[["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]],"siblings3":[["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"],["0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0","0"]],"sign1":["0","0","0"],"sign2":["0","0","0"],"sign3":["0","0"],"toBjjAy":["0","0","0"],"toEthAddr":["0","0","0"],"toIdx":["1","0","0"],"tokenID1":["0","0","0"],"tokenID2":["0","0","0"],"tokenID3":["0","0"],"txCompressedData":["79228162514336395195199514127","7617635855","7617635855"],"txCompressedDataV2":["0","0","0"]}` + assert.Equal(t, expected, string(s)) +}