arnaucube
arnaucube/hermez-nodePublic Self-hosted repository
Merge pull request #296 from hermeznetwork/feature/zkinputs2

Add methods for ZKInputs IntermStates generation
Eduard S eduard@iden3.io
Mon, 23 Nov 2020 18:11:45 +0100
commit

132691bb43a325a261e089281135032a1a5c6a14

parent

bf88eb60b8c1d2764da05f60902e6cbefc902f07

M common/fee.go → common/fee.go

@@ -6,6 +6,16 @@ "math"

"math/big" ) +// MaxFeePlan is the maximum value of the FeePlan +const MaxFeePlan = 256 + +// FeePlan represents the fee model, a position in the array indicates the +// percentage of tokens paid in concept of fee for a transaction +var FeePlan = [MaxFeePlan]float64{} + +// FeeFactorLsh60 is the feeFactor << 60 +var FeeFactorLsh60 [256]*big.Int + // RecommendedFee is the recommended fee to pay in USD per transaction set by // the coordinator according to the tx type (if the tx requires to create an // account and register, only register or he account already esists)

@@ -31,13 +41,6 @@ return math.Pow(2, (float64(f) - 192.0)) //nolint:gomnd

} } -// MaxFeePlan is the maximum value of the FeePlan -const MaxFeePlan = 256 - -// FeePlan represents the fee model, a position in the array indicates the -// percentage of tokens paid in concept of fee for a transaction -var FeePlan = [MaxFeePlan]float64{} - // CalcFeeAmount calculates the fee amount in tokens from an amount and // feeSelector (fee index). func CalcFeeAmount(amount *big.Int, feeSel FeeSelector) (*big.Int, error) {

@@ -54,9 +57,6 @@

func init() { setFeeFactorLsh60(&FeeFactorLsh60) } - -// FeeFactorLsh60 is the feeFactor << 60 -var FeeFactorLsh60 [256]*big.Int func setFeeFactorLsh60(feeFactorLsh60 *[256]*big.Int) { feeFactorLsh60[0], _ = new(big.Int).SetString("0", 10)
M common/l1tx.go → common/l1tx.go

@@ -146,6 +146,43 @@ }

return genericTx } +// TxCompressedData spec: +// [ 1 bits ] empty (toBJJSign) // 1 byte +// [ 8 bits ] empty (userFee) // 1 byte +// [ 40 bits ] empty (nonce) // 5 bytes +// [ 32 bits ] tokenID // 4 bytes +// [ 16 bits ] amountFloat16 // 2 bytes +// [ 48 bits ] toIdx // 6 bytes +// [ 48 bits ] fromIdx // 6 bytes +// [ 16 bits ] chainId // 2 bytes +// [ 32 bits ] empty (signatureConstant) // 4 bytes +// Total bits compressed data: 241 bits // 31 bytes in *big.Int representation +func (tx L1Tx) TxCompressedData() (*big.Int, error) { + amountFloat16, err := NewFloat16(tx.Amount) + if err != nil { + return nil, err + } + var b [31]byte + // b[0:7] empty: no fee neither nonce + copy(b[7:11], tx.TokenID.Bytes()) + copy(b[11:13], amountFloat16.Bytes()) + toIdxBytes, err := tx.ToIdx.Bytes() + if err != nil { + return nil, err + } + copy(b[13:19], toIdxBytes[:]) + fromIdxBytes, err := tx.FromIdx.Bytes() + if err != nil { + return nil, err + } + copy(b[19:25], fromIdxBytes[:]) + copy(b[25:27], []byte{0, 1}) // TODO this will be generated by the ChainID config parameter + // b[27:] empty: no signature + + bi := new(big.Int).SetBytes(b[:]) + return bi, nil +} + // 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
M common/l1tx_test.go → common/l1tx_test.go

@@ -49,6 +49,22 @@ assert.Nil(t, err)

