From a7fe80f150e12d78565f0d4f627f2626db6ace76 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Tue, 8 Sep 2020 18:41:32 +0200 Subject: [PATCH 1/3] Refactor ZKInputs & add struct initialization Refactor ZKInputs & add struct initialization & add BJJCompressedTo256BigInts util --- common/token.go | 6 + common/utils.go | 18 +++ common/utils_test.go | 39 +++++ common/zk.go | 345 ++++++++++++++++++++++++++++++------------- common/zk_test.go | 15 ++ 5 files changed, 321 insertions(+), 102 deletions(-) create mode 100644 common/utils_test.go create mode 100644 common/zk_test.go diff --git a/common/token.go b/common/token.go index 22a60da..1888698 100644 --- a/common/token.go +++ b/common/token.go @@ -2,6 +2,7 @@ package common import ( "encoding/binary" + "math/big" "time" ethCommon "github.com/ethereum/go-ethereum/common" @@ -34,3 +35,8 @@ func (t TokenID) Bytes() []byte { binary.LittleEndian.PutUint32(tokenIDBytes[:], uint32(t)) return tokenIDBytes[:] } + +// BigInt returns the *big.Int representation of the TokenID +func (t TokenID) BigInt() *big.Int { + return big.NewInt(int64(t)) +} diff --git a/common/utils.go b/common/utils.go index 9dad220..3ec26be 100644 --- a/common/utils.go +++ b/common/utils.go @@ -4,6 +4,7 @@ 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. @@ -19,3 +20,20 @@ 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/utils_test.go b/common/utils_test.go new file mode 100644 index 0000000..e058de5 --- /dev/null +++ b/common/utils_test.go @@ -0,0 +1,39 @@ +package common + +import ( + "math/big" + "testing" + + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/stretchr/testify/assert" +) + +func TestBJJCompressedTo256BigInt(t *testing.T) { + var pkComp babyjub.PublicKeyComp + r := BJJCompressedTo256BigInts(pkComp) + zero := big.NewInt(0) + for i := 0; i < 256; i++ { + assert.Equal(t, zero, r[i]) + } + + pkComp[0] = 3 + r = BJJCompressedTo256BigInts(pkComp) + one := big.NewInt(1) + for i := 0; i < 256; i++ { + if i != 0 && i != 1 { + assert.Equal(t, zero, r[i]) + } else { + assert.Equal(t, one, r[i]) + } + } + + pkComp[31] = 4 + r = BJJCompressedTo256BigInts(pkComp) + for i := 0; i < 256; i++ { + if i != 0 && i != 1 && i != 250 { + assert.Equal(t, zero, r[i]) + } else { + assert.Equal(t, one, r[i]) + } + } +} diff --git a/common/zk.go b/common/zk.go index d7d6892..1b7d378 100644 --- a/common/zk.go +++ b/common/zk.go @@ -1,6 +1,6 @@ // 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 +//nolint:deadcode,structcheck,unused package common import "math/big" @@ -20,127 +20,268 @@ type maxFeeTx uint32 // ZKInputs represents the inputs that will be used to generate the zkSNARK proof type ZKInputs struct { + // + // General + // + // inputs for final `hashGlobalInputs` - // oldLastIdx is the last index assigned to an account - oldLastIdx *big.Int // uint64 (max nLevels bits) - // oldStateRoot is the current state merkle tree root - oldStateRoot *big.Int // Hash - // globalChainID is the blockchain ID (0 for Ethereum mainnet). This value can be get from the smart contract. - globalChainID *big.Int // uint16 - // feeIdxs is an array of merkle tree indexes where the coordinator will receive the accumulated fees - feeIdxs []*big.Int // uint64 (max nLevels bits), len: [maxFeeTx] + // OldLastIdx is the last index assigned to an account + OldLastIdx *big.Int // uint64 (max nLevels bits) + // OldStateRoot is the current state merkle tree root + OldStateRoot *big.Int // Hash + // GlobalChainID is the blockchain ID (0 for Ethereum mainnet). This value can be get from the smart contract. + GlobalChainID *big.Int // uint16 + // FeeIdxs is an array of merkle tree indexes where the coordinator will receive the accumulated fees + FeeIdxs []*big.Int // uint64 (max nLevels bits), len: [maxFeeTx] // accumulate fees - // feePlanTokens contains all the tokenIDs for which the fees are being accumulated - feePlanTokens []*big.Int // uint32 (max 32 bits), len: [maxFeeTx] + // FeePlanTokens contains all the tokenIDs for which the fees are being accumulated + FeePlanTokens []*big.Int // uint32 (max 32 bits), len: [maxFeeTx] - // Intermediary States to parallelize witness computation - // decode-tx - // imOnChain indicates if tx is L1 (true) or L2 (false) - imOnChain []*big.Int // bool, len: [nTx - 1] - // imOutIdx current index account for each Tx - imOutIdx []*big.Int // uint64 (max nLevels bits), len: [nTx - 1] - // rollup-tx - // imStateRoot root at the moment of the Tx, the state root value once the Tx is processed into the state tree - imStateRoot []*big.Int // Hash, len: [nTx - 1] - // imExitTree root at the moment of the Tx the value once the Tx is processed into the exit tree - imExitRoot []*big.Int // Hash, len: [nTx - 1] - // imAccFeeOut accumulated fees once the Tx is processed - imAccFeeOut [][]*big.Int // big.Int, len: [nTx - 1][maxFeeTx] - // fee-tx - // imStateRootFee root at the moment of the Tx, the state root value once the Tx is processed into the state tree - imStateRootFee []*big.Int // Hash, len: [maxFeeTx - 1] - // imInitStateRootFee state root once all L1-L2 tx are processed (before computing the fees-tx) - imInitStateRootFee *big.Int // Hash - // imFinalAccFee final accumulated fees (before computing the fees-tx) - imFinalAccFee []*big.Int // big.Int, len: [maxFeeTx - 1] + // + // Txs (L1&L2) + // // transaction L1-L2 - // txCompressedData - txCompressedData []*big.Int // big.Int (max 251 bits), len: [nTx] - // txCompressedDataV2 - txCompressedDataV2 []*big.Int // big.Int (max 193 bits), len: [nTx] - // fromIdx - fromIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] - // auxFromIdx is the Idx of the new created account which is consequence of a L1CreateAccountTx - auxFromIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] - - // toIdx - toIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] - // auxToIdx is the Idx of the Tx that has 'toIdx==0', is the coordinator who will find which Idx corresponds to the 'toBjjAy' or 'toEthAddr' - auxToIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] - // toBjjAy - toBjjAy []*big.Int // big.Int, len: [nTx] - // toEthAddr - toEthAddr []*big.Int // ethCommon.Address, len: [nTx] - - // onChain determines if is L1 (1/true) or L2 (0/false) - onChain []*big.Int // bool, len: [nTx] - // newAccount boolean (0/1) flag to set L1 tx creates a new account - newAccount []*big.Int // bool, len: [nTx] - // rqOffset relative transaction position to be linked. Used to perform atomic transactions. - rqOffset []*big.Int // uint8 (max 3 bits), len: [nTx] + // TxCompressedData + TxCompressedData []*big.Int // big.Int (max 251 bits), len: [nTx] + // TxCompressedDataV2, only used in L2Txs, in L1Txs is set to 0 + TxCompressedDataV2 []*big.Int // big.Int (max 193 bits), len: [nTx] + + // FromIdx + FromIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] + // AuxFromIdx is the Idx of the new created account which is consequence of a L1CreateAccountTx + AuxFromIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] + + // ToIdx + ToIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] + // AuxToIdx is the Idx of the Tx that has 'toIdx==0', is the coordinator who will find which Idx corresponds to the 'toBJJAy' or 'toEthAddr' + AuxToIdx []*big.Int // uint64 (max nLevels bits), len: [nTx] + // ToBJJAy + ToBJJAy []*big.Int // big.Int, len: [nTx] + // ToEthAddr + ToEthAddr []*big.Int // ethCommon.Address, len: [nTx] + + // 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 + // LoadAmountF encoded as float16 + LoadAmountF []*big.Int // uint16, len: [nTx] + // FromEthAddr + FromEthAddr []*big.Int // ethCommon.Address, len: [nTx] + // FromBJJCompressed boolean encoded where each value is a *big.Int + FromBJJCompressed [][256]*big.Int // bool array, len: [nTx][256] + + // + // Txs/L2Txs + // + + // RqOffset relative transaction position to be linked. Used to perform atomic transactions. + RqOffset []*big.Int // uint8 (max 3 bits), len: [nTx] // transaction L2 request data - // rqTxCompressedDataV2 - rqTxCompressedDataV2 []*big.Int // big.Int (max 251 bits), len: [nTx] - // rqToEthAddr - rqToEthAddr []*big.Int // ethCommon.Address, len: [nTx] - // rqToBjjAy - rqToBjjAy []*big.Int // big.Int, len: [nTx] + // RqTxCompressedDataV2 + RqTxCompressedDataV2 []*big.Int // big.Int (max 251 bits), len: [nTx] + // RqToEthAddr + RqToEthAddr []*big.Int // ethCommon.Address, len: [nTx] + // RqToBJJAy + RqToBJJAy []*big.Int // big.Int, len: [nTx] // transaction L2 signature - // s - s []*big.Int // big.Int, len: [nTx] - // r8x - r8x []*big.Int // big.Int, len: [nTx] - // r8y - r8y []*big.Int // big.Int, len: [nTx] + // S + S []*big.Int // big.Int, len: [nTx] + // R8x + R8x []*big.Int // big.Int, len: [nTx] + // R8y + R8y []*big.Int // big.Int, len: [nTx] - // transaction L1 - // loadAmountF encoded as float16 - loadAmountF []*big.Int // uint16, len: [nTx] - // fromEthAddr - fromEthAddr []*big.Int // ethCommon.Address, len: [nTx] - // fromBjjCompressed boolean encoded where each value is a *big.Int - fromBjjCompressed [][]*big.Int // bool array, len: [nTx][256] + // + // State MerkleTree Leafs transitions + // // state 1, value of the sender (from) account leaf - 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] - ethAddr1 []*big.Int // ethCommon.Address, len: [nTx] - siblings1 [][]*big.Int // big.Int, len: [nTx][nLevels + 1] + 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] + 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) - isOld0_1 []*big.Int // bool, len: [nTx] - oldKey1 []*big.Int // uint64 (max 40 bits), len: [nTx] - oldValue1 []*big.Int // Hash, len: [nTx] + IsOld0_1 []*big.Int // bool, len: [nTx] + OldKey1 []*big.Int // uint64 (max 40 bits), len: [nTx] + OldValue1 []*big.Int // Hash, len: [nTx] // state 2, value of the receiver (to) account leaf - 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] - ethAddr2 []*big.Int // ethCommon.Address, len: [nTx] - siblings2 [][]*big.Int // big.Int, len: [nTx][nLevels + 1] + // if Tx is an Exit, state 2 is used for the Exit Merkle Proof + 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] + 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 - newExit []*big.Int // bool, len: [nTx] + NewExit []*big.Int // bool, len: [nTx] // Required for inserts and deletes, values of the CircomProcessorProof (smt insert proof) - isOld0_2 []*big.Int // bool, len: [nTx] - oldKey2 []*big.Int // uint64 (max 40 bits), len: [nTx] - oldValue2 []*big.Int // Hash, len: [nTx] + IsOld0_2 []*big.Int // bool, len: [nTx] + OldKey2 []*big.Int // uint64 (max 40 bits), len: [nTx] + OldValue2 []*big.Int // Hash, len: [nTx] // state 3, value of the account leaf receiver of the Fees // fee tx // State fees - 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] - ethAddr3 []*big.Int // ethCommon.Address, len: [maxFeeTx] - siblings3 [][]*big.Int // Hash, len: [maxFeeTx][nLevels + 1] + 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] + EthAddr3 []*big.Int // ethCommon.Address, len: [maxFeeTx] + Siblings3 [][]*big.Int // Hash, len: [maxFeeTx][nLevels + 1] + + // + // Intermediate States + // + + // Intermediate States to parallelize witness computation + // decode-tx + // ISOnChain indicates if tx is L1 (true) or L2 (false) + ISOnChain []*big.Int // bool, len: [nTx - 1] + // ISOutIdx current index account for each Tx + ISOutIdx []*big.Int // 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 []*big.Int // Hash, len: [nTx - 1] + // ISExitTree root at the moment of the Tx the value once the Tx is processed into the exit tree + ISExitRoot []*big.Int // Hash, len: [nTx - 1] + // ISAccFeeOut accumulated fees once the Tx is processed + ISAccFeeOut [][]*big.Int // big.Int, len: [nTx - 1][maxFeeTx] + // 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 // Hash, len: [maxFeeTx - 1] + // ISInitStateRootFee state root once all L1-L2 tx are processed (before computing the fees-tx) + ISInitStateRootFee *big.Int // Hash + // ISFinalAccFee final accumulated fees (before computing the fees-tx) + ISFinalAccFee []*big.Int // big.Int, len: [maxFeeTx - 1] +} + +// NewZKInputs returns a pointer to an initialized struct of ZKInputs +func NewZKInputs(nTx, maxFeeTx, nLevels int) *ZKInputs { + zki := &ZKInputs{} + + // General + zki.OldLastIdx = big.NewInt(0) + zki.OldStateRoot = big.NewInt(0) + zki.GlobalChainID = big.NewInt(0) + zki.FeeIdxs = newSlice(maxFeeTx) + zki.FeePlanTokens = newSlice(maxFeeTx) + + // Txs + zki.TxCompressedData = newSlice(nTx) + zki.TxCompressedDataV2 = newSlice(nTx) + zki.FromIdx = newSlice(nTx) + zki.AuxFromIdx = newSlice(nTx) + zki.ToIdx = newSlice(nTx) + zki.AuxToIdx = newSlice(nTx) + zki.ToBJJAy = newSlice(nTx) + zki.ToEthAddr = newSlice(nTx) + zki.OnChain = newSlice(nTx) + zki.NewAccount = newSlice(nTx) + + // L1 + zki.LoadAmountF = newSlice(nTx) + zki.FromEthAddr = newSlice(nTx) + zki.FromBJJCompressed = make([][256]*big.Int, nTx) + for i := 0; i < len(zki.FromBJJCompressed); i++ { + // zki.FromBJJCompressed[i] = newSlice(256) + for j := 0; j < 256; j++ { + zki.FromBJJCompressed[i][j] = big.NewInt(0) + } + } + + // L2 + zki.RqOffset = newSlice(nTx) + zki.RqTxCompressedDataV2 = newSlice(nTx) + zki.RqToEthAddr = newSlice(nTx) + zki.RqToBJJAy = newSlice(nTx) + zki.S = newSlice(nTx) + zki.R8x = newSlice(nTx) + zki.R8y = newSlice(nTx) + + // State MerkleTree Leafs transitions + zki.TokenID1 = newSlice(nTx) + zki.Nonce1 = newSlice(nTx) + zki.Sign1 = newSlice(nTx) + zki.Balance1 = newSlice(nTx) + zki.Ay1 = newSlice(nTx) + zki.EthAddr1 = newSlice(nTx) + zki.Siblings1 = make([][]*big.Int, nTx) + for i := 0; i < len(zki.Siblings1); i++ { + zki.Siblings1[i] = newSlice(nLevels + 1) + } + zki.IsOld0_1 = newSlice(nTx) + zki.OldKey1 = newSlice(nTx) + zki.OldValue1 = newSlice(nTx) + + zki.TokenID2 = newSlice(nTx) + zki.Nonce2 = newSlice(nTx) + zki.Sign2 = newSlice(nTx) + zki.Balance2 = newSlice(nTx) + zki.Ay2 = newSlice(nTx) + zki.EthAddr2 = newSlice(nTx) + zki.Siblings2 = make([][]*big.Int, nTx) + for i := 0; i < len(zki.Siblings2); i++ { + zki.Siblings2[i] = newSlice(nLevels + 1) + } + zki.NewExit = newSlice(nTx) + zki.IsOld0_2 = newSlice(nTx) + zki.OldKey2 = newSlice(nTx) + zki.OldValue2 = newSlice(nTx) + + zki.TokenID3 = newSlice(maxFeeTx) + zki.Nonce3 = newSlice(maxFeeTx) + zki.Sign3 = newSlice(maxFeeTx) + zki.Balance3 = newSlice(maxFeeTx) + zki.Ay3 = newSlice(maxFeeTx) + zki.EthAddr3 = newSlice(maxFeeTx) + zki.Siblings3 = make([][]*big.Int, maxFeeTx) + for i := 0; i < len(zki.Siblings3); i++ { + zki.Siblings3[i] = newSlice(nLevels + 1) + } + + // Intermediate States + zki.ISOnChain = newSlice(nTx - 1) + zki.ISOutIdx = newSlice(nTx - 1) + zki.ISStateRoot = newSlice(nTx - 1) + 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.ISStateRootFee = newSlice(maxFeeTx - 1) + zki.ISInitStateRootFee = big.NewInt(0) + zki.ISFinalAccFee = newSlice(maxFeeTx - 1) + + return zki +} + +// newSlice returns a []*big.Int slice of length n with values initialized at +// 0. +// Is used to initialize all *big.Ints of the ZKInputs data structure, so when +// the transactions are processed and the ZKInputs filled, there is no need to +// set all the elements, and if a transaction does not use a parameter, can be +// leaved as it is in the ZKInputs, as will be 0, so later when using the +// ZKInputs to generate the zkSnark proof there is no 'nil'/'null' values. +func newSlice(n int) []*big.Int { + s := make([]*big.Int, n) + for i := 0; i < len(s); i++ { + s[i] = big.NewInt(0) + } + return s } diff --git a/common/zk_test.go b/common/zk_test.go new file mode 100644 index 0000000..8767838 --- /dev/null +++ b/common/zk_test.go @@ -0,0 +1,15 @@ +package common + +import ( + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestZKInputs(t *testing.T) { + zki := NewZKInputs(100, 24, 32) + _, err := json.Marshal(zki) + require.Nil(t, err) + // fmt.Println(string(s)) +} From aa0bde61d223fd2894f6a2256c0b6272a6b5f1d8 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 9 Sep 2020 14:40:55 +0200 Subject: [PATCH 2/3] Add StateDB ZKInputs generation for L1 & L2 Txs --- batchbuilder/batchbuilder.go | 4 +- common/l2tx.go | 25 +++++ coordinator/coordinator.go | 3 +- db/statedb/statedb.go | 2 + db/statedb/txprocessors.go | 167 ++++++++++++++++++++++++++++++-- db/statedb/txprocessors_test.go | 47 +++++++-- db/statedb/utils.go | 17 ++++ 7 files changed, 245 insertions(+), 20 deletions(-) create mode 100644 db/statedb/utils.go diff --git a/batchbuilder/batchbuilder.go b/batchbuilder/batchbuilder.go index 0f864e5..35c5ad3 100644 --- a/batchbuilder/batchbuilder.go +++ b/batchbuilder/batchbuilder.go @@ -51,8 +51,8 @@ func (bb *BatchBuilder) Reset(batchNum common.BatchNum, fromSynchronizer bool) e } // BuildBatch takes the transactions and returns the common.ZKInputs of the next batch -func (bb *BatchBuilder) BuildBatch(configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.L2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { - zkInputs, _, err := bb.localStateDB.ProcessTxs(false, l1usertxs, l1coordinatortxs, l2txs) +func (bb *BatchBuilder) BuildBatch(configBatch *ConfigBatch, l1usertxs, l1coordinatortxs []*common.L1Tx, pooll2txs []*common.PoolL2Tx, tokenIDs []common.TokenID) (*common.ZKInputs, error) { + zkInputs, _, err := bb.localStateDB.ProcessTxs(false, true, l1usertxs, l1coordinatortxs, pooll2txs) if err != nil { return nil, err } diff --git a/common/l2tx.go b/common/l2tx.go index 44a0283..3d49694 100644 --- a/common/l2tx.go +++ b/common/l2tx.go @@ -30,3 +30,28 @@ func (tx *L2Tx) Tx() *Tx { Type: tx.Type, } } + +// PoolL2Tx returns the data structure of PoolL2Tx with the parameters of a +// L2Tx filled +func (tx *L2Tx) PoolL2Tx() *PoolL2Tx { + return &PoolL2Tx{ + TxID: tx.TxID, + BatchNum: tx.BatchNum, + FromIdx: tx.FromIdx, + ToIdx: tx.ToIdx, + Amount: tx.Amount, + Fee: tx.Fee, + Nonce: tx.Nonce, + Type: tx.Type, + } +} + +// L2TxsToPoolL2Txs returns an array of []*PoolL2Tx from an array of []*L2Tx, +// where the PoolL2Tx only have the parameters of a L2Tx filled. +func L2TxsToPoolL2Txs(txs []*L2Tx) []*PoolL2Tx { + var r []*PoolL2Tx + for _, tx := range txs { + r = append(r, tx.PoolL2Tx()) + } + return r +} diff --git a/coordinator/coordinator.go b/coordinator/coordinator.go index a2d1d1b..85b6a98 100644 --- a/coordinator/coordinator.go +++ b/coordinator/coordinator.go @@ -221,8 +221,7 @@ func (c *Coordinator) forgeSequence() error { configBatch := &batchbuilder.ConfigBatch{ ForgerAddress: c.config.ForgerAddress, } - l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs) - zkInputs, err := c.batchBuilder.BuildBatch(configBatch, l1UserTxsExtra, l1OperatorTxs, l2Txs, nil) // TODO []common.TokenID --> feesInfo + zkInputs, err := c.batchBuilder.BuildBatch(configBatch, l1UserTxsExtra, l1OperatorTxs, poolL2Txs, nil) // TODO []common.TokenID --> feesInfo if err != nil { return err } diff --git a/db/statedb/statedb.go b/db/statedb/statedb.go index 5d69494..e5e83ff 100644 --- a/db/statedb/statedb.go +++ b/db/statedb/statedb.go @@ -43,6 +43,8 @@ type StateDB struct { mt *merkletree.MerkleTree // idx holds the current Idx that the BatchBuilder is using idx common.Idx + zki *common.ZKInputs + i int // i is used for 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 280942c..d3ea768 100644 --- a/db/statedb/txprocessors.go +++ b/db/statedb/txprocessors.go @@ -1,8 +1,12 @@ package statedb import ( + "bytes" + "errors" + "fmt" "math/big" + ethCommon "github.com/ethereum/go-ethereum/common" "github.com/hermeznetwork/hermez-node/common" "github.com/iden3/go-iden3-crypto/poseidon" "github.com/iden3/go-merkletree" @@ -10,19 +14,42 @@ import ( "github.com/iden3/go-merkletree/db/memory" ) -// keyidx is used as key in the db to store the current Idx -var keyidx = []byte("idx") +var ( + // keyidx is used as key in the db to store the current Idx + keyidx = []byte("idx") + + ffAddr = ethCommon.HexToAddress("0xffffffffffffffffffffffffffffffffffffffff") +) + +func (s *StateDB) resetZKInputs() { + s.zki = nil + s.i = 0 +} // 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 // cmpExitTree is set to true, returns common.ExitTreeLeaf that is later used // by the Synchronizer to update the HistoryDB. -func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*common.L1Tx, l2txs []*common.L2Tx) (*common.ZKInputs, []*common.ExitInfo, error) { +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") + } + 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 + } + + if cmpZKInputs { + s.zki = common.NewZKInputs(nTx, 24, 32) // TODO this values will be parameters of the function + } + // TBD if ExitTree is only in memory or stored in disk, for the moment // only needed in memory exitTree, err = merkletree.NewMerkleTree(memory.NewMemoryStorage(), s.mt.MaxLevels()) @@ -30,7 +57,8 @@ func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*co return nil, nil, err } - for _, tx := range l1coordinatortxs { + // assumption: l1usertx are sorted by L1Tx.Position + for _, tx := range l1usertxs { exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx) if err != nil { return nil, nil, err @@ -38,8 +66,11 @@ func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*co if exitIdx != nil && cmpExitTree { exits[*exitIdx] = *exitAccount } + if s.zki != nil { + s.i++ + } } - for _, tx := range l1usertxs { + for _, tx := range l1coordinatortxs { exitIdx, exitAccount, err := s.processL1Tx(exitTree, tx) if err != nil { return nil, nil, err @@ -47,6 +78,9 @@ func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*co if exitIdx != nil && cmpExitTree { exits[*exitIdx] = *exitAccount } + if s.zki != nil { + s.i++ + } } for _, tx := range l2txs { exitIdx, exitAccount, err := s.processL2Tx(exitTree, tx) @@ -56,9 +90,12 @@ func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*co if exitIdx != nil && cmpExitTree { exits[*exitIdx] = *exitAccount } + if s.zki != nil { + s.i++ + } } - if !cmpExitTree { + if !cmpExitTree && !cmpZKInputs { return nil, nil, nil } @@ -93,15 +130,78 @@ func (s *StateDB) ProcessTxs(cmpExitTree bool, l1usertxs, l1coordinatortxs []*co } exitInfos = append(exitInfos, ei) } + 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 + // zki.FeeIdxs = ? // TODO, this will be get from the config file + tokenIDs, err := s.getTokenIDsBigInt(l1usertxs, l1coordinatortxs, l2txs) + if err != nil { + return nil, nil, err + } + s.zki.FeePlanTokens = tokenIDs + + // s.zki.ISInitStateRootFee = s.mt.Root().BigInt() + // compute fees + + // once fees are computed // return exitInfos, so Synchronizer will be able to store it into // HistoryDB for the concrete BatchNum - return nil, exitInfos, nil + return s.zki, exitInfos, 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 + } + for i := 0; i < len(l2txs); i++ { + // as L2Tx does not have parameter TokenID, get it from the + // AccountsDB (in the StateDB) + acc, err := s.GetAccount(l2txs[i].ToIdx) + if err != nil { + return nil, err + } + tokenIDs[acc.TokenID] = true + } + var tBI []*big.Int + for t := range tokenIDs { + tBI = append(tBI, t.BigInt()) + } + return tBI, nil } // 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) { + // ZKInputs + if s.zki != nil { + // Txs + // s.zki.TxCompressedData[s.i] = tx.TxCompressedData() // uncomment once L1Tx.TxCompressedData is ready + s.zki.FromIdx[s.i] = tx.FromIdx.BigInt() + s.zki.ToIdx[s.i] = tx.ToIdx.BigInt() + s.zki.OnChain[s.i] = big.NewInt(1) + + // L1Txs + 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()) + } + + // Intermediate States + s.zki.ISOnChain[s.i] = big.NewInt(1) + } + switch tx.Type { case common.TxTypeForceTransfer, common.TxTypeTransfer: // go to the MT account of sender and receiver, and update balance @@ -116,6 +216,11 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) if err != nil { return nil, 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.TxTypeDeposit: // update balance of the MT account err := s.applyDeposit(tx, false) @@ -140,6 +245,11 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) if err != nil { return nil, 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.TxTypeExit: // execute exit flow exitAccount, err := s.applyExit(exitTree, tx.Tx()) @@ -155,7 +265,48 @@ func (s *StateDB) processL1Tx(exitTree *merkletree.MerkleTree, tx *common.L1Tx) // 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.L2Tx) (*common.Idx, *common.Account, error) { +func (s *StateDB) processL2Tx(exitTree *merkletree.MerkleTree, tx *common.PoolL2Tx) (*common.Idx, *common.Account, error) { + // 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.FromIdx[s.i] = tx.FromIdx.BigInt() + s.zki.ToIdx[s.i] = tx.ToIdx.BigInt() + + // fill AuxToIdx if needed + if tx.ToIdx == common.Idx(0) { + // Idx not set in the Tx, get it from DB through ToEthAddr or ToBJJ + var idx common.Idx + 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") + } + } 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") + } + } + s.zki.AuxToIdx[s.i] = idx.BigInt() + } + s.zki.ToBJJAy[s.i] = tx.ToBJJ.Y + s.zki.ToEthAddr[s.i] = common.EthAddrToBigInt(tx.ToEthAddr) + + s.zki.OnChain[s.i] = big.NewInt(0) + s.zki.NewAccount[s.i] = big.NewInt(0) + + // L2Txs + // s.zki.RqOffset[s.i] = // TODO + // 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 + s.zki.S[s.i] = tx.Signature.S + s.zki.R8x[s.i] = tx.Signature.R8.X + s.zki.R8y[s.i] = tx.Signature.R8.Y + } + switch tx.Type { case common.TxTypeTransfer: // go to the MT account of sender and receiver, and update diff --git a/db/statedb/txprocessors_test.go b/db/statedb/txprocessors_test.go index 5a9dd06..3e95d92 100644 --- a/db/statedb/txprocessors_test.go +++ b/db/statedb/txprocessors_test.go @@ -1,6 +1,8 @@ package statedb import ( + "encoding/json" + "fmt" "io/ioutil" "strings" "testing" @@ -11,6 +13,8 @@ import ( "github.com/stretchr/testify/require" ) +var debug = false + func TestProcessTxs(t *testing.T) { dir, err := ioutil.TempDir("", "tmpdb") require.Nil(t, err) @@ -30,9 +34,9 @@ func TestProcessTxs(t *testing.T) { // iterate for each batch for i := 0; i < len(l1Txs); i++ { - l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs[i]) + // l2Txs := common.PoolL2TxsToL2Txs(poolL2Txs[i]) - _, _, err := sdb.ProcessTxs(true, l1Txs[i], coordinatorL1Txs[i], l2Txs) + _, _, err := sdb.ProcessTxs(true, true, l1Txs[i], coordinatorL1Txs[i], poolL2Txs[i]) require.Nil(t, err) } @@ -65,8 +69,8 @@ func TestProcessTxsBatchByBatch(t *testing.T) { assert.Equal(t, 7, len(poolL2Txs[2])) // use first batch - l2txs := common.PoolL2TxsToL2Txs(poolL2Txs[0]) - _, exitInfos, err := sdb.ProcessTxs(true, l1Txs[0], coordinatorL1Txs[0], l2txs) + // l2txs := common.PoolL2TxsToL2Txs(poolL2Txs[0]) + _, exitInfos, err := sdb.ProcessTxs(true, true, l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) require.Nil(t, err) assert.Equal(t, 0, len(exitInfos)) acc, err := sdb.GetAccount(common.Idx(1)) @@ -74,8 +78,8 @@ func TestProcessTxsBatchByBatch(t *testing.T) { assert.Equal(t, "28", acc.Balance.String()) // use second batch - l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[1]) - _, exitInfos, err = sdb.ProcessTxs(true, l1Txs[1], coordinatorL1Txs[1], l2txs) + // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[1]) + _, exitInfos, err = sdb.ProcessTxs(true, true, l1Txs[1], coordinatorL1Txs[1], poolL2Txs[1]) require.Nil(t, err) assert.Equal(t, 5, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(1)) @@ -83,11 +87,38 @@ func TestProcessTxsBatchByBatch(t *testing.T) { assert.Equal(t, "48", acc.Balance.String()) // use third batch - l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[2]) - _, exitInfos, err = sdb.ProcessTxs(true, l1Txs[2], coordinatorL1Txs[2], l2txs) + // l2txs = common.PoolL2TxsToL2Txs(poolL2Txs[2]) + _, exitInfos, err = sdb.ProcessTxs(true, true, l1Txs[2], coordinatorL1Txs[2], poolL2Txs[2]) require.Nil(t, err) assert.Equal(t, 1, len(exitInfos)) acc, err = sdb.GetAccount(common.Idx(1)) assert.Nil(t, err) assert.Equal(t, "23", acc.Balance.String()) } + +func TestZKInputsGeneration(t *testing.T) { + dir, err := ioutil.TempDir("", "tmpdb") + require.Nil(t, err) + + sdb, err := NewStateDB(dir, true, 32) + assert.Nil(t, err) + + // generate test transactions from test.SetTest0 code + parser := test.NewParser(strings.NewReader(test.SetTest0)) + instructions, err := parser.Parse() + assert.Nil(t, err) + + l1Txs, coordinatorL1Txs, poolL2Txs := test.GenerateTestTxs(t, instructions) + assert.Equal(t, 29, len(l1Txs[0])) + assert.Equal(t, 0, len(coordinatorL1Txs[0])) + assert.Equal(t, 21, len(poolL2Txs[0])) + + zki, _, err := sdb.ProcessTxs(false, true, l1Txs[0], coordinatorL1Txs[0], poolL2Txs[0]) + require.Nil(t, err) + + s, err := json.Marshal(zki) + require.Nil(t, err) + if debug { + fmt.Println(string(s)) + } +} diff --git a/db/statedb/utils.go b/db/statedb/utils.go new file mode 100644 index 0000000..ce46240 --- /dev/null +++ b/db/statedb/utils.go @@ -0,0 +1,17 @@ +package statedb + +import ( + ethCommon "github.com/ethereum/go-ethereum/common" + "github.com/hermeznetwork/hermez-node/common" + "github.com/iden3/go-iden3-crypto/babyjub" +) + +// TODO +func (s *StateDB) getIdxByEthAddr(addr ethCommon.Address) common.Idx { + return common.Idx(0) +} + +// TODO +func (s *StateDB) getIdxByBJJ(pk *babyjub.PublicKey) common.Idx { + return common.Idx(0) +} From 69fe471f112da52b07ab2dc5f33f46c3259dcfb6 Mon Sep 17 00:00:00 2001 From: arnaucube Date: Wed, 9 Sep 2020 19:21:09 +0200 Subject: [PATCH 3/3] 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"