From a3ed35efbae10e9ced9d8f4f154abd365b9afad3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Men=C3=A9ndez?= Date: Fri, 22 Nov 2024 12:17:37 +0100 Subject: [PATCH] address derivation returns also the address in little endian format --- address/address.go | 43 +++++++++++++++++++++++++++-------------- address/address_test.go | 21 ++++++++++++++++---- 2 files changed, 45 insertions(+), 19 deletions(-) diff --git a/address/address.go b/address/address.go index a937780..12762d7 100644 --- a/address/address.go +++ b/address/address.go @@ -9,8 +9,7 @@ import ( ) // elemToU8 converts a field element to a slice of uint8 by converting each -// limb to a slice of uint8 and concatenating them. The order of the bytes is -// reversed to match the endianness of the Ethereum address (msb). +// limb to a slice of uint8 and concatenating them. func elemToU8[T emulated.FieldParams](api frontend.API, elem emulated.Element[T]) ([]uints.U8, error) { bf, err := uints.New[uints.U64](api) if err != nil { @@ -24,12 +23,7 @@ func elemToU8[T emulated.FieldParams](api frontend.API, elem emulated.Element[T] res = append(res, b) } } - // swap the order of the bytes - var swap []uints.U8 - for i := len(res) - 1; i >= 0; i-- { - swap = append(swap, res[i]) - } - return swap, nil + return res, nil } // u8ToVar converts a slice of uint8 to a variable by multiplying the current @@ -46,27 +40,46 @@ func u8ToVar(api frontend.API, u8 []uints.U8) (frontend.Variable, error) { return res, nil } +// swapEndianness swaps the endianness of a slice of uint8 by reversing it. +func swapEndianness(u8 []uints.U8) []uints.U8 { + var swap []uints.U8 + for i := len(u8) - 1; i >= 0; i-- { + swap = append(swap, u8[i]) + } + return swap +} + // DeriveAddress derives an Ethereum address from a public key by hashing the // public key and returning the last 20 bytes of the hash as an address into a -// variable. -func DeriveAddress(api frontend.API, pubKey ecdsa.PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr]) (frontend.Variable, error) { +// variable. It also returns the address with the bytes in little-endian order. +func DeriveAddress(api frontend.API, pubKey ecdsa.PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr]) (frontend.Variable, frontend.Variable, error) { // convert public key coords to uint8 and concatenate them xBytes, err := elemToU8(api, pubKey.X) if err != nil { - return 0, err + return 0, 0, err } yBytes, err := elemToU8(api, pubKey.Y) if err != nil { - return 0, err + return 0, 0, err } - pubBytes := append(xBytes, yBytes...) + // swap endianness of the bytes and concatenate them + pubBytes := append(swapEndianness(xBytes), swapEndianness(yBytes)...) // hash the public key keccak, err := sha3.NewLegacyKeccak256(api) if err != nil { - return 0, err + return 0, 0, err } keccak.Write(pubBytes) hash := keccak.Sum() // return the last 20 bytes of the hash as an address - return u8ToVar(api, hash[12:]) + addrBytes := hash[12:] + addr, err := u8ToVar(api, addrBytes) + if err != nil { + return 0, 0, err + } + addrLE, err := u8ToVar(api, swapEndianness(addrBytes)) + if err != nil { + return 0, 0, err + } + return addr, addrLE, nil } diff --git a/address/address_test.go b/address/address_test.go index 47d2093..93a4ea0 100644 --- a/address/address_test.go +++ b/address/address_test.go @@ -2,6 +2,7 @@ package address import ( "fmt" + "math/big" "testing" "time" @@ -19,19 +20,29 @@ import ( ) type testAddressCircuit struct { - Address frontend.Variable `gnark:",public"` - PublicKey gecdsa.PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr] + Address frontend.Variable `gnark:",public"` + AddressLittleEndian frontend.Variable `gnark:",public"` + PublicKey gecdsa.PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr] } func (c *testAddressCircuit) Define(api frontend.API) error { - addr, err := DeriveAddress(api, c.PublicKey) + addr, addrLE, err := DeriveAddress(api, c.PublicKey) if err != nil { return err } api.AssertIsEqual(c.Address, addr) + api.AssertIsEqual(c.AddressLittleEndian, addrLE) return nil } +func goSwapEndianness(b []byte) []byte { + var swap []byte + for i := len(b) - 1; i >= 0; i-- { + swap = append(swap, b[i]) + } + return swap +} + func TestAddressDerivation(t *testing.T) { c := qt.New(t) // compile the circuit and get the constraints @@ -45,9 +56,11 @@ func TestAddressDerivation(t *testing.T) { input := crypto.Keccak256Hash([]byte("hello")).Bytes() testSig, err := internaltest.GenerateAccountAndSign(input) c.Assert(err, qt.IsNil) + addrLE := new(big.Int).SetBytes(goSwapEndianness(testSig.Address.Bytes())) // init inputs witness := testAddressCircuit{ - Address: testSig.Address, + Address: testSig.Address, + AddressLittleEndian: addrLE, PublicKey: gecdsa.PublicKey[emulated.Secp256k1Fp, emulated.Secp256k1Fr]{ X: emulated.ValueOf[emulated.Secp256k1Fp](testSig.PublicKey.X), Y: emulated.ValueOf[emulated.Secp256k1Fp](testSig.PublicKey.Y),