[Ethereum] block-header hash verification

block-headerhashhash-algorithm

As part of a validation service I am working on, I need to be able to verify that the blockHeader hash was created correctly. Based on my understanding following are the steps to regenerate the header hash, and the corresponding scripts I used for this exercise.

  1. Use web3 provider as Infura mainnet api key
  2. With block number as input, get values from eth.getBlock, which gives the following response:

{ difficulty: '6022643743806', extraData:
'0xd583010202844765746885676f312e35856c696e7578', gasLimit: 3141592,
gasUsed: 0, hash:
'0x5d15649e25d8f3e2c0374946078539d200710afc977cdfc6a977bd23f20fa8e8',
logsBloom:
'0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000',
miner: '0x2a65Aca4D5fC5B5C859090a6c34d164135398226', mixHash:
'0x3fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38',
nonce: '0x6af23caae95692ef', number: 400000, parentHash:
'0x1e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14',
receiptsRoot:
'0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
sha3Uncles:
'0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347',
size: 539, stateRoot:
'0x0b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89',
timestamp: 1445130204, totalDifficulty: '2014130882275463845',
transactions: [], transactionsRoot:
'0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
uncles: [] }

  1. The idea is to create the hash value in this response, as this is the header hash of the block (am I right in this assumption?). Based on the documentation (https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go), I need to first create a rlp of the above response. My approach has been to: feed the response into a nestedList and generate a rlp buffer with this library(https://github.com/ethereumjs/rlp), and here are the results

<Buffer f9 02 b7 d2 8a 64 69 66 66 69 63 75 6c 74 79 86 05 7a 41 8a
7c 3e e1 89 65 78 74 72 61 44 61 74 61 96 d5 83 01 02 02 84 47 65 74
68 85 67 6f 31 2e 35 ... 648 more bytes>

  1. At this point, I took the output of above step, to compute a keccak256 hash (https://github.com/ethereumjs/ethereumjs-util/tree/master/docs#keccak256), and here are the results

0xc5bad52524f562d2783ec20331af3780b8233c2d13b04de79acb4347804f7578

  1. Check the output of step 4 above and compare with the hash value from Step 2. The hash value from my test does not match

Would someone be able to validate the above steps and provide guidance on how I can re-create the blockHeader hash? Suggestions?

Best Answer

Below are the steps to calculate blockHash, given a blockNumber:

Step1. eth.getBlock(400000)

Output:{ difficulty: '6022643743806', extraData: '0xd583010202844765746885676f312e35856c696e7578', gasLimit: 3141592, gasUsed: 0, hash: '0x5d15649e25d8f3e2c0374946078539d200710afc977cdfc6a977bd23f20fa8e8', logsBloom: '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', miner: '0x2a65Aca4D5fC5B5C859090a6c34d164135398226', mixHash: '0x3fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38', nonce: '0x6af23caae95692ef', number: 400000, parentHash: '0x1e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14', receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', size: 539, stateRoot: '0x0b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89', timestamp: 1445130204, totalDifficulty: '2014130882275463845', transactions: [], transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', uncles: [] }

Hash in the above object refers to the blockHash which we are trying to validate. This is the actual output which will be used later to compare the test results with.

Step2. Remove some of the elements from the above object, keep only those that are inputs into a blockHeader, and reorder it as below:

[['ParentHash','0x1e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14'], ['UncleHash','0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'], ['Coinbase','0x2a65Aca4D5fC5B5C859090a6c34d164135398226'], ['Root','0x0b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89'], ['TxHash','0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'], ['ReceiptHash','0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421'], ['Bloom','0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'], ['Difficulty',6022643743806], ['Number','0x400000'], ['GasLimit',3141592], ['GasUsed',0], ['Time',1445130204], ['Extra','0xd583010202844765746885676f312e35856c696e7578'], ['MixDigest','0x3fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38'], ['Nonce','0x6af23caae95692ef']]

Step3. Convert values with numbers to Hex

console.log(web3.utils.toHex(gasLimit));
console.log(web3.utils.toHex(gasUsed));
console.log(web3.utils.toHex(time));
console.log(web3.utils.toHex(difficulty));
console.log(web3.utils.toHex(number));

Step4. For elements with value as 0, use 0x and not 0x0

Example: for block 400000, the value for gasUsed is 0. So the value used for gasUsed should be 0x and not 0x0

Step5. After changing the numbers to Hex and handing values with 0, we can go ahead and remove all the keys listed in Step2 and restructure the array as below:

[ '0x1e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14', '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347', '0x2a65aca4d5fc5b5c859090a6c34d164135398226', '0x0b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89', '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421', '0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', '0x57a418a7c3e', '0x61a80', '0x2fefd8', '0x', '0x5622efdc', '0xd583010202844765746885676f312e35856c696e7578', '0x3fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38', '0x6af23caae95692ef' ]

Step6. The above array of values can now be fed into a rlp.encode function

Output: 0xf90213a01e77d8f1267348b516ebc4f4da1e2aa59f85f0cbd853949500ffac8bfc38ba14a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942a65aca4d5fc5b5c859090a6c34d164135398226a00b5e4386680f43c224c5c037efc0b645c8e1c3f6b30da0eec07272b4e6f8cd89a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000086057a418a7c3e83061a80832fefd880845622efdc96d583010202844765746885676f312e35856c696e7578a03fbea7af642a4e20cd93a945a1f5e23bd72fc5261153e09102cf718980aeff38886af23caae95692ef

Step7. Pass the above RLP through a keccak256 function. Below is the output:

TestOutput: 0x5d15649e25d8f3e2c0374946078539d200710afc977cdfc6a977bd23f20fa8e8

ActualOutput: 0x5d15649e25d8f3e2c0374946078539d200710afc977cdfc6a977bd23f20fa8e8

Conclusion: Use eth.getBlock(). Take the object it returns as output. Remove unwanted elements and keep only those that are inputs to a blockHeader. Convert numbers to Hex and handle values with 0s. Restructure the object to form an array of strings. Pass this array into a rlp.encode function. Take the rlp output and apply a keccak256 hash on it.

Links: https://github.com/ethereum/go-ethereum/blob/master/core/types/block.go#L69

Related Topic