assert.Equal(t, "0x01000000000000cafe005800", l1Tx.TxID.String()) } +func TestL1TxCompressedData(t *testing.T) { + tx := L1Tx{ + FromIdx: 2, + ToIdx: 3, + Amount: big.NewInt(4), + TokenID: 5, + } + txCompressedData, err := tx.TxCompressedData() + assert.Nil(t, err) + + // test vector value generated from javascript implementation + expectedStr := "7307597389635308713748674793997299267460566876160" + assert.Equal(t, expectedStr, txCompressedData.String()) + assert.Equal(t, "050004000000000003000000000002000100000000", hex.EncodeToString(txCompressedData.Bytes())) +} + func TestL1userTxByteParsers(t *testing.T) { var pkComp babyjub.PublicKeyComp pkCompL := []byte("0x56ca90f80d7c374ae7485e9bcc47d4ac399460948da6aeeb899311097925a72c")
M common/pooll2tx.go → common/pooll2tx.go

@@ -140,13 +140,64 @@ if err != nil {

return nil, err } copy(b[19:25], fromIdxBytes[:]) - copy(b[25:27], []byte{0, 1, 0, 0}) // TODO check js implementation (unexpected behaviour from test vector generated from js) + copy(b[25:27], []byte{0, 1}) // TODO this will be generated by the ChainID config parameter copy(b[27:31], sc.Bytes()) bi := new(big.Int).SetBytes(b[:]) return bi, nil } +// TxCompressedDataV2 spec: +// [ 1 bits ] toBJJSign // 1 byte +// [ 8 bits ] userFee // 1 byte +// [ 40 bits ] nonce // 5 bytes +// [ 32 bits ] tokenID // 4 bytes +// [ 16 bits ] amountFloat16 // 2 bytes +// [ 48 bits ] toIdx // 6 bytes +// [ 48 bits ] fromIdx // 6 bytes +// Total bits compressed data: 193 bits // 25 bytes in *big.Int representation +func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) { + if tx.Amount == nil { + tx.Amount = big.NewInt(0) + } + amountFloat16, err := NewFloat16(tx.Amount) + if err != nil { + return nil, err + } + var b [25]byte + toBJJSign := byte(0) + if tx.ToBJJ != nil && babyjub.PointCoordSign(tx.ToBJJ.X) { + toBJJSign = byte(1) + } + b[0] = toBJJSign + b[1] = byte(tx.Fee) + nonceBytes, err := tx.Nonce.Bytes() + if err != nil { + return nil, err + } + copy(b[2:7], nonceBytes[:]) + copy(b[7:11], tx.TokenID.Bytes()) + copy(b[11:13], amountFloat16.Bytes()) + toIdxBytes, err := tx.ToIdx.Bytes() + if err != nil { + return nil, err + } + copy(b[13:19], toIdxBytes[:]) + fromIdxBytes, err := tx.FromIdx.Bytes() + if err != nil { + return nil, err + } + copy(b[19:25], fromIdxBytes[:]) + + bi := new(big.Int).SetBytes(b[:]) + return bi, nil +} + +// RqTxCompressedDataV2 is like the TxCompressedDataV2 but using the 'Rq' +// parameters. In a future iteration of the hermez-node, the 'Rq' parameters +// can be inside a struct, which contains the 'Rq' transaction grouped inside, +// so then computing the 'RqTxCompressedDataV2' would be just calling +// 'tx.Rq.TxCompressedDataV2()'. // RqTxCompressedDataV2 spec: // [ 1 bits ] rqToBJJSign // 1 byte // [ 8 bits ] rqUserFee // 1 byte
M common/pooll2tx_test.go → common/pooll2tx_test.go

@@ -40,11 +40,54 @@ assert.Nil(t, err)

// test vector value generated from javascript implementation expectedStr := "1766847064778421992193717128424891165872736891548909569553540449389241871" assert.Equal(t, expectedStr, txCompressedData.String()) + assert.Equal(t, "010000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())) + tx = PoolL2Tx{ + RqFromIdx: 7, + RqToIdx: 8, + RqAmount: big.NewInt(9), + RqTokenID: 10, + RqNonce: 11, + RqFee: 12, + RqToBJJ: sk.Public(), + } + txCompressedData, err = tx.RqTxCompressedDataV2() + assert.Nil(t, err) + // test vector value generated from javascript implementation + expectedStr = "6571340879233176732837827812956721483162819083004853354503" + assert.Equal(t, expectedStr, txCompressedData.String()) + assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())) +} + +func TestTxCompressedDataV2(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + tx := PoolL2Tx{ + FromIdx: 7, + ToIdx: 8, + Amount: big.NewInt(9), + TokenID: 10, + Nonce: 11, + Fee: 12, + ToBJJ: sk.Public(), + } + txCompressedData, err := tx.TxCompressedDataV2() + assert.Nil(t, err) + // test vector value generated from javascript implementation + expectedStr := "6571340879233176732837827812956721483162819083004853354503" + assert.Equal(t, expectedStr, txCompressedData.String()) expected, ok := new(big.Int).SetString(expectedStr, 10) assert.True(t, ok) + assert.Equal(t, expected.Bytes(), txCompressedData.Bytes()) - assert.Equal(t, "10000000000060000000500040000000000030000000000020001c60be60f", hex.EncodeToString(txCompressedData.Bytes())[1:]) - tx = PoolL2Tx{ + assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())) +} + +func TestRqTxCompressedDataV2(t *testing.T) { + var sk babyjub.PrivateKey + _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) + assert.Nil(t, err) + tx := PoolL2Tx{ RqFromIdx: 7, RqToIdx: 8, RqAmount: big.NewInt(9),

@@ -53,15 +96,15 @@ RqNonce: 11,

RqFee: 12, RqToBJJ: sk.Public(), } - txCompressedData, err = tx.RqTxCompressedDataV2() + txCompressedData, err := tx.RqTxCompressedDataV2() assert.Nil(t, err) // test vector value generated from javascript implementation - expectedStr = "6571340879233176732837827812956721483162819083004853354503" + expectedStr := "6571340879233176732837827812956721483162819083004853354503" assert.Equal(t, expectedStr, txCompressedData.String()) - expected, ok = new(big.Int).SetString(expectedStr, 10) + expected, ok := new(big.Int).SetString(expectedStr, 10) assert.True(t, ok) assert.Equal(t, expected.Bytes(), txCompressedData.Bytes()) - assert.Equal(t, "10c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())[1:]) + assert.Equal(t, "010c000000000b0000000a0009000000000008000000000007", hex.EncodeToString(txCompressedData.Bytes())) } func TestHashToSign(t *testing.T) {
M common/token.go → common/token.go

