[Ethereum] Python keccak256 hash of uint8 array not matching solidity hash

contract-developmentkeccakpython

I have a function in a smart contract:

  function getNumberHash(uint8[] _numbers) constant public returns (bytes32) {
    return keccak256(_numbers);
}

When I pass [1,2,3,4,5] as the argument it returns:

"0": "bytes32: 0x5917e5a395fb9b454434de59651d36822a9e29c5ec57474df3e67937b969460c"

I have this Python script but it returns a different hash:

import sha3
import numpy as np

a = np.array([1, 2, 3, 4, 5], dtype=np.uint8)

k = sha3.keccak_256()
k.update(a)
print(k.hexdigest())

Which returns the following hash:

0x7d87c5ea75f7378bb701e404c50639161af3eff66293e9f375b5f17eb50476f4

Why aren't the two functions returning the same value since they are both uint8[] and using the same keccak256 algorithm? How can I resolve this issue?

Best Answer

Web3.soliditySha3() will give you the correct hash.

The underlying reason is that solidity 0-pads values in arrays, which you won't get by default in most implementations. web3.py has the convenience method just for this purpose.

In this example, you would use:

from web3 import Web3
hash = Web3.soliditySha3(['uint8[]'], [[1, 2, 3, 4, 5]])
assert hash.hex() == '0x5917e5a395fb9b454434de59651d36822a9e29c5ec57474df3e67937b969460c'