Add HashGlobalInputs for ZKInputs

Add HashGlobalInputs for ZKInputs compatible with js & circom circuits version.

Compatible with hermeznetwork/commonjs at version: c6a1448db5bae4cda839ce36c1f35d8defccc9cd
(c6a1448db5)
This commit is contained in:
arnaucube
2020-11-09 14:03:13 +01:00
parent 4aa686797d
commit ce772b1d19
15 changed files with 501 additions and 52 deletions

View File

@@ -125,11 +125,13 @@ type Account struct {
func (a *Account) String() string {
buf := bytes.NewBufferString("")
fmt.Fprintf(buf, "Idx: %v, ", a.Idx)
fmt.Fprintf(buf, "PublicKey: %s..., ", a.PublicKey.String()[:10])
fmt.Fprintf(buf, "EthAddr: %s..., ", a.EthAddr.String()[:10])
fmt.Fprintf(buf, "TokenID: %v, ", a.TokenID)
fmt.Fprintf(buf, "Nonce: %d, ", a.Nonce)
fmt.Fprintf(buf, "Balance: %s, ", a.Balance.String())
fmt.Fprintf(buf, "BatchNum: %v, ", a.BatchNum)
return buf.String()
}

View File

@@ -153,9 +153,11 @@ func (tx L1Tx) Tx() Tx {
func (tx *L1Tx) BytesGeneric() ([]byte, error) {
var b [L1UserTxBytesLen]byte
copy(b[0:20], tx.FromEthAddr.Bytes())
pkCompL := tx.FromBJJ.Compress()
pkCompB := SwapEndianness(pkCompL[:])
copy(b[20:52], pkCompB[:])
if tx.FromBJJ != nil {
pkCompL := tx.FromBJJ.Compress()
pkCompB := SwapEndianness(pkCompL[:])
copy(b[20:52], pkCompB[:])
}
fromIdxBytes, err := tx.FromIdx.Bytes()
if err != nil {
return nil, err

View File

@@ -104,7 +104,7 @@ func L2TxsToPoolL2Txs(txs []L2Tx) []PoolL2Tx {
}
// Bytes encodes a L2Tx into []byte
func (tx *L2Tx) Bytes(nLevels int) ([]byte, error) {
func (tx L2Tx) Bytes(nLevels uint32) ([]byte, error) {
idxLen := nLevels / 8 //nolint:gomnd
b := make([]byte, ((nLevels*2)+16+8)/8) //nolint:gomnd

View File

@@ -1,9 +1,11 @@
package common
import (
"encoding/hex"
"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 +21,18 @@ func SwapEndianness(b []byte) []byte {
func EthAddrToBigInt(a ethCommon.Address) *big.Int {
return new(big.Int).SetBytes(a.Bytes())
}
// BJJFromStringWithChecksum parses a hex string in Hermez format (which has
// the Hermez checksum at the last byte, and is encoded in BigEndian) and
// returns the corresponding *babyjub.PublicKey. This method is not part of the
// spec, is used for importing javascript test vectors data.
func BJJFromStringWithChecksum(s string) (*babyjub.PublicKey, error) {
b, err := hex.DecodeString(s)
if err != nil {
return nil, err
}
pkBytes := SwapEndianness(b)
var pkComp babyjub.PublicKeyComp
copy(pkComp[:], pkBytes[:])
return pkComp.Decompress()
}

21
common/utils_test.go Normal file
View File

@@ -0,0 +1,21 @@
package common
import (
"encoding/hex"
"testing"
"github.com/stretchr/testify/assert"
)
func TestBJJFromStringWithChecksum(t *testing.T) {
s := "21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7"
pk, err := BJJFromStringWithChecksum(s)
assert.Nil(t, err)
sBytes, err := hex.DecodeString(s)
assert.Nil(t, err)
assert.Equal(t, hex.EncodeToString(SwapEndianness(sBytes)), pk.Compress().String())
// expected values computed with js implementation
assert.Equal(t, "2492816973395423007340226948038371729989170225696553239457870892535792679622", pk.X.String())
assert.Equal(t, "15238403086306505038849621710779816852318505119327426213168494964113886299863", pk.Y.String())
}

View File

@@ -3,10 +3,15 @@
package common
import (
"crypto/sha256"
"encoding/binary"
"encoding/json"
"fmt"
"math/big"
"github.com/hermeznetwork/hermez-node/log"
cryptoConstants "github.com/iden3/go-iden3-crypto/constants"
"github.com/iden3/go-merkletree"
"github.com/mitchellh/mapstructure"
)
@@ -21,6 +26,8 @@ type ZKMetadata struct {
MaxLevels uint32
// absolute maximum of L1 transaction allowed
MaxL1Tx uint32
// total txs allowed
MaxTx uint32
// Maximum number of Idxs where Fees can be send in a batch (currently
// is constant for all circuits: 64)
MaxFeeIdxs uint32
@@ -28,6 +35,10 @@ type ZKMetadata struct {
L1TxsData [][]byte
L2TxsData [][]byte
ChainID uint16
NewLastIdxRaw Idx
NewStateRootRaw *merkletree.Hash
NewExitRootRaw *merkletree.Hash
}
// ZKInputs represents the inputs that will be used to generate the zkSNARK proof
@@ -250,11 +261,14 @@ func (z ZKInputs) MarshalJSON() ([]byte, error) {
}
// NewZKInputs returns a pointer to an initialized struct of ZKInputs
func NewZKInputs(nTx, maxFeeIdxs, nLevels int) *ZKInputs {
func NewZKInputs(nTx, maxL1Tx, maxTx, maxFeeIdxs, nLevels uint32) *ZKInputs {
zki := &ZKInputs{}
zki.Metadata.NTx = uint32(nTx)
zki.Metadata.MaxFeeIdxs = uint32(maxFeeIdxs)
zki.Metadata.NLevels = uint32(nLevels)
zki.Metadata.NTx = nTx
zki.Metadata.MaxFeeIdxs = maxFeeIdxs
zki.Metadata.NLevels = nLevels
zki.Metadata.MaxLevels = uint32(48) //nolint:gomnd
zki.Metadata.MaxL1Tx = maxL1Tx
zki.Metadata.MaxTx = maxTx
// General
zki.OldLastIdx = big.NewInt(0)
@@ -359,10 +373,113 @@ func NewZKInputs(nTx, maxFeeIdxs, nLevels int) *ZKInputs {
// 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 {
func newSlice(n uint32) []*big.Int {
s := make([]*big.Int, n)
for i := 0; i < len(s); i++ {
s[i] = big.NewInt(0)
}
return s
}
// HashGlobalData returns the HashGlobalData
func (z ZKInputs) HashGlobalData() (*big.Int, error) {
b, err := z.ToHashGlobalData()
if err != nil {
return nil, err
}
h := sha256.New()
_, err = h.Write(b)
if err != nil {
return nil, err
}
r := new(big.Int).SetBytes(h.Sum(nil))
v := r.Mod(r, cryptoConstants.Q)
return v, nil
}
// ToHashGlobalData returns the data to be hashed in the method HashGlobalData
func (z ZKInputs) ToHashGlobalData() ([]byte, error) {
var b []byte
bytesMaxLevels := int(z.Metadata.MaxLevels / 8) //nolint:gomnd
// [MAX_NLEVELS bits] oldLastIdx
oldLastIdx := make([]byte, bytesMaxLevels)
copy(oldLastIdx, z.OldLastIdx.Bytes())
b = append(b, SwapEndianness(oldLastIdx)...)
// [MAX_NLEVELS bits] newLastIdx
newLastIdx := make([]byte, bytesMaxLevels)
newLastIdxBytes, err := z.Metadata.NewLastIdxRaw.Bytes()
if err != nil {
return nil, err
}
copy(newLastIdx, newLastIdxBytes[len(newLastIdxBytes)-bytesMaxLevels:])
b = append(b, newLastIdx...)
// [256 bits] oldStRoot
oldStateRoot := make([]byte, 32)
copy(oldStateRoot, z.OldStateRoot.Bytes())
b = append(b, oldStateRoot...)
// [256 bits] newStateRoot
newStateRoot := make([]byte, 32)
copy(newStateRoot, z.Metadata.NewStateRootRaw.Bytes())
b = append(b, newStateRoot...)
// [256 bits] newExitRoot
newExitRoot := make([]byte, 32)
copy(newExitRoot, z.Metadata.NewExitRootRaw.Bytes())
b = append(b, newExitRoot...)
// [MAX_L1_TX * (2 * MAX_NLEVELS + 480) bits] L1TxsData
l1TxDataLen := (2*z.Metadata.MaxLevels + 480)
l1TxsDataLen := (z.Metadata.MaxL1Tx * l1TxDataLen)
l1TxsData := make([]byte, l1TxsDataLen/8) //nolint:gomnd
for i := 0; i < len(z.Metadata.L1TxsData); i++ {
dataLen := int(l1TxDataLen) / 8 //nolint:gomnd
pos0 := i * dataLen
pos1 := i*dataLen + dataLen
copy(l1TxsData[pos0:pos1], z.Metadata.L1TxsData[i])
}
b = append(b, l1TxsData...)
// [MAX_TX*(2*NLevels + 24) bits] L2TxsData
var l2TxsData []byte
l2TxDataLen := 2*z.Metadata.NLevels + 24 //nolint:gomnd
l2TxsDataLen := (z.Metadata.MaxTx * l2TxDataLen)
expectedL2TxsDataLen := l2TxsDataLen / 8 //nolint:gomnd
for i := 0; i < len(z.Metadata.L2TxsData); i++ {
l2TxsData = append(l2TxsData, z.Metadata.L2TxsData[i]...)
}
if len(l2TxsData) > int(expectedL2TxsDataLen) {
return nil, fmt.Errorf("len(l2TxsData): %d, expected: %d", len(l2TxsData), expectedL2TxsDataLen)
}
l2TxsPadding := make([]byte, (int(z.Metadata.MaxTx)-len(z.Metadata.L2TxsData))*int(l2TxDataLen)/8) //nolint:gomnd
b = append(b, l2TxsPadding...)
b = append(b, l2TxsData...)
// [NLevels * MAX_TOKENS_FEE bits] feeTxsData
for i := 0; i < len(z.FeeIdxs); i++ {
var r []byte
padding := make([]byte, bytesMaxLevels/4) //nolint:gomnd
r = append(r, padding...)
feeIdx := make([]byte, bytesMaxLevels/2) //nolint:gomnd
feeIdxBytes := z.FeeIdxs[i].Bytes()
copy(feeIdx[len(feeIdx)-len(feeIdxBytes):], feeIdxBytes[:])
r = append(r, feeIdx...)
b = append(b, r...)
}
// [16 bits] chainID
var chainID [2]byte
binary.BigEndian.PutUint16(chainID[:], z.Metadata.ChainID)
b = append(b, chainID[:]...)
return b, nil
}

View File

@@ -8,7 +8,7 @@ import (
)
func TestZKInputs(t *testing.T) {
zki := NewZKInputs(100, 24, 32)
zki := NewZKInputs(100, 16, 512, 24, 32)
_, err := json.Marshal(zki)
require.Nil(t, err)
// fmt.Println(string(s))