q9f / eth.rb

a straightforward library to build, sign, and broadcast ethereum transactions anywhere you can run ruby.
https://q9f.github.io/eth.rb
Apache License 2.0
200 stars 86 forks source link

Bug: Compressed public key to the wrong address #205

Closed an-lee closed 1 year ago

an-lee commented 1 year ago

The original code

# https://github.com/q9f/eth.rb/blob/main/lib/eth/util.rb#L29
#
# Generates an Ethereum address from a given compressed or
# uncompressed binary or hexadecimal public key string.
#
# @param str [String] the public key to be converted.
# @return [Eth::Address] an Ethereum address.
def public_key_to_address(str)
  str = hex_to_bin str if hex? str
  bytes = keccak256(str[1..-1])[-20..-1]
  Address.new bin_to_prefixed_hex bytes
end

It seems that it doesn't handle the compressed public key right.

Example:

key = Eth::Key.new priv: '25ef37d1b525991c00b0feb44de32b72df35b20ddaae104f65bf09cae129a082'
Eth::Util.public_key_to_address(key.public_hex).to_s
# output: 0xa92Af1419c32A95c042A560E30bba1a6DcF1FD8e
Eth::Util.public_key_to_address(key.public_hex_compressed).to_s
# output: 0xa26bd09A48B9a83aC847Ed72275dEB0783a11c3E

Maybe we can fix it like this:

def public_key_to_address(str)
  str = hex_to_bin str if hex? str
+ str = Secp256k1::PublicKey.from_data(str).uncompressed
  bytes = keccak256(str[1..-1])[-20..-1]
  Address.new bin_to_prefixed_hex bytes
end

ref: https://github.com/etscrivner/rbsecp256k1/blob/master/documentation/public_key.md#from_datapublic_key_data

q9f commented 1 year ago

Good find. You are probably the first to use compressed public keys in the context of Ethereum. Most wallets and libraries don't support that.

Let me test this tomorrow.