Skip to content

Commit

Permalink
address derivation returns also the address in little endian format
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmenendez committed Nov 22, 2024
1 parent ec10056 commit a3ed35e
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 19 deletions.
43 changes: 28 additions & 15 deletions address/address.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
Expand All @@ -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
}
21 changes: 17 additions & 4 deletions address/address_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package address

import (
"fmt"
"math/big"
"testing"
"time"

Expand All @@ -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
Expand All @@ -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),
Expand Down

0 comments on commit a3ed35e

Please sign in to comment.