6

When trying to test a smart contract that uses

 sha256(abi.encodePacked(_myargument)) 

I don't get the same hash on truffle ! I tried both

web3.utils.keccak256('_myargument')
web3.utils.sha3('_myargument') 

but both gave a hash different from what I get within the smart contract function.

So I thought it was because of abi.encodePacked, thus I tried

web3.utils.sha3(web3.eth.abi.encodeParameter('string', _myargument)) 

but the hash is still not the one expected.

Do you know why and how to get the same hash ? I don't understand why in general people use abi.encodePacked before hashing the argument with sha256(). If abi.encodePacked is involved, is it really necessary to keep it or can I safely remove it to just use sha256(_myargument) instead of sha256(abi.encodePacked(_myargument)) inside the smart contract ?

Best regards

3 Answers 3

9

First you need to know that sha256 and keccak256 functions are not the same. Check the docs to see available functions.

sha256 (with pyhton):

>>> sha256("Hello World!").hexdigest()
'7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069'

keccak256 (with web3):

web3.utils.keccak256("Hello World!")
'0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0'

To get the same hash:

contract Hash {
    function func() external pure returns (bytes32) {
        return keccak256(abi.encodePacked("Hello World!")) ;
    }
}

Output:

decoded output 
{
    "0": "bytes32: 0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0"
}

Choose:

truffle(development)> web3.utils.soliditySha3("Hello World!")
'0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0'
truffle(development)> web3.utils.keccak256("Hello World!")
'0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0'
truffle(development)> web3.utils.sha3("Hello World!")
'0x3ea2f1d0abf3fc66cf29eebb70cbd4e7fe762ef8a09bcc06c8edf641230afec0'

About abi.encodePacked(...)

abi.encodePacked(...) returns (bytes memory): Performs packed encoding of the given arguments. Note that packed encoding can be ambiguous!

You can test it, but with one argument, the result hash will be the same if you don't use abi.encodePacked(...).

EDIT:

From docs:

If you use keccak256(abi.encodePacked(a, b)) and both a and b are dynamic types, it is easy to craft collisions in the hash value by moving parts of a into b and vice-versa. More specifically, abi.encodePacked("a", "bc") == abi.encodePacked("ab", "c").

  • abi.encodePacked(...) encode dynamic types without length and static types are not padded if they are shorter than 32 bytes.
  • abi.encode(...) ABI-encodes the given arguments

abi.encode("a", "bc") result:

0x0000000000000000000000000000000000000000000000000000000000000040 // Start offset for the first parameter ("a")
0x0000000000000000000000000000000000000000000000000000000000000080 // Start offset for the second parameter ("bc")
0x0000000000000000000000000000000000000000000000000000000000000001 // Length of the first parameter ("a")
0x6100000000000000000000000000000000000000000000000000000000000000 // Padded to 32 bytes ("a") UTF-8 encoded
0x0000000000000000000000000000000000000000000000000000000000000002 // Length of the second parameter ("bc")
0x6263000000000000000000000000000000000000000000000000000000000000 // Padded to 32 bytes ("bc") UTF-8 encoded

keccak256: 0x68cd083d4c97fcbd081751d5390da5b37f5c485fd0879180d4816c456e8e532c

abi.encode("ab", "c") result:

0x0000000000000000000000000000000000000000000000000000000000000040 // Start offset for the first parameter ("ab")
0x0000000000000000000000000000000000000000000000000000000000000080 // Start offset for the second parameter ("c")
0x0000000000000000000000000000000000000000000000000000000000000002 // Length of the first parameter ("ab")
0x6162000000000000000000000000000000000000000000000000000000000000 // Padded to 32 bytes ("ab") UTF-8 encoded
0x0000000000000000000000000000000000000000000000000000000000000001 // Length of the second parameter ("c")
0x6300000000000000000000000000000000000000000000000000000000000000 // Padded to 32 bytes ("c") UTF-8 encoded

keccak256: 0x8c98d57214b9f76c3240d1fc677eb9fc1529ec2a4f56949bb6abe31d50b4b7c6

abi.encodePacked("a", "bc") result:

0x61    // ("a")  UTF-8 encoded without padding
0x6263  // ("bc") UTF-8 encoded without padding

keccak256: 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45

abi.encodePacked("ab", "c") result:

0x6162  // ("ab") UTF-8 encoded without padding
0x63    // ("c")  UTF-8 encoded without padding

