5

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?

  • 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. – Noel Maersk Feb 25 '18 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). – Noel Maersk Feb 25 '18 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 Feb 25 '18 at 23:24
7

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));
  • 2
    return address(keccak256(_publicKey)); also works, how come it does not include the first 12 byte of the publickey-hash? – schnorr Feb 25 '18 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 Feb 25 '18 at 23:22
  • Isn't the public key 64 bytes? How does this work? – Daniel Que May 26 '18 at 1:43
  • A public key in Ethereum is 32 bytes, but why does that matter? (keccak256 always returns 32 bytes.) – user19510 May 26 '18 at 1:44
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);
}
0

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)
    }    
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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