Solidity SHA3 – How Does keccak256 Hash Uints?

hash-algorithmjavascriptkeccaksha3solidity

This is not a question about the issue with Ethereum using a non-standard sha3. I have found the right JS hashing library, and am able to get matching hashes in JS and Solidity on byte strings. What I am wondering is how to represent a uint when passing it to the JS hash lib so that it results in the same hash as created by Solidity sha3.

JS
'0x' + keccak(
  1
)
// 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470

Solidity
sha3(
  1
);
// 0x5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2

Best Answer

Jehan's answer is great, but we need to explain one more thing: Why does sha3(1) in solidity produce b10e2d...fa0cf6?

This is because solidity's sha3 function hashes its inputs based on the argument types. Thus the value 1 will generate a different hash if it is stored as bytes8, bytes16, bytes32, etc. Since sha3(1) is being passed 1 as a number literal, it is converted into the smallest necessary type, uint81.

8 bits fit into 2 hex characters, so if you pad your input to 2 characters you will get the same result in web3:

Javascript:

web3.sha3(leftPad((1).toString(16), 2, 0), { encoding: 'hex' })
// 5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2

Likewise, you can cast the number on the solidity side:

Solidity:

// uint is equivalent to uint256
sha3(uint(1))
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

Javascript:

// note that the value is padded by 64 characters to fit 256 bits
web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6

A note about BigNumber types:

They don't work automatically with web3.sha3. You have to convert them to hex first.

Solidity:

sha3(uint(100 ether))
// c7cc234d21c9cfbd4632749fd77669e7ae72f5241ce5895e410c45185a469273

Javascript:

// the .slice is to remove the leading '0x'
web3.sha3(leftPad(web3.toHex(web3.toWei(100)).slice(2).toString(16), 64, 0), { encoding: 'hex' })
// c7cc234d21c9cfbd4632749fd77669e7ae72f5241ce5895e410c45185a469273

EDIT:

I wrote a small lib that provides a version of web3.sha3 that exactly matches the behavior of sha3 in Solidity. Hopefully this clears up all your hashing woes :). https://github.com/raineorshine/solidity-sha3