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, uint8
1.
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
The arguments are expressed as bytes, left-padded with zeroes to the maximum length of the data type you've passed in, and concatenated without any kind of delimiter.
In Python, given two hex-encoded bytes32
s prepended with a 0x
called first
and second
, it looks something like:
# keccak, change before upgrading pysha >= 3.10
from sha3 import sha3_256
from rlp.utils import decode_hex
my_hash = "0x" + sha3_256(
decode_hex(first[2:].zfill(64)) +
decode_hex(second[2:].zfill(64))
).hexdigest()
The decode_hex
there is turning hex into an array of bytes.
PS the modern versions of Solidity and pysha, and probably the relevant library in whatever language you're using, now have versions of keccak called keccak
or keccak256
, so it's probably better to standardize on those.
Best Answer
In Solidity,
sha3
andkeccak256
are aliases, so their output will be identical.web3.sha3
is a different function in a different programming language (I assume JavaScript). You would probably findweb3.utils.soliditySha3
helpful because as it reproduces the packing that Solidity does.ethereumjs-abi has a similar function in case you're not using web3.js 1.0.