Solidity Testing – SHA256(abi.encodePacked(_argument)) in Truffle Tests

hash-algorithmsha3testingtruffle-test

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

Best Answer

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
Related Topic