@@ -52,3 +52,8 @@ }

tid := binary.BigEndian.Uint32(b[:4]) return TokenID(tid), nil } + +// TokenIDFromBigInt returns a TokenID with the value of the given *big.Int +func TokenIDFromBigInt(b *big.Int) TokenID { + return TokenID(b.Int64()) +}
M common/zk.go → common/zk.go

@@ -43,7 +43,7 @@ }

// ZKInputs represents the inputs that will be used to generate the zkSNARK proof type ZKInputs struct { - Metadata ZKMetadata + Metadata ZKMetadata `json:"-"` // // General

@@ -183,23 +183,28 @@ // Last transaction does not need intermediate state since its output

// will not be used. // decode-tx - // ISOnChain indicates if tx is L1 (true) or L2 (false) + // ISOnChain indicates if tx is L1 (true (1)) or L2 (false (0)) ISOnChain []*big.Int `json:"imOnChain"` // bool, len: [nTx - 1] // ISOutIdx current index account for each Tx + // Contains the index of the created account in case that the tx is of + // account creation type. ISOutIdx []*big.Int `json:"imOutIdx"` // uint64 (max nLevels bits), len: [nTx - 1] // rollup-tx - // ISStateRoot root at the moment of the Tx, the state root value once the Tx is processed into the state tree + // ISStateRoot root at the moment of the Tx (once processed), the state root value once the Tx is processed into the state tree ISStateRoot []*big.Int `json:"imStateRoot"` // Hash, len: [nTx - 1] - // ISExitTree root at the moment of the Tx the value once the Tx is processed into the exit tree + // ISExitTree root at the moment (once processed) 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 accumulated fees once the Tx is processed. Contains the + // array of FeeAccount Balances at each moment of each Tx processed. 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 root at the moment of the Tx (once processed), the state root value once the Tx is processed into the state tree 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 final accumulated fees (before computing the fees-tx). + // Contains the final values of the ISAccFeeOut parameter ISFinalAccFee []*big.Int `json:"imFinalAccFee"` // big.Int, len: [maxFeeIdxs - 1] }
M db/statedb/txprocessors.go → db/statedb/txprocessors.go

