From def6f46d75820ff584a4bd66e8671df9349b16ad Mon Sep 17 00:00:00 2001 From: arnaucube Date: Mon, 9 Nov 2020 19:22:38 +0100 Subject: [PATCH] Update Account Hash comp to match js version --- common/account.go | 63 +++++++++-------- common/account_test.go | 124 +++++++++++++++++++++++++++++++-- test/debugapi/debugapi_test.go | 2 +- 3 files changed, 155 insertions(+), 34 deletions(-) diff --git a/common/account.go b/common/account.go index 092ecc3..4247f6b 100644 --- a/common/account.go +++ b/common/account.go @@ -152,14 +152,20 @@ func (a *Account) Bytes() ([32 * NLeafElems]byte, error) { return b, err } - copy(b[0:4], a.TokenID.Bytes()) - copy(b[4:9], nonceBytes[:]) + copy(b[28:32], a.TokenID.Bytes()) + copy(b[23:28], nonceBytes[:]) + + if a.PublicKey == nil { + return b, fmt.Errorf("Account.PublicKey can not be nil") + } if babyjub.PointCoordSign(a.PublicKey.X) { - b[10] = 1 + b[22] = 1 } - copy(b[32:64], SwapEndianness(a.Balance.Bytes())) - copy(b[64:96], SwapEndianness(a.PublicKey.Y.Bytes())) - copy(b[96:116], a.EthAddr.Bytes()) + balanceBytes := a.Balance.Bytes() + copy(b[64-len(balanceBytes):64], balanceBytes) + ayBytes := a.PublicKey.Y.Bytes() + copy(b[96-len(ayBytes):96], ayBytes) + copy(b[108:128], a.EthAddr.Bytes()) return b, nil } @@ -173,26 +179,21 @@ func (a *Account) BigInts() ([NLeafElems]*big.Int, error) { return e, err } - e[0] = new(big.Int).SetBytes(SwapEndianness(b[0:32])) - e[1] = new(big.Int).SetBytes(SwapEndianness(b[32:64])) - e[2] = new(big.Int).SetBytes(SwapEndianness(b[64:96])) - e[3] = new(big.Int).SetBytes(SwapEndianness(b[96:128])) + e[0] = new(big.Int).SetBytes(b[0:32]) + e[1] = new(big.Int).SetBytes(b[32:64]) + e[2] = new(big.Int).SetBytes(b[64:96]) + e[3] = new(big.Int).SetBytes(b[96:128]) return e, nil } // HashValue returns the value of the Account, which is the Poseidon hash of its *big.Int representation func (a *Account) HashValue() (*big.Int, error) { - b0 := big.NewInt(0) - toHash := []*big.Int{b0, b0, b0, b0, b0, b0} - lBI, err := a.BigInts() + bi, err := a.BigInts() if err != nil { return nil, err } - copy(toHash[:], lBI[:]) - - v, err := poseidon.Hash(toHash) - return v, err + return poseidon.Hash(bi[:]) } // AccountFromBigInts returns a Account from a [5]*big.Int @@ -200,36 +201,42 @@ func AccountFromBigInts(e [NLeafElems]*big.Int) (*Account, error) { if !cryptoUtils.CheckBigIntArrayInField(e[:]) { return nil, ErrNotInFF } + e0B := e[0].Bytes() + e1B := e[1].Bytes() + e2B := e[2].Bytes() + e3B := e[3].Bytes() var b [32 * NLeafElems]byte - copy(b[0:32], SwapEndianness(e[0].Bytes())) // SwapEndianness, as big.Int uses BigEndian - copy(b[32:64], SwapEndianness(e[1].Bytes())) - copy(b[64:96], SwapEndianness(e[2].Bytes())) - copy(b[96:128], SwapEndianness(e[3].Bytes())) + copy(b[32-len(e0B):32], e0B) + copy(b[64-len(e1B):64], e1B) + copy(b[96-len(e2B):96], e2B) + copy(b[128-len(e3B):128], e3B) return AccountFromBytes(b) } // AccountFromBytes returns a Account from a byte array func AccountFromBytes(b [32 * NLeafElems]byte) (*Account, error) { - tokenID, err := TokenIDFromBytes(b[0:4]) + tokenID, err := TokenIDFromBytes(b[28:32]) if err != nil { return nil, err } var nonceBytes5 [5]byte - copy(nonceBytes5[:], b[4:9]) + copy(nonceBytes5[:], b[23:28]) nonce := NonceFromBytes(nonceBytes5) - sign := b[10] == 1 - balance := new(big.Int).SetBytes(SwapEndianness(b[32:56])) // b[32:56], as Balance is 192 bits (24 bytes) - if !bytes.Equal(b[56:64], []byte{0, 0, 0, 0, 0, 0, 0, 0}) { + sign := b[22] == 1 + + balance := new(big.Int).SetBytes(b[40:64]) + // Balance is max of 192 bits (24 bytes) + if !bytes.Equal(b[32:40], []byte{0, 0, 0, 0, 0, 0, 0, 0}) { return nil, fmt.Errorf("%s Balance", ErrNumOverflow) } - ay := new(big.Int).SetBytes(SwapEndianness(b[64:96])) + ay := new(big.Int).SetBytes(b[64:96]) pkPoint, err := babyjub.PointFromSignAndY(sign, ay) if err != nil { return nil, err } publicKey := babyjub.PublicKey(*pkPoint) - ethAddr := ethCommon.BytesToAddress(b[96:116]) + ethAddr := ethCommon.BytesToAddress(b[108:128]) if !cryptoUtils.CheckBigIntInField(balance) { return nil, ErrNotInFF diff --git a/common/account_test.go b/common/account_test.go index 1c92cc6..12867ae 100644 --- a/common/account_test.go +++ b/common/account_test.go @@ -11,8 +11,10 @@ import ( ethCrypto "github.com/ethereum/go-ethereum/crypto" "github.com/iden3/go-iden3-crypto/babyjub" cryptoConstants "github.com/iden3/go-iden3-crypto/constants" + "github.com/iden3/go-iden3-crypto/poseidon" cryptoUtils "github.com/iden3/go-iden3-crypto/utils" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestIdxParser(t *testing.T) { @@ -85,7 +87,7 @@ func TestAccount(t *testing.T) { } b, err := account.Bytes() assert.Nil(t, err) - assert.Equal(t, byte(1), b[10]) + assert.Equal(t, byte(1), b[22]) a1, err := AccountFromBytes(b) assert.Nil(t, err) assert.Equal(t, account, a1) @@ -99,7 +101,7 @@ func TestAccount(t *testing.T) { assert.Equal(t, "1000", e[1].String()) assert.Equal(t, pk.Y.String(), e[2].String()) - assert.Equal(t, new(big.Int).SetBytes(SwapEndianness(account.EthAddr.Bytes())).String(), e[3].String()) + assert.Equal(t, new(big.Int).SetBytes(account.EthAddr.Bytes()).String(), e[3].String()) a2, err := AccountFromBigInts(e) assert.Nil(t, err) @@ -108,7 +110,7 @@ func TestAccount(t *testing.T) { } func TestAccountLoop(t *testing.T) { - // check that for different Address there is no problem + // check that for different deterministic BabyJubJub keys & random Address there is no problem for i := 0; i < 256; i++ { var sk babyjub.PrivateKey _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) @@ -145,6 +147,51 @@ func TestAccountLoop(t *testing.T) { } } +func TestAccountLoopRandom(t *testing.T) { + // check that for different random Address & BabyJubJub keys there is + // no problem + for i := 0; i < 256; i++ { + sk := babyjub.NewRandPrivKey() + pk := sk.Public() + + key, err := ethCrypto.GenerateKey() + assert.Nil(t, err) + address := ethCrypto.PubkeyToAddress(key.PublicKey) + + account := &Account{ + TokenID: TokenID(i), + Nonce: Nonce(i), + Balance: big.NewInt(1000), + PublicKey: pk, + EthAddr: address, + } + b, err := account.Bytes() + assert.Nil(t, err) + a1, err := AccountFromBytes(b) + assert.Nil(t, err) + assert.Equal(t, account, a1) + + e, err := account.BigInts() + assert.Nil(t, err) + assert.True(t, cryptoUtils.CheckBigIntInField(e[0])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[1])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[2])) + assert.True(t, cryptoUtils.CheckBigIntInField(e[3])) + + a2, err := AccountFromBigInts(e) + assert.Nil(t, err) + assert.Equal(t, account, a2) + } +} + +func bigFromStr(h string, u int) *big.Int { + b, ok := new(big.Int).SetString(h, u) + if !ok { + panic("bigFromStr err") + } + return b +} + func TestAccountHashValue(t *testing.T) { var sk babyjub.PrivateKey _, err := hex.Decode(sk[:], []byte("0001020304050607080900010203040506070809000102030405060708090001")) @@ -158,10 +205,77 @@ func TestAccountHashValue(t *testing.T) { PublicKey: pk, EthAddr: ethCommon.HexToAddress("0xc58d29fA6e86E4FAe04DDcEd660d45BCf3Cb2370"), } + v, err := account.HashValue() + assert.Nil(t, err) + assert.Equal(t, "16297758255249203915951182296472515138555043617458222397753168518282206850764", v.String()) +} + +func TestAccountHashValueTestVectors(t *testing.T) { + // values from js test vectors + ay := new(big.Int).Sub(new(big.Int).Exp(big.NewInt(2), big.NewInt(253), nil), big.NewInt(1)) + assert.Equal(t, "1fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", (hex.EncodeToString(ay.Bytes()))) + bjj, err := babyjub.PointFromSignAndY(true, ay) + require.Nil(t, err) + + account := &Account{ + Idx: 1, + TokenID: 0xFFFFFFFF, + PublicKey: (*babyjub.PublicKey)(bjj), + EthAddr: ethCommon.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), + Nonce: Nonce(0xFFFFFFFFFF), + Balance: bigFromStr("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16), + } + + e, err := account.BigInts() + assert.Nil(t, err) + assert.Equal(t, "9444732965739290427391", e[0].String()) + assert.Equal(t, "6277101735386680763835789423207666416102355444464034512895", e[1].String()) + assert.Equal(t, "14474011154664524427946373126085988481658748083205070504932198000989141204991", e[2].String()) + assert.Equal(t, "1461501637330902918203684832716283019655932542975", e[3].String()) + + h, err := poseidon.Hash(e[:]) + assert.Nil(t, err) + assert.Equal(t, "4550823210217540218403400309533329186487982452461145263910122718498735057257", h.String()) v, err := account.HashValue() assert.Nil(t, err) - assert.Equal(t, "9478468711598093334066833736294178928569163287501434518121324135729106649559", v.String()) + assert.Equal(t, "4550823210217540218403400309533329186487982452461145263910122718498735057257", v.String()) + + // second account + ay = big.NewInt(0) + bjj, err = babyjub.PointFromSignAndY(false, ay) + require.Nil(t, err) + account = &Account{ + TokenID: 0, + PublicKey: (*babyjub.PublicKey)(bjj), + EthAddr: ethCommon.HexToAddress("0x00"), + Nonce: Nonce(0), + Balance: big.NewInt(0), + } + v, err = account.HashValue() + assert.Nil(t, err) + assert.Equal(t, "7750253361301235345986002241352365187241910378619330147114280396816709365657", v.String()) + + // third account + ay = bigFromStr("21b0a1688b37f77b1d1d5539ec3b826db5ac78b2513f574a04c50a7d4f8246d7", 16) + bjj, err = babyjub.PointFromSignAndY(false, ay) + require.Nil(t, err) + account = &Account{ + TokenID: 3, + PublicKey: (*babyjub.PublicKey)(bjj), + EthAddr: ethCommon.HexToAddress("0xA3C88ac39A76789437AED31B9608da72e1bbfBF9"), + Nonce: Nonce(129), + Balance: bigFromStr("42000000000000000000", 10), + } + e, err = account.BigInts() + assert.Nil(t, err) + assert.Equal(t, "554050781187", e[0].String()) + assert.Equal(t, "42000000000000000000", e[1].String()) + assert.Equal(t, "15238403086306505038849621710779816852318505119327426213168494964113886299863", e[2].String()) + assert.Equal(t, "935037732739828347587684875151694054123613453305", e[3].String()) + v, err = account.HashValue() + assert.Nil(t, err) + assert.Equal(t, "10565754214047872850889045989683221123564392137456000481397520902594455245517", v.String()) } func TestAccountErrNotInFF(t *testing.T) { @@ -244,7 +358,7 @@ func TestAccountErrNumOverflowBalance(t *testing.T) { _, err = AccountFromBytes(b) assert.Nil(t, err) - b[56] = 1 + b[39] = 1 _, err = AccountFromBytes(b) assert.NotNil(t, err) assert.Equal(t, fmt.Errorf("%s Balance", ErrNumOverflow), err) diff --git a/test/debugapi/debugapi_test.go b/test/debugapi/debugapi_test.go index 183acc1..2748893 100644 --- a/test/debugapi/debugapi_test.go +++ b/test/debugapi/debugapi_test.go @@ -80,7 +80,7 @@ func TestDebugAPI(t *testing.T) { require.Nil(t, err) // Testing against a hardcoded value obtained by running the test and // printing the value previously. - assert.Equal(t, "8902613552504893273500019895709436294962812188236308621387152512232191202510", + assert.Equal(t, "21765339739823365993496282904432398015268846626944509989242908567129545640185", mtroot.String()) var accountAPI common.Account