6

Been learning about generating wallets in Ethereum and things have been smooth until the point where I turn a public key into an actual address.

Multiple sources say to use Keccak to hash the public key (sans first byte), then take the last 20 bytes of the resulting hash as the address.

The public key I'm using (generated from a tool that I hope does this correctly), is: 0x025f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae And the resulting address is supposed to be: 0xd09D3103CcABfb769eDc3e9B01500Ca7241D470A

But none of the various implementations of Keccak (yes, even the ones that are really Keccak and not SHA3) are giving me something that resembles that address.

The hash I'm getting is 83453a8e57a0a2bb1277baea830983bd264e23140ef2cadd74b1e68c5c12f98f which means the address would be the last 20 bytes, or 0x830983bd264e23140ef2cadd74b1e68c5c12f98f

What am I doing wrong here?

2 Answers 2

7

This program computes your address given a public key. In your case it returns "0xd09d3103ccabfb769edc3e9b01500ca7241d470a" as address

const assert = require('assert');
const EC = require('elliptic').ec;
const keccak256 = require('js-sha3').keccak256;

async function main() {
  try {
    const ec = new EC('secp256k1');

    // Decode public key
    const key = ec.keyFromPublic('025f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae', 'hex');

    // Convert to uncompressed format
    const publicKey = key.getPublic().encode('hex').slice(2);

    // Now apply keccak
    const address = keccak256(Buffer.from(publicKey, 'hex')).slice(64 - 40);

    console.log(`Public Key: 0x${publicKey}`);
    console.log(`Address: 0x${address.toString()}`);
  } catch (err) {
    console.log(err);
  }
}

main();

If you have the private key you can replace ec.keyFromPublic('025f...', 'hex') with ec.keyFromPrivate('4444...', 'hex') and should output the same address.

6
  • Thanks Ismael - there's lots of code out there that does the right thing. I'm more concerned with what what I'm not understanding about the process that's causing me to get the wrong hash.
    – Nick
    Commented Oct 29, 2017 at 3:07
  • Your public key is in uncompressed format (indicated by the '02' in front). You have to convert to uncompressed format, and hash the uncompressed value.
    – Ismael
    Commented Oct 29, 2017 at 4:21
  • Awesome, that seems like a good direction to go in. If a key is made up of X and Y values (two very large ints), how does one construct an uncompressed key format from that?
    – Nick
    Commented Oct 29, 2017 at 19:38
  • 1
    @Ismael, in your first comment (second comment in this thread), did you mean to say, "Your public key is in the compressed format..."?
    – EvilJordan
    Commented Feb 18, 2018 at 6:53
  • 1
    @EvilJordan You are correct '02' is compressed format, unfortunatelly comments cannot be modified after some time.
    – Ismael
    Commented Feb 18, 2018 at 16:56
2

In the official ethereum foundation eth-keys library on https://github.com/ethereum/eth-keys, there is a suite of tools that could be useful for calculating the ethereum address of an ethereum public key (using Python, compared to the javascript code provided in the prior answer).

Note: the public key should 64 bytes and thus in an uncompressed format denoted by the leading 0x04 prefix (as noted in the comments in the prior answer by @Ismael), as using the one the main question from @Nick (0x025f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae) appears compressed (indicated by the leading 0x02 prefix which is 33 bytes in total) and would return an error when using the following command unless first uncompressed as I will demonstrate below in order to derive the ethereum address:

(After installing the eth-keys module via pip install eth-keys, the following can be run in a Python IDLE or .py file after using from eth_keys import keys)

>>> keys.PublicKey(b'\x02_7\xd2\x0e[\x18\x90\x93a\xe0\xea\xd7\xed\x17\xc6\x9bA{\xeeptl\x9e\x9c+\xcb\x13\x94\xd9!\xd4\xae').to_address()

eth_keys.exceptions.ValidationError: Unexpected uncompressed public key length: Expected 64, but got 33 bytes

Instead, we can uncompress the key, but first get its bytes, using this command from the eth-keys library:

>>> bytes.fromhex('025f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae')

b'\x02_7\xd2\x0e[\x18\x90\x93a\xe0\xea\xd7\xed\x17\xc6\x9bA{\xeeptl\x9e\x9c+\xcb\x13\x94\xd9!\xd4\xae'

Then calculate the uncompressed public key using the above bytes:

keys.PublicKey.from_compressed_bytes(b'\x02_7\xd2\x0e[\x18\x90\x93a\xe0\xea\xd7\xed\x17\xc6\x9bA{\xeeptl\x9e\x9c+\xcb\x13\x94\xd9!\xd4\xae')

'0x5f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae612d83e3487012034792ff36357ee25f382913cfeb54a8622b7ef35d635d8740'

Now we can pull the full 66-bytes from the uncompressed above public key with this command:

>>> bytes.fromhex('5f37d20e5b18909361e0ead7ed17c69b417bee70746c9e9c2bcb1394d921d4ae612d83e3487012034792ff36357ee25f382913cfeb54a8622b7ef35d635d8740')

b'_7\xd2\x0e[\x18\x90\x93a\xe0\xea\xd7\xed\x17\xc6\x9bA{\xeeptl\x9e\x9c+\xcb\x13\x94\xd9!\xd4\xaea-\x83\xe3Hp\x12\x03G\x92\xff65~\xe2_8)\x13\xcf\xebT\xa8b+~\xf3]c]\x87@'

Finally, the ethereum address can be computed using the above bytes (where this library's API will hash the above bytes using Keccak_256 and take the last 40 hex characters as the address):

>>> keys.PublicKey(b'_7\xd2\x0e[\x18\x90\x93a\xe0\xea\xd7\xed\x17\xc6\x9bA{\xeeptl\x9e\x9c+\xcb\x13\x94\xd9!\xd4\xaea-\x83\xe3Hp\x12\x03G\x92\xff65~\xe2_8)\x13\xcf\xebT\xa8b+~\xf3]c]\x87@').to_address() '0xd09d3103ccabfb769edc3e9b01500ca7241d470a'

Here is a visual of how the code looks in Python's default IDLE Interpreter: eth-keys example python public-key-to-address

(for similar tools see the libraries referenced in this related question: )

Note: While there could be a more efficient, faster way to do the above in the same library, I outlined these steps to show the entire process.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.