@@ -2,6 +2,7 @@ package statedb

import ( "errors" + "fmt" "io/ioutil" "math/big" "os"

@@ -72,6 +73,10 @@ return nil, errors.New("Expected StateDB.zki==nil, something went wrong and it's not empty")

} defer s.resetZKInputs() + if len(coordIdxs) >= int(ptc.MaxFeeTx) { + return nil, fmt.Errorf("CoordIdxs (%d) length must be smaller than MaxFeeTx (%d)", len(coordIdxs), ptc.MaxFeeTx) + } + s.accumulatedFees = make(map[common.Idx]*big.Int) nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs)

@@ -94,7 +99,7 @@ s.zki.OldStateRoot = s.mt.Root().BigInt()

} // TBD if ExitTree is only in memory or stored in disk, for the moment - // only needed in memory + // is only needed in memory if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { tmpDir, err := ioutil.TempDir("", "hermez-statedb-exittree") if err != nil {

@@ -122,6 +127,22 @@ exitIdx, exitAccount, newExit, createdAccount, err := s.processL1Tx(exitTree, &l1usertxs[i])

if err != nil { return nil, err } + 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) + + if s.i < nTx-1 { + s.zki.ISOutIdx[s.i] = s.idx.BigInt() + s.zki.ISStateRoot[s.i] = s.mt.Root().BigInt() + } + } if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { if exitIdx != nil && exitTree != nil { exits[s.i] = processedExit{

@@ -133,17 +154,6 @@ }

} s.i++ } - 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

@@ -164,6 +174,12 @@ if err != nil {

return nil, err } s.zki.Metadata.L1TxsData = append(s.zki.Metadata.L1TxsData, l1TxData) + + if s.i < nTx-1 { + s.zki.ISOutIdx[s.i] = s.idx.BigInt() + s.zki.ISStateRoot[s.i] = s.mt.Root().BigInt() + } + s.i++ } }

@@ -179,12 +195,24 @@ coordIdxsMap, err := s.getTokenIDsFromIdxs(coordIdxs)

if err != nil { return nil, err } + // collectedFees will contain the amount of fee collected for each + // TokenID var collectedFees map[common.TokenID]*big.Int - if s.typ == TypeSynchronizer { + if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { collectedFees = make(map[common.TokenID]*big.Int) for tokenID := range coordIdxsMap { collectedFees[tokenID] = big.NewInt(0) } + } + + if s.zki != nil { + // get the feePlanTokens + feePlanTokens, err := s.getFeePlanTokens(coordIdxs, l2txs) + if err != nil { + log.Error(err) + return nil, err + } + copy(s.zki.FeePlanTokens, feePlanTokens) } // Process L2Txs

@@ -193,6 +221,23 @@ exitIdx, exitAccount, newExit, err := s.processL2Tx(coordIdxsMap, collectedFees, exitTree, &l2txs[i])

