8

The address is the last 20 bytes of sha3(publicKey)

In Solidity, using assembly if needed, how could I splice the first 12 bytes from sha3(publicKey) and get the address? What I came up with runs out of gas:

bytes32 b = sha3(publicKey)
address signer;
assembly {
signer := mload(add(b, 0x0c) // skip the first 12 bytes, 0c in hex = 12
}

MLOAD(0XAB) loads the word (32 byte) located at the memory address 0XAB. Does my function fail because it is trying to load 20 byte only? Can MLOAD load only 20 byte?

3
  • What you're doing here is loading from memory location sha3(pk)+12, which is (very likely) a 256-bit number. You're running out of gas because expanding memory to such high a location eats all of it. Commented Feb 25, 2018 at 19:58
  • The "usual" way to achieve what you want is to AND with 0x[12 zero-bytes][20 ff-bytes]. An alternative (commonplace elsewhere in computing) is shifting the input left X bits, then back right X bits (without "rotation") - but the EVM doesn't have shift operations (yet). Commented Feb 25, 2018 at 20:02
  • 1
    @NoelMaersk That's not what that assembly does. add(b, 0x0c) is analogous to pointer arithmetic. (It gives you the location in memory 12 bytes past the beginning of b.)
    – user19510
    Commented Feb 25, 2018 at 23:24

4 Answers 4

10

This works:

return address(keccak256(publicKey) & (2**(8*21)-1));

2**(8*21)-1 is just a trick to get 0xFFFFFF... (40 Fs) without typing it. :-)

EDIT

As pointed out by @schnorr, there's no need for the mask:

return address(keccak256(publicKey));
5
  • 2
    return address(keccak256(_publicKey)); also works, how come it does not include the first 12 byte of the publickey-hash?
    – schnorr
    Commented Feb 25, 2018 at 23:16
  • 1
    Oh, that makes sense too. address is of size 20 bytes, so it can't store more bytes than that. It keeps the least significant bytes, like all casts to smaller-size numbers.
    – user19510
    Commented Feb 25, 2018 at 23:22
  • Isn't the public key 64 bytes? How does this work?
    – Daniel Que
    Commented May 26, 2018 at 1:43
  • A public key in Ethereum is 32 bytes, but why does that matter? (keccak256 always returns 32 bytes.)
    – user19510
    Commented May 26, 2018 at 1:44
  • public keys in Ethereum are 64 bytes, not 32! PRIVATE keys are 32 bytes though. Source: hackernoon.com/…
    – Xenonite
    Commented Jun 16, 2021 at 10:17
1

From an answer to How to convert an bytes to address in Solidity? as well an answer to How to concatenate a bytes32[] array to a string?, I came up with this for converting a bytes32 keyHash to an address.

function getAddressFromPublicKey(bytes _publicKey) returns (address signer) {
    // Get address from public key
    bytes32 keyHash = keccak256(_publicKey);
    uint result = 0;
    for (uint i = keyHash.length-1; i+1 > 12; i--) {
      uint c = uint(keyHash[i]);
      uint to_inc = c * ( 16 ** ((keyHash.length - i-1) * 2));
      result += to_inc;
    }
    return address(result);
}
1

In solidity 0.5:

function calculateAddress(bytes memory pub) public pure returns (address addr) {
    bytes32 hash = keccak256(pub);
    assembly {
        mstore(0, hash)
        addr := mload(0)
    }    
}
1

This should work for Solidity 0.5.x:

bytes32 hash = keccak256(publicKey);
address wallet = address(uint160(bytes20(hash)));
1

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.