keccak256: 0x4e03657aea45a94fc7d47ba826c8d667c0d1e6e33a64a036ec44f58fa12d6c45
5
  • @KNK Ok I get it. But with this technique is it sure that I will never get same output with different inputs because of abi.encodePacked ? They say "Unless there is a compelling reason, abi.encode should be preferred to abi.encodePacked." so is abi.encode better for me if I really need to get different outputs for different inputs ? Plus note that there are places where I use hashing with several arguments inside the smart contract
    – Kevin Wad
    Jun 25, 2019 at 9:03
  • @KevinWad Answer edited. Probably for your purpose its better to use abi.encode to avoid possible collisions.
    – alberto
    Jun 25, 2019 at 9:54
  • @KNK for the last 2 example you meant abi.encodePacked("a", "bc") and abi.encode("ab", "c"), right ? So we see that with abi.encodePacked we get same output with different inputs although with encode we don't have this issue. If I use abi.encode, will the keccak256() function give the same hash when testing on truffle ? Best regards
    – Kevin Wad
    Jun 25, 2019 at 17:04
  • 1
    @KevinWad Sorry, there was an error in the answer, now corrected. Yes, you can get the same hash on truffle but you need to transform the arguments to ABI-encoded format, read here: ethereum.stackexchange.com/questions/22884/…
    – alberto
    Jun 25, 2019 at 17:35
  • Am I mistaken, or did this answer not really give an answer to the original question? The first line of the answer is "sha256 is different from keccak256", and the answer then proceeds to explain keccak256. But the original question concerned itself with testing sha256 on truffle. Apr 30, 2022 at 8:30
1

abi.encodePacked is padding your argument, so sha3(0) is not the same as sha3(0x0000000000000000000000000000000000000000000000000000000000000000) assuming that your parameter is a number or a hex string

If it is a hex string - you should use web3.eth.abi.encodeParameter('uint256', _myargument) not web3.eth.abi.encodeParameter('string', _myargument)

Also please note that sha256 and keccak256 are not the same hash function

1
  • Hi @KNK thanks for reply ! The argument type is a string but I get this error when I try what you said: Error: invalid number value (arg="", coderType="uint256", value="BMW")
    – Kevin Wad
    Jun 25, 2019 at 8:36
1

It bears noticing that web3.utils.keccak256 and web3.utils.sha3 are synonyms, so no point using both. You should use keccak256, if given a choice, because the function it implements is not actually Sha3 as defined in the NIST standard, but rather a different function (an early implemementation of Keccak256, before it was modified for the SHA3 standard). I've written about this here.

Because of this, Solidity removed the sha3() function in 0.5.0, and kept just keccak256().

You can, however, invoke the 256-bit version of SHA2 in Solidity by using sha256(). This calls a precompiled contract instead of converting to an EVM opcode like keccak256() does, but I haven't compared the gas costs.

4
  • Thanks for this precision. If I want to use a hash function for mining purpose and I want the mining system to be as secure as bitcoin. Does it have an impact to use keccack256 instead of sha256 like bitcoin? If yes is it still secure enough if I use keccak256? Best regards
    – Kevin Wad
    Jul 25, 2019 at 14:36
  • Hmmm, what do you mean by "security"? In terms of the hash function per se, SHA2 has still not been broken. Last I read (csrc.nist.gov/Projects/Hash-Functions/…), NIST don't recommend an upgrade from SHA-2 to SHA-3. Their comparison of security parameters (nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf, pg 23) shows both algorithms are pretty close (slight mismatch on the 2nd preimage resistance). I'd presume Keccak256 and SHA-3 to be equivalent in this comparison. Remember, sha256 is the older of the two.
    – Alex Pinto
    Jul 28, 2019 at 21:14
  • @Kevin Wad, In terms of mining and security, you could look at another angle. The security of the blockchain depends a lot on the concetration of miners. So, if a hash can only be efficiently executed by specialized hardware, the community may concentrate around big mining pools, leading to a loss of decentralization and making the blockchain arguably less secure. Is this what you meant?
    – Alex Pinto
    Jul 28, 2019 at 21:22
  • Thanks for reply. I was talking about the risk of breaking the hash function as it already happened at least twice to Sha2 in the past 20 years. But in fact you put in light another point: does the choice of sha256 or keccak256 have an impact on asic mining and easyness to mine for regular people?
    – Kevin Wad
    Jul 29, 2019 at 22:05

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.