if err != nil { return nil, err } + if s.zki != nil { + l2TxData, err := l2txs[i].L2Tx().Bytes(s.zki.Metadata.NLevels) + if err != nil { + return nil, err + } + s.zki.Metadata.L2TxsData = append(s.zki.Metadata.L2TxsData, l2TxData) + + if s.i < nTx-1 { + // Intermediate States + s.zki.ISOutIdx[s.i] = s.idx.BigInt() + s.zki.ISStateRoot[s.i] = s.mt.Root().BigInt() + s.zki.ISAccFeeOut[s.i] = formatAccumulatedFees(collectedFees, s.zki.FeePlanTokens) + } + if s.i == nTx-1 { + s.zki.ISFinalAccFee = formatAccumulatedFees(collectedFees, s.zki.FeePlanTokens) + } + } if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { if exitIdx != nil && exitTree != nil { exits[s.i] = processedExit{

@@ -204,13 +249,11 @@ }

} s.i++ } - if s.zki != nil { - l2TxData, err := l2txs[i].L2Tx().Bytes(s.zki.Metadata.NLevels) - if err != nil { - return nil, err - } - s.zki.Metadata.L2TxsData = append(s.zki.Metadata.L2TxsData, l2TxData) - } + } + + if s.zki != nil { + // before computing the Fees txs, set the ISInitStateRootFee + s.zki.ISInitStateRootFee = s.mt.Root().BigInt() } // distribute the AccumulatedFees from the processed L2Txs into the

@@ -242,6 +285,8 @@ s.zki.Siblings3[iFee] = siblingsToZKInputFormat(pFee.Siblings)

// add Coord Idx to ZKInputs.FeeTxsData s.zki.FeeIdxs[iFee] = idx.BigInt() + + s.zki.ISStateRootFee[iFee] = s.mt.Root().BigInt() } iFee++ }

@@ -293,6 +338,10 @@ s.zki.IsOld0_2[i] = big.NewInt(1)

} s.zki.OldKey2[i] = p.OldKey.BigInt() s.zki.OldValue2[i] = p.OldValue.BigInt() + + if i < nTx-1 { + s.zki.ISExitRoot[i] = exitTree.Root().BigInt() + } } } if s.typ == TypeSynchronizer {

@@ -310,17 +359,9 @@

// compute last ZKInputs parameters s.zki.GlobalChainID = big.NewInt(0) // TODO, 0: ethereum, this will be get from config file // zki.FeeIdxs = ? // TODO, this will be get from the config file - tokenIDs, err := s.getTokenIDsBigInt(l1usertxs, l1coordinatortxs, l2txs) - if err != nil { - log.Error(err) - return nil, err - } - s.zki.FeePlanTokens = tokenIDs s.zki.Metadata.NewStateRootRaw = s.mt.Root() s.zki.Metadata.NewExitRootRaw = exitTree.Root() - // s.zki.ISInitStateRootFee = s.mt.Root().BigInt() - // return ZKInputs as the BatchBuilder will return it to forge the Batch return &ProcessTxOutput{ ZKInputs: s.zki,

@@ -331,15 +372,22 @@ CollectedFees: nil,

}, nil } -// getTokenIDsBigInt returns the list of TokenIDs in *big.Int format -func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []common.L1Tx, l2txs []common.PoolL2Tx) ([]*big.Int, error) { - tokenIDs := make(map[common.TokenID]bool) - for i := 0; i < len(l1usertxs); i++ { - tokenIDs[l1usertxs[i].TokenID] = true - } - for i := 0; i < len(l1coordinatortxs); i++ { - tokenIDs[l1coordinatortxs[i].TokenID] = true +// getFeePlanTokens returns an array of *big.Int containing a list of tokenIDs +// corresponding to the given CoordIdxs and the processed L2Txs +func (s *StateDB) getFeePlanTokens(coordIdxs []common.Idx, l2txs []common.PoolL2Tx) ([]*big.Int, error) { + // get Coordinator TokenIDs corresponding to the Idxs where the Fees + // will be sent + coordTokenIDs := make(map[common.TokenID]bool) + for i := 0; i < len(coordIdxs); i++ { + acc, err := s.GetAccount(coordIdxs[i]) + if err != nil { + log.Errorf("could not get account to determine TokenID of CoordIdx %d not found: %s", coordIdxs[i], err.Error()) + return nil, err + } + coordTokenIDs[acc.TokenID] = true } + + tokenIDs := make(map[common.TokenID]bool) for i := 0; i < len(l2txs); i++ { // as L2Tx does not have parameter TokenID, get it from the // AccountsDB (in the StateDB)

