29

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
1
  • 1
    Update: Solidity's keccak256 produces identical results to its sha3 so everything in this thread applies to keccak256.
    – eth
    Commented Nov 21, 2016 at 10:15

3 Answers 3

29

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

10

Solidity use HEX values internally.

> web3.sha3(web3.toHex(1))
"5fe7f977e71dba2ea1a68e21057beebb9be2ac30c6410aa38d4f3fbe41dcffd2"
2
  • 1
    I'm getting web3.sha3(web3.toHex(1)) === 8a07523229fdc48491a5e56c76620ba40639eb940e6a2fbdf62b2799b4c86643
    – Jehan
    Commented Apr 6, 2016 at 3:26
  • This is not really a full answer, see my answer.
    – Jehan
    Commented May 4, 2016 at 5:16
8

Solidity's sha3 function hashes the byte representation of a uint. That is, the number in hex (base 16), padded to 32 bytes. 32 empty bytes in hex representation is 64 zeros.

To do this in JS, we can utilize the infamous left-pad package:

const jsHashWeb3 = web3.sha3(leftPad((1).toString(16), 64, 0), { encoding: 'hex' })
// b10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6
2
  • In Solidity, sha3(1) produces 0x5fe7f9...dcffd2, not b10e2d...fa0cf6 as your code produces. Could you explain? Commented Jul 12, 2016 at 1:07
  • I know the feeling. Unfortunately I cannot explain. Maybe a typo on my part, it's been a couple of months. I've been using the snippet that I posted about here: github.com/ethereum/web3.js/issues/445. It doesn't cover all edge cases, but it's been working for me.
    – Jehan
    Commented Jul 14, 2016 at 1:39

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.