From 69fe471f112da52b07ab2dc5f33f46c3259dcfb6 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 9 Sep 2020 19:21:09 +0200 Subject: [PATCH] Add StateTree leafs & ExtTree data ZKI calculation --- common/pooll2tx.go | 25 +- common/utils.go | 18 -- common/zk.go | 17 +- db/statedb/statedb.go | 2 +- db/statedb/txprocessors.go | 327 +++++++++++++++++++++------ db/statedb/utils.go | 28 +++ {common => db/statedb}/utils_test.go | 2 +- 7 files changed, 316 insertions(+), 103 deletions(-) rename {common => db/statedb}/utils_test.go (97%) diff --git a/common/pooll2tx.go b/common/pooll2tx.go index 92009b6..04844b2 100644 --- a/common/pooll2tx.go +++ b/common/pooll2tx.go @@ -27,6 +27,11 @@ func (n Nonce) Bytes() ([5]byte, error) { return b, nil } +// BigInt returns the *big.Int representation of the Nonce value +func (n Nonce) BigInt() *big.Int { + return big.NewInt(int64(n)) +} + // NonceFromBytes returns Nonce from a [5]byte func NonceFromBytes(b [5]byte) Nonce { var nonceBytes [8]byte @@ -76,7 +81,7 @@ type PoolL2Tx struct { // [ 32 bits ] tokenID // 4 bytes: [20:24] // [ 40 bits ] nonce // 5 bytes: [24:29] // [ 8 bits ] userFee // 1 byte: [29:30] -// [ 1 bits ] toBjjSign // 1 byte: [30:31] +// [ 1 bits ] toBJJSign // 1 byte: [30:31] // Total bits compressed data: 241 bits // 31 bytes in *big.Int representation func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) { // sigconstant @@ -102,11 +107,11 @@ func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) { } copy(b[24:29], nonceBytes[:]) b[29] = byte(tx.Fee) - toBjjSign := byte(0) + toBJJSign := byte(0) if babyjub.PointCoordSign(tx.ToBJJ.X) { - toBjjSign = byte(1) + toBJJSign = byte(1) } - b[30] = toBjjSign + b[30] = toBJJSign bi := new(big.Int).SetBytes(SwapEndianness(b[:])) return bi, nil @@ -119,7 +124,7 @@ func (tx *PoolL2Tx) TxCompressedData() (*big.Int, error) { // [ 32 bits ] tokenID // 4 bytes: [14:18] // [ 40 bits ] nonce // 5 bytes: [18:23] // [ 8 bits ] userFee // 1 byte: [23:24] -// [ 1 bits ] toBjjSign // 1 byte: [24:25] +// [ 1 bits ] toBJJSign // 1 byte: [24:25] // Total bits compressed data: 193 bits // 25 bytes in *big.Int representation func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) { amountFloat16, err := utils.NewFloat16(tx.Amount) @@ -137,11 +142,11 @@ func (tx *PoolL2Tx) TxCompressedDataV2() (*big.Int, error) { } copy(b[18:23], nonceBytes[:]) b[23] = byte(tx.Fee) - toBjjSign := byte(0) + toBJJSign := byte(0) if babyjub.PointCoordSign(tx.ToBJJ.X) { - toBjjSign = byte(1) + toBJJSign = byte(1) } - b[24] = toBjjSign + b[24] = toBJJSign bi := new(big.Int).SetBytes(SwapEndianness(b[:])) return bi, nil @@ -154,13 +159,13 @@ func (tx *PoolL2Tx) HashToSign() (*big.Int, error) { return nil, err } toEthAddr := EthAddrToBigInt(tx.ToEthAddr) - toBjjAy := tx.ToBJJ.Y + toBJJAy := tx.ToBJJ.Y rqTxCompressedDataV2, err := tx.TxCompressedDataV2() if err != nil { return nil, err } - return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBjjAy, rqTxCompressedDataV2, EthAddrToBigInt(tx.RqToEthAddr), tx.RqToBJJ.Y}) + return poseidon.Hash([]*big.Int{toCompressedData, toEthAddr, toBJJAy, rqTxCompressedDataV2, EthAddrToBigInt(tx.RqToEthAddr), tx.RqToBJJ.Y}) } // VerifySignature returns true if the signature verification is correct for the given PublicKey diff --git a/common/utils.go b/common/utils.go index 3ec26be..9dad220 100644 --- a/common/utils.go +++ b/common/utils.go @@ -4,7 +4,6 @@ import ( "math/big" ethCommon "github.com/ethereum/go-ethereum/common" - "github.com/iden3/go-iden3-crypto/babyjub" ) // SwapEndianness swaps the order of the bytes in the slice. @@ -20,20 +19,3 @@ func SwapEndianness(b []byte) []byte { func EthAddrToBigInt(a ethCommon.Address) *big.Int { return new(big.Int).SetBytes(a.Bytes()) } - -// BJJCompressedTo256BigInts returns a [256]*big.Int array with the bit -// representation of the babyjub.PublicKeyComp -func BJJCompressedTo256BigInts(pkComp babyjub.PublicKeyComp) [256]*big.Int { - var r [256]*big.Int - b := pkComp[:] - - for i := 0; i < 256; i++ { - if b[i/8]&(1<<(i%8)) == 0 { - r[i] = big.NewInt(0) - } else { - r[i] = big.NewInt(1) - } - } - - return r -} diff --git a/common/zk.go b/common/zk.go index 1b7d378..4becfbd 100644 --- a/common/zk.go +++ b/common/zk.go @@ -64,13 +64,12 @@ type ZKInputs struct { // OnChain determines if is L1 (1/true) or L2 (0/false) OnChain []*big.Int // bool, len: [nTx] - // NewAccount boolean (0/1) flag set 'true' when L1 tx creates a new account (fromIdx==0) - NewAccount []*big.Int // bool, len: [nTx] // // Txs/L1Txs // - // transaction L1 + // NewAccount boolean (0/1) flag set 'true' when L1 tx creates a new account (fromIdx==0) + NewAccount []*big.Int // bool, len: [nTx] // LoadAmountF encoded as float16 LoadAmountF []*big.Int // uint16, len: [nTx] // FromEthAddr @@ -109,8 +108,8 @@ type ZKInputs struct { TokenID1 []*big.Int // uint32, len: [nTx] Nonce1 []*big.Int // uint64 (max 40 bits), len: [nTx] Sign1 []*big.Int // bool, len: [nTx] - Balance1 []*big.Int // big.Int (max 192 bits), len: [nTx] Ay1 []*big.Int // big.Int, len: [nTx] + Balance1 []*big.Int // big.Int (max 192 bits), len: [nTx] EthAddr1 []*big.Int // ethCommon.Address, len: [nTx] Siblings1 [][]*big.Int // big.Int, len: [nTx][nLevels + 1] // Required for inserts and deletes, values of the CircomProcessorProof (smt insert proof) @@ -123,8 +122,8 @@ type ZKInputs struct { TokenID2 []*big.Int // uint32, len: [nTx] Nonce2 []*big.Int // uint64 (max 40 bits), len: [nTx] Sign2 []*big.Int // bool, len: [nTx] - Balance2 []*big.Int // big.Int (max 192 bits), len: [nTx] Ay2 []*big.Int // big.Int, len: [nTx] + Balance2 []*big.Int // big.Int (max 192 bits), len: [nTx] EthAddr2 []*big.Int // ethCommon.Address, len: [nTx] Siblings2 [][]*big.Int // big.Int, len: [nTx][nLevels + 1] // newExit determines if an exit transaction has to create a new leaf in the exit tree @@ -140,8 +139,8 @@ type ZKInputs struct { TokenID3 []*big.Int // uint32, len: [maxFeeTx] Nonce3 []*big.Int // uint64 (max 40 bits), len: [maxFeeTx] Sign3 []*big.Int // bool, len: [maxFeeTx] - Balance3 []*big.Int // big.Int (max 192 bits), len: [maxFeeTx] Ay3 []*big.Int // big.Int, len: [maxFeeTx] + Balance3 []*big.Int // big.Int (max 192 bits), len: [maxFeeTx] EthAddr3 []*big.Int // ethCommon.Address, len: [maxFeeTx] Siblings3 [][]*big.Int // Hash, len: [maxFeeTx][nLevels + 1] @@ -218,8 +217,8 @@ func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { zki.TokenID1 = newSlice(nTx) zki.Nonce1 = newSlice(nTx) zki.Sign1 = newSlice(nTx) - zki.Balance1 = newSlice(nTx) zki.Ay1 = newSlice(nTx) + zki.Balance1 = newSlice(nTx) zki.EthAddr1 = newSlice(nTx) zki.Siblings1 = make([][]*big.Int, nTx) for i := 0; i < len(zki.Siblings1); i++ { @@ -232,8 +231,8 @@ func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { zki.TokenID2 = newSlice(nTx) zki.Nonce2 = newSlice(nTx) zki.Sign2 = newSlice(nTx) - zki.Balance2 = newSlice(nTx) zki.Ay2 = newSlice(nTx) + zki.Balance2 = newSlice(nTx) zki.EthAddr2 = newSlice(nTx) zki.Siblings2 = make([][]*big.Int, nTx) for i := 0; i < len(zki.Siblings2); i++ { @@ -247,8 +246,8 @@ func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { zki.TokenID3 = newSlice(maxFeeTx) zki.Nonce3 = newSlice(maxFeeTx) zki.Sign3 = newSlice(maxFeeTx) - zki.Balance3 = newSlice(maxFeeTx) zki.Ay3 = newSlice(maxFeeTx) + zki.Balance3 = newSlice(maxFeeTx) zki.EthAddr3 = newSlice(maxFeeTx) zki.Siblings3 = make([][]*big.Int, maxFeeTx) for i := 0; i < len(zki.Siblings3); i++ { diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go index e5e83ff..008e8b0 100644 --- a/db/statedb/statedb.go +++ b/db/statedb/statedb.go @@ -44,7 +44,7 @@ type StateDB struct { // idx holds the current Idx that the BatchBuilder is using idx common.Idx zki *common.ZKInputs - i int // i is used for zki + i int // i is the current transaction index in the ZKInputs generation (zki) } // 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 d3ea768..368c444 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -8,6 +8,8 @@ import ( ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" + "github.com/hermeznetwork/hermez-node/log" + "github.com/iden3/go-iden3-crypto/babyjub" "github.com/iden3/go-iden3-crypto/poseidon" "github.com/iden3/go-merkletree" "github.com/iden3/go-merkletree/db" @@ -26,6 +28,13 @@ func (s *StateDB) resetZKInputs() { s.i = 0 } +type processedExit struct { + exit bool + newExit bool + idx common.Idx + acc common.Account +} + // ProcessTxs process the given L1Txs & L2Txs applying the needed updates to // the StateDB depending on the transaction Type. Returns the common.ZKInputs // to generate the SnarkProof later used by the BatchBuilder, and if @@ -34,20 +43,23 @@ func (s *StateDB) resetZKInputs() { func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.PoolL2Tx) (*common.ZKInputs, []*common.ExitInfo, error) { var err error var exitTree *merkletree.MerkleTree - exits := make(map[common.Idx]common.Account) if s.zki != nil { - return nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong ans is not empty") + return nil, nil, errors.New("Expected StateDB.zki==nil, something went wrong and it's not empty") } defer s.resetZKInputs() nTx := len(l1usertxs) + len(l1coordinatortxs) + len(l2txs) if nTx == 0 { - return nil, nil, nil // TBD if return an error in the case of no Txs to process + // TODO return ZKInputs of batch without txs + return nil, nil, nil } + exits := make([]processedExit, nTx) if cmpZKInputs { - s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function + s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function, taken from config file/coordinator call + s.zki.OldLastIdx = (s.idx - 1).BigInt() + s.zki.OldStateRoot = s.mt.Root().BigInt() } // TBD if ExitTree is only in memory or stored in disk, for the moment @@ -59,36 +71,54 @@ func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordin // assumption: l1usertx are sorted by L1Tx.Position for _, tx := range l1usertxs { - exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx) + exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx) if err != nil { return nil, nil, err } if exitIdx != nil && cmpExitTree { - exits[*exitIdx] = *exitAccount + exits[s.i] = processedExit{ + exit: true, + newExit: newExit, + idx: *exitIdx, + acc: *exitAccount, + } } if s.zki != nil { s.i++ } } for _, tx := range l1coordinatortxs { - exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx) + exitIdx, exitAccount, newExit, err := s.processL1Tx(exitTree, tx) if err != nil { return nil, nil, err } + if exitIdx != nil { + log.Error("Unexpected Exit in L1CoordinatorTx") + } if exitIdx != nil && cmpExitTree { - exits[*exitIdx] = *exitAccount + exits[s.i] = processedExit{ + exit: true, + newExit: newExit, + idx: *exitIdx, + acc: *exitAccount, + } } if s.zki != nil { s.i++ } } for _, tx := range l2txs { - exitIdx, exitAccount, err := s.processL2Tx(exitTree, tx) + exitIdx, exitAccount, newExit, err := s.processL2Tx(exitTree, tx) if err != nil { return nil, nil, err } if exitIdx != nil && cmpExitTree { - exits[*exitIdx] = *exitAccount + exits[s.i] = processedExit{ + exit: true, + newExit: newExit, + idx: *exitIdx, + acc: *exitAccount, + } } if s.zki != nil { s.i++ @@ -99,10 +129,16 @@ func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordin return nil, nil, nil } - // once all txs processed (exitTree root frozen), for each leaf + // once all txs processed (exitTree root frozen), for each Exit, // generate common.ExitInfo data var exitInfos []*common.ExitInfo - for exitIdx, exitAccount := range exits { + for i := 0; i < nTx; i++ { + if !exits[i].exit { + continue + } + exitIdx := exits[i].idx + exitAccount := exits[i].acc + // 0. generate MerkleProof p, err := exitTree.GenerateCircomVerifierProof(exitIdx.BigInt(), nil) if err != nil { @@ -129,15 +165,33 @@ func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordin Balance: exitAccount.Balance, } exitInfos = append(exitInfos, ei) + + if s.zki != nil { + s.zki.TokenID2[i] = exitAccount.TokenID.BigInt() + s.zki.Nonce2[i] = exitAccount.Nonce.BigInt() + if babyjub.PointCoordSign(exitAccount.PublicKey.X) { + s.zki.Sign2[i] = big.NewInt(1) + } + s.zki.Ay2[i] = exitAccount.PublicKey.Y + s.zki.Balance2[i] = exitAccount.Balance + s.zki.EthAddr2[i] = common.EthAddrToBigInt(exitAccount.EthAddr) + s.zki.Siblings2[i] = p.Siblings + if exits[i].newExit { + s.zki.NewExit[i] = big.NewInt(1) + } + if p.IsOld0 { + s.zki.IsOld0_2[i] = big.NewInt(1) + } + s.zki.OldKey2[i] = p.OldKey.BigInt() + s.zki.OldValue2[i] = p.OldValue.BigInt() + } } if !cmpZKInputs { return nil, exitInfos, nil } // compute last ZKInputs parameters - s.zki.OldLastIdx = (s.idx - 1).BigInt() - s.zki.OldStateRoot = s.mt.Root().BigInt() - s.zki.GlobalChainID = big.NewInt(0) // TODO, 0: ethereum, get this from config file + 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 { @@ -146,9 +200,9 @@ func (s *StateDB) ProcessTxs(cmpExitTree, cmpZKInputs bool, l1usertxs, l1coordin s.zki.FeePlanTokens = tokenIDs // s.zki.ISInitStateRootFee = s.mt.Root().BigInt() - // compute fees - // once fees are computed + // TODO once the Node Config sets the Accounts where to send the Fees + // compute fees & update ZKInputs // return exitInfos, so Synchronizer will be able to store it into // HistoryDB for the concrete BatchNum @@ -181,8 +235,10 @@ func (s *StateDB) getTokenIDsBigInt(l1usertxs, l1coordinatortxs []*common.L1Tx, } // processL1Tx process the given L1Tx applying the needed updates to the -// StateDB depending on the transaction Type. -func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, error) { +// StateDB depending on the transaction Type. It returns the 3 parameters +// related to the Exit (in case of): Idx, ExitAccount, boolean determining if +// the Exit created a new Leaf in the ExitTree. +func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) (*common.Idx, *common.Account, bool, error) { // ZKInputs if s.zki != nil { // Txs @@ -195,7 +251,7 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) s.zki.LoadAmountF[s.i] = tx.LoadAmount s.zki.FromEthAddr[s.i] = common.EthAddrToBigInt(tx.FromEthAddr) if tx.FromBJJ != nil { - s.zki.FromBJJCompressed[s.i] = common.BJJCompressedTo256BigInts(tx.FromBJJ.Compress()) + s.zki.FromBJJCompressed[s.i] = BJJCompressedTo256BigInts(tx.FromBJJ.Compress()) } // Intermediate States @@ -208,13 +264,13 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) // & nonce err := s.applyTransfer(tx.Tx()) if err != nil { - return nil, nil, err + return nil, nil, false, err } case common.TxTypeCreateAccountDeposit: // add new account to the MT, update balance of the MT account err := s.applyCreateAccount(tx) if err != nil { - return nil, nil, err + return nil, nil, false, err } if s.zki != nil { @@ -225,25 +281,21 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) // update balance of the MT account err := s.applyDeposit(tx, false) if err != nil { - return nil, nil, err + return nil, nil, false, err } case common.TxTypeDepositTransfer: // update balance in MT account, update balance & nonce of sender // & receiver err := s.applyDeposit(tx, true) if err != nil { - return nil, nil, err + return nil, nil, false, err } case common.TxTypeCreateAccountDepositTransfer: // add new account to the merkletree, update balance in MT account, // update balance & nonce of sender & receiver - err := s.applyCreateAccount(tx) - if err != nil { - return nil, nil, err - } - err = s.applyTransfer(tx.Tx()) + err := s.applyCreateAccountDepositTransfer(tx) if err != nil { - return nil, nil, err + return nil, nil, false, err } if s.zki != nil { @@ -252,20 +304,22 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) } case common.TxTypeExit: // execute exit flow - exitAccount, err := s.applyExit(exitTree, tx.Tx()) + exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx()) if err != nil { - return nil, nil, err + return nil, nil, false, err } - return &tx.FromIdx, exitAccount, nil + return &tx.FromIdx, exitAccount, newExit, nil default: } - return nil, nil, nil + return nil, nil, false, nil } -// processL2Tx process the given L2Tx applying the needed updates to -// the StateDB depending on the transaction Type. -func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, error) { +// processL2Tx process the given L2Tx applying the needed updates to the +// StateDB depending on the transaction Type. It returns the 3 parameters +// related to the Exit (in case of): Idx, ExitAccount, boolean determining if +// the Exit created a new Leaf in the ExitTree. +func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, bool, error) { // ZKInputs if s.zki != nil { // Txs @@ -281,12 +335,12 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 if !bytes.Equal(tx.ToEthAddr.Bytes(), ffAddr.Bytes()) { idx = s.getIdxByEthAddr(tx.ToEthAddr) if idx == common.Idx(0) { - return nil, nil, fmt.Errorf("Idx can not be found for given tx.FromEthAddr") + return nil, nil, false, fmt.Errorf("Idx can not be found for given tx.FromEthAddr") } } else { idx = s.getIdxByBJJ(tx.ToBJJ) if idx == common.Idx(0) { - return nil, nil, fmt.Errorf("Idx can not be found for given tx.FromBJJ") + return nil, nil, false, fmt.Errorf("Idx can not be found for given tx.FromBJJ") } } s.zki.AuxToIdx[s.i] = idx.BigInt() @@ -298,7 +352,7 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 s.zki.NewAccount[s.i] = big.NewInt(0) // L2Txs - // s.zki.RqOffset[s.i] = // TODO + // s.zki.RqOffset[s.i] = // TODO Rq once TxSelector is ready // s.zki.RqTxCompressedDataV2[s.i] = // TODO // s.zki.RqToEthAddr[s.i] = common.EthAddrToBigInt(tx.RqToEthAddr) // TODO // s.zki.RqToBJJAy[s.i] = tx.ToBJJ.Y // TODO @@ -313,18 +367,18 @@ func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2 // balance & nonce err := s.applyTransfer(tx.Tx()) if err != nil { - return nil, nil, err + return nil, nil, false, err } case common.TxTypeExit: // execute exit flow - exitAccount, err := s.applyExit(exitTree, tx.Tx()) + exitAccount, newExit, err := s.applyExit(exitTree, tx.Tx()) if err != nil { - return nil, nil, err + return nil, nil, false, err } - return &tx.FromIdx, exitAccount, nil + return &tx.FromIdx, exitAccount, newExit, nil default: } - return nil, nil, nil + return nil, nil, false, nil } // applyCreateAccount creates a new account in the account of the depositer, it @@ -338,10 +392,26 @@ func (s *StateDB) applyCreateAccount(tx *common.L1Tx) error { EthAddr: tx.FromEthAddr, } - _, err := s.CreateAccount(common.Idx(s.idx+1), account) + p, err := s.CreateAccount(common.Idx(s.idx+1), account) if err != nil { return err } + if s.zki != nil { + s.zki.TokenID1[s.i] = tx.TokenID.BigInt() + s.zki.Nonce1[s.i] = big.NewInt(0) + if babyjub.PointCoordSign(tx.FromBJJ.X) { + s.zki.Sign1[s.i] = big.NewInt(1) + } + s.zki.Ay1[s.i] = tx.FromBJJ.Y + s.zki.Balance1[s.i] = tx.LoadAmount + s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(tx.FromEthAddr) + s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings) + if p.IsOld0 { + s.zki.IsOld0_1[s.i] = big.NewInt(1) + } + s.zki.OldKey1[s.i] = p.OldKey.BigInt() + s.zki.OldValue1[s.i] = p.OldValue.BigInt() + } s.idx = s.idx + 1 return s.setIdx(s.idx) @@ -359,8 +429,9 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) // in case that the tx is a L1Tx>DepositTransfer + var accReceiver *common.Account if transfer { - accReceiver, err := s.GetAccount(tx.ToIdx) + accReceiver, err = s.GetAccount(tx.ToIdx) if err != nil { return err } @@ -368,17 +439,46 @@ func (s *StateDB) applyDeposit(tx *common.L1Tx, transfer bool) error { accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) // add amount to the receiver accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) - // update receiver account in localStateDB - _, err = s.UpdateAccount(tx.ToIdx, accReceiver) - if err != nil { - return err - } } // update sender account in localStateDB - _, err = s.UpdateAccount(tx.FromIdx, accSender) + p, err := s.UpdateAccount(tx.FromIdx, accSender) if err != nil { return err } + if s.zki != nil { + s.zki.TokenID1[s.i] = accSender.TokenID.BigInt() + s.zki.Nonce1[s.i] = accSender.Nonce.BigInt() + if babyjub.PointCoordSign(accSender.PublicKey.X) { + s.zki.Sign1[s.i] = big.NewInt(1) + } + s.zki.Ay1[s.i] = accSender.PublicKey.Y + s.zki.Balance1[s.i] = accSender.Balance + s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(accSender.EthAddr) + s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings) + // IsOld0_1, OldKey1, OldValue1 not needed as this is not an insert + } + + // this is done after updating Sender Account (depositer) + if transfer { + // update receiver account in localStateDB + p, err := s.UpdateAccount(tx.ToIdx, accReceiver) + if err != nil { + return err + } + if s.zki != nil { + s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt() + s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt() + if babyjub.PointCoordSign(accReceiver.PublicKey.X) { + s.zki.Sign2[s.i] = big.NewInt(1) + } + s.zki.Ay2[s.i] = accReceiver.PublicKey.Y + s.zki.Balance2[s.i] = accReceiver.Balance + s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr) + s.zki.Siblings2[s.i] = siblingsToZKInputFormat(p.Siblings) + // IsOld0_2, OldKey2, OldValue2 not needed as this is not an insert + } + } + return nil } @@ -403,31 +503,130 @@ func (s *StateDB) applyTransfer(tx *common.Tx) error { // add amount to the receiver accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) - // update receiver account in localStateDB - _, err = s.UpdateAccount(tx.ToIdx, accReceiver) + // update sender account in localStateDB + pSender, err := s.UpdateAccount(tx.FromIdx, accSender) if err != nil { return err } - // update sender account in localStateDB - _, err = s.UpdateAccount(tx.FromIdx, accSender) + if s.zki != nil { + s.zki.TokenID1[s.i] = accSender.TokenID.BigInt() + s.zki.Nonce1[s.i] = accSender.Nonce.BigInt() + if babyjub.PointCoordSign(accSender.PublicKey.X) { + s.zki.Sign1[s.i] = big.NewInt(1) + } + s.zki.Ay1[s.i] = accSender.PublicKey.Y + s.zki.Balance1[s.i] = accSender.Balance + s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(accSender.EthAddr) + s.zki.Siblings1[s.i] = siblingsToZKInputFormat(pSender.Siblings) + } + + // update receiver account in localStateDB + pReceiver, err := s.UpdateAccount(tx.ToIdx, accReceiver) if err != nil { return err } + if s.zki != nil { + s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt() + s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt() + if babyjub.PointCoordSign(accReceiver.PublicKey.X) { + s.zki.Sign2[s.i] = big.NewInt(1) + } + s.zki.Ay2[s.i] = accReceiver.PublicKey.Y + s.zki.Balance2[s.i] = accReceiver.Balance + s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr) + s.zki.Siblings2[s.i] = siblingsToZKInputFormat(pReceiver.Siblings) + } return nil } -func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*common.Account, error) { +// applyCreateAccountDepositTransfer, in a single tx, creates a new account, +// makes a deposit, and performs a transfer to another account +func (s *StateDB) applyCreateAccountDepositTransfer(tx *common.L1Tx) error { + accSender := &common.Account{ + TokenID: tx.TokenID, + Nonce: 0, + Balance: tx.LoadAmount, + PublicKey: tx.FromBJJ, + EthAddr: tx.FromEthAddr, + } + accSender.Balance = new(big.Int).Add(accSender.Balance, tx.LoadAmount) + accReceiver, err := s.GetAccount(tx.ToIdx) + if err != nil { + return err + } + // subtract amount to the sender + accSender.Balance = new(big.Int).Sub(accSender.Balance, tx.Amount) + // add amount to the receiver + accReceiver.Balance = new(big.Int).Add(accReceiver.Balance, tx.Amount) + + // create Account of the Sender + p, err := s.CreateAccount(common.Idx(s.idx+1), accSender) + if err != nil { + return err + } + if s.zki != nil { + s.zki.TokenID1[s.i] = tx.TokenID.BigInt() + s.zki.Nonce1[s.i] = big.NewInt(0) + if babyjub.PointCoordSign(tx.FromBJJ.X) { + s.zki.Sign1[s.i] = big.NewInt(1) + } + s.zki.Ay1[s.i] = tx.FromBJJ.Y + s.zki.Balance1[s.i] = tx.LoadAmount + s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(tx.FromEthAddr) + s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings) + if p.IsOld0 { + s.zki.IsOld0_1[s.i] = big.NewInt(1) + } + s.zki.OldKey1[s.i] = p.OldKey.BigInt() + s.zki.OldValue1[s.i] = p.OldValue.BigInt() + } + + // update receiver account in localStateDB + p, err = s.UpdateAccount(tx.ToIdx, accReceiver) + if err != nil { + return err + } + if s.zki != nil { + s.zki.TokenID2[s.i] = accReceiver.TokenID.BigInt() + s.zki.Nonce2[s.i] = accReceiver.Nonce.BigInt() + if babyjub.PointCoordSign(accReceiver.PublicKey.X) { + s.zki.Sign2[s.i] = big.NewInt(1) + } + s.zki.Ay2[s.i] = accReceiver.PublicKey.Y + s.zki.Balance2[s.i] = accReceiver.Balance + s.zki.EthAddr2[s.i] = common.EthAddrToBigInt(accReceiver.EthAddr) + s.zki.Siblings2[s.i] = siblingsToZKInputFormat(p.Siblings) + } + + s.idx = s.idx + 1 + return s.setIdx(s.idx) +} + +// It returns the ExitAccount and a boolean determining if the Exit created a +// new Leaf in the ExitTree. +func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*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 := s.GetAccount(tx.FromIdx) if err != nil { - return nil, err + return nil, false, err } acc.Balance = new(big.Int).Sub(acc.Balance, tx.Amount) - _, err = s.UpdateAccount(tx.FromIdx, acc) + p, err := s.UpdateAccount(tx.FromIdx, acc) if err != nil { - return nil, err + return nil, false, err + } + if s.zki != nil { + s.zki.TokenID1[s.i] = acc.TokenID.BigInt() + s.zki.Nonce1[s.i] = acc.Nonce.BigInt() + if babyjub.PointCoordSign(acc.PublicKey.X) { + s.zki.Sign1[s.i] = big.NewInt(1) + } + s.zki.Ay1[s.i] = acc.PublicKey.Y + s.zki.Balance1[s.i] = acc.Balance + s.zki.EthAddr1[s.i] = common.EthAddrToBigInt(acc.EthAddr) + s.zki.Siblings1[s.i] = siblingsToZKInputFormat(p.Siblings) } exitAccount, err := getAccountInTreeDB(exitTree.DB(), tx.FromIdx) @@ -442,16 +641,16 @@ func (s *StateDB) applyExit(exitTree *merkletree.MerkleTree, tx *common.Tx) (*co EthAddr: acc.EthAddr, } _, err = createAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount) - return exitAccount, err + return exitAccount, true, err } else if err != nil { - return exitAccount, err + return exitAccount, false, err } // 1b. if idx already exist in exitTree: // update account, where account.Balance += exitAmount exitAccount.Balance = new(big.Int).Add(exitAccount.Balance, tx.Amount) _, err = updateAccountInTreeDB(exitTree.DB(), exitTree, tx.FromIdx, exitAccount) - return exitAccount, err + return exitAccount, false, err } // getIdx returns the stored Idx from the localStateDB, which is the last Idx diff --git a/db/statedb/utils.go b/db/statedb/utils.go index ce46240..7ba3d74 100644 --- a/db/statedb/utils.go +++ b/db/statedb/utils.go @@ -1,9 +1,12 @@ package statedb import ( + "math/big" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/iden3/go-merkletree" ) // TODO @@ -15,3 +18,28 @@ func (s *StateDB) getIdxByEthAddr(addr ethCommon.Address) common.Idx { func (s *StateDB) getIdxByBJJ(pk *babyjub.PublicKey) common.Idx { return common.Idx(0) } + +func siblingsToZKInputFormat(s []*merkletree.Hash) []*big.Int { + b := make([]*big.Int, len(s)) + for i := 0; i < len(s); i++ { + b[i] = s[i].BigInt() + } + return b +} + +// BJJCompressedTo256BigInts returns a [256]*big.Int array with the bit +// representation of the babyjub.PublicKeyComp +func BJJCompressedTo256BigInts(pkComp babyjub.PublicKeyComp) [256]*big.Int { + var r [256]*big.Int + b := pkComp[:] + + for i := 0; i < 256; i++ { + if b[i/8]&(1<<(i%8)) == 0 { + r[i] = big.NewInt(0) + } else { + r[i] = big.NewInt(1) + } + } + + return r +} diff --git a/common/utils_test.go b/db/statedb/utils_test.go similarity index 97% rename from common/utils_test.go rename to db/statedb/utils_test.go index e058de5..c4453ca 100644 --- a/common/utils_test.go +++ b/db/statedb/utils_test.go @@ -1,4 +1,4 @@ -package common +package statedb import ( "math/big"