@@ -348,7 +396,9 @@ if err != nil {

log.Errorf("could not get account to determine TokenID of L2Tx: FromIdx %d not found: %s", l2txs[i].FromIdx, err.Error()) return nil, err } - tokenIDs[acc.TokenID] = true + if _, ok := coordTokenIDs[acc.TokenID]; ok { + tokenIDs[acc.TokenID] = true + } } var tBI []*big.Int for t := range tokenIDs {

@@ -368,7 +418,12 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, bool, *common.Account, error) {

// ZKInputs if s.zki != nil { // Txs - // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready + var err error + s.zki.TxCompressedData[s.i], err = tx.TxCompressedData() + if err != nil { + log.Error(err) + return nil, nil, false, nil, err + } s.zki.FromIdx[s.i] = tx.FromIdx.BigInt() s.zki.ToIdx[s.i] = tx.ToIdx.BigInt() s.zki.OnChain[s.i] = big.NewInt(1)

@@ -408,11 +463,6 @@ }

// TODO applyCreateAccount will return the created account, // which in the case type==TypeSynchronizer will be added to an // array of created accounts that will be returned - - if s.zki != nil { - s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account - s.zki.NewAccount[s.i] = big.NewInt(1) - } case common.TxTypeDeposit: // update balance of the MT account err := s.applyDeposit(tx, false)

@@ -436,11 +486,6 @@ if err != nil {

log.Error(err) return nil, nil, false, nil, err } - - if s.zki != nil { - s.zki.AuxFromIdx[s.i] = s.idx.BigInt() // last s.idx is the one used for creating the new account - s.zki.NewAccount[s.i] = big.NewInt(1) - } case common.TxTypeForceExit: // execute exit flow // coordIdxsMap is 'nil', as at L1Txs there is no L2 fees

@@ -485,8 +530,14 @@

// ZKInputs if s.zki != nil { // Txs - // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready - // s.zki.TxCompressedDataV2[s.i] = tx.TxCompressedDataV2() // uncomment once L2Tx.TxCompressedDataV2 is ready + s.zki.TxCompressedData[s.i], err = tx.TxCompressedData() + if err != nil { + return nil, nil, false, err + } + s.zki.TxCompressedDataV2[s.i], err = tx.TxCompressedDataV2() + if err != nil { + return nil, nil, false, err + } s.zki.FromIdx[s.i] = tx.FromIdx.BigInt() s.zki.ToIdx[s.i] = tx.ToIdx.BigInt()

@@ -587,6 +638,14 @@ s.zki.OldKey1[s.i] = p.OldKey.BigInt()

s.zki.OldValue1[s.i] = p.OldValue.BigInt() s.zki.Metadata.NewLastIdxRaw = s.idx + 1 + + s.zki.AuxFromIdx[s.i] = common.Idx(s.idx + 1).BigInt() + s.zki.NewAccount[s.i] = big.NewInt(1) + + if s.i < len(s.zki.ISOnChain) { // len(s.zki.ISOnChain) == nTx + // intermediate states + s.zki.ISOnChain[s.i] = big.NewInt(1) + } } s.idx = s.idx + 1

@@ -695,7 +754,7 @@ // accumulate the fee for the Coord account

accumulated := s.accumulatedFees[accCoord.Idx] accumulated.Add(accumulated, fee) - if s.typ == TypeSynchronizer { + if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { collected := collectedFees[accCoord.TokenID] collected.Add(collected, fee) }

@@ -800,6 +859,12 @@ s.zki.OldKey1[s.i] = p.OldKey.BigInt()

s.zki.OldValue1[s.i] = p.OldValue.BigInt() s.zki.Metadata.NewLastIdxRaw = s.idx + 1 + + s.zki.AuxFromIdx[s.i] = common.Idx(s.idx + 1).BigInt() + s.zki.NewAccount[s.i] = big.NewInt(1) + + // intermediate states + s.zki.ISOnChain[s.i] = big.NewInt(1) } // update receiver account in localStateDB

@@ -854,7 +919,7 @@ // accumulate the fee for the Coord account

accumulated := s.accumulatedFees[accCoord.Idx] accumulated.Add(accumulated, fee) - if s.typ == TypeSynchronizer { + if s.typ == TypeSynchronizer || s.typ == TypeBatchBuilder { collected := collectedFees[accCoord.TokenID] collected.Add(collected, fee) }
M db/statedb/txprocessors_test.go → db/statedb/txprocessors_test.go

@@ -477,6 +477,80 @@ require.Nil(t, err)

assert.Equal(t, "9827704113668630072730115158977131501210702363656902211840117643154933433410", sdb.mt.Root().BigInt().String()) } +func TestCircomTest(t *testing.T) { + dir, err := ioutil.TempDir("", "tmpdb") + require.Nil(t, err) + defer assert.Nil(t, os.RemoveAll(dir)) + + sdb, err := NewStateDB(dir, TypeBatchBuilder, 8) + assert.Nil(t, err) + + // same values than in the js test + bjj0, err := common.BJJFromStringWithChecksum("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7") + assert.Nil(t, err) + l1Txs := []common.L1Tx{ + { + FromIdx: 0, + // LoadAmount: big.NewInt(10400), + LoadAmount: big.NewInt(16000000), + Amount: big.NewInt(0), + TokenID: 1, + FromBJJ: bjj0, + FromEthAddr: ethCommon.HexToAddress("0x7e5f4552091a69125d5dfcb7b8c2659029395bdf"), + ToIdx: 0, + Type: common.TxTypeCreateAccountDeposit, + }, + } + l2Txs := []common.PoolL2Tx{ + { + FromIdx: 256, + ToIdx: 256, + TokenID: 1, + Amount: big.NewInt(1000), + Nonce: 0, + Fee: 126, + Type: common.TxTypeTransfer, + }, + } + + ptc := ProcessTxsConfig{ + NLevels: 8, + MaxFeeTx: 2, + MaxTx: 5, + MaxL1Tx: 2, + } + ptOut, err := sdb.ProcessTxs(ptc, nil, l1Txs, nil, l2Txs) + require.Nil(t, err) + + // check expected account keys values from tx inputs + acc, err := sdb.GetAccount(common.Idx(256)) + require.Nil(t, err) + assert.Equal(t, "d746824f7d0ac5044a573f51b278acb56d823bec39551d1d7bf7378b68a1b021", acc.PublicKey.Compress().String()) + assert.Equal(t, "0x7E5F4552091A69125d5DfCb7b8C2659029395Bdf", acc.EthAddr.Hex()) + + // check that there no exist more accounts + _, err = sdb.GetAccount(common.Idx(257)) + require.NotNil(t, err) + ptOut.ZKInputs.FeeIdxs[0] = common.Idx(256).BigInt() + + s, err := json.Marshal(ptOut.ZKInputs) + require.Nil(t, err) + debug := false + if debug { + fmt.Println("\nCopy&Paste into js circom test:\n let zkInput = JSON.parse(`" + string(s) + "`);") + + h, err := ptOut.ZKInputs.HashGlobalData() + require.Nil(t, err) + fmt.Printf(` + const output={ + hashGlobalInputs: "%s", + }; + await circuit.assertOut(w, output); + `, h.String()) + fmt.Println("") + } +} + func TestZKInputsHashTestVector0(t *testing.T) { dir, err := ioutil.TempDir("", "tmpdb") require.Nil(t, err)
M db/statedb/utils.go → db/statedb/utils.go

@@ -160,3 +160,19 @@ }

return r } + +// formatAccumulatedFees returns an array of [nFeeAccounts]*big.Int containing +// the balance of each FeeAccount, taken from the 'collectedFees' map, in the +// order of the 'orderTokenIDs' +func formatAccumulatedFees(collectedFees map[common.TokenID]*big.Int, orderTokenIDs []*big.Int) []*big.Int { + accFeeOut := make([]*big.Int, len(orderTokenIDs)) + for i := 0; i < len(orderTokenIDs); i++ { + tokenID := common.TokenIDFromBigInt(orderTokenIDs[i]) + if _, ok := collectedFees[tokenID]; ok { + accFeeOut[i] = collectedFees[tokenID] + } else { + accFeeOut[i] = big.NewInt(0) + } + } + return accFeeOut +}