0

I am trying to understand how Monero hashes blocks.

I understand there is something called a "block hashing blob" that is combined with a nonce, but how can I get the block hashing blob for a specific block?

I tried the Monero Daemon JSON API get_block, which has a field blob in the return value (Python):

from Crypto.Hash import keccak

hash_blob = daemon.get_block(1)["blob"]
k = keccak.new(digest_bits=256)
hash_bin = bytes.fromhex(hash_blob)
k.update(hash_bin)
print(k.hexdigest())

The hash blob returned for get_block(1)["blob"] is:

0100d186c49a05418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e345fc7c30013d01ff0109c0a710025f391ad90e8b64ef432a0a28d0d9d699189b9c1322248ddbdb699c101a3cd9c3c0a8a5040233b81ac838467f7c59079e80517be1a45de51248a9bd841bf783e0aca7b2d5e2808ece1c023dc61a8cab11504ef1a36c51affeb7eed222cd09c98bf5045e489c23b409c9bc80c2d72f022caa4a9af327b36df136599a104278cf44342b0732bb5ad8c4c3582531add94b80a8d6b90702f9edf7d361176a29d1ce53e890e6ebc1e721d9719d273b69f1eb15d96025c6348088aca3cf02022e5ba472765cb2297f51f1a47aaf5f358cf48e16593c25bfc1880578014a65ed8090cad2c60e021b82a36cde1c9a6103d99ea6ebfb9f82b2547cef8934d8e87cf5e20c098ede9c80e08d84ddcb0102b37974cac8678446a230fb3e1b0ec06d56666bf0bf43dd64ff096a01f5aa2fda80c0caf384a30202a0c492751cb53389c9228f68b1ec1be831fb774a285f06c4ee02053858486e8221012a9fca96074c5216f9622c58c5c95024e53ff579d128913548363cb14d7f637400

and running the above code for it prints:

e26f21cfccf0ac4297647c879be976ed427e97ce02745dbbd6385decc8af6b53

querying the deamon for block 1, as in get_block(1)["header"]["hash"], I get hash:

771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148

So, obviously this is quite different. get_block_template seems to produce something that could be combined with a nonce to build a valid hash, but this seems to be only for new blocks.

How do I calculate the hashes for an existing block? Some pseudocode or similar is fine, I tried to read some of the Monero C code, but it jumps around and my C/C++ is a bit limited, so couldn't quite figure it out.

I understand the block hashing blob would need things like coinbase transaction, merkle tree root, nonce, etc. But I also understand I need to combine them in a very specific way to get the exact correct hash. So first to build the block hashing blob, then to combine it with nonce..

An example for a block would be great but anything helps..

EDIT: I modified the daemon to add the block hashing blob to the get_block RPC method. With this I get the block hashing blob for block 1 as:

0100d186c49a05418015bb9ae982a1975da7d79277c2705727a56894ba0fb246adaabb1f4632e345fc7c3052578a3816ec18ca6db2ec4f594b7c8a778caa4c52d2c1705bcbab9798a9ea7b01

However, calling cn_slow_hash from pycryptonight on this does not give the correct hash: pycryptonight.cn_slow_hash(binascii.unhexlify(hashblob), x)

Where x is the hash variant, I tried values 0-4. The block hash in the get_block(1) is 771fbcd656ec1464d3a02ead5e18644030007a0fc664c0a964d30922821a8148 but the value returned by variant 0 for above is 5aeebb3de73859d92f3f82fdb97286d81264ecb72a42e4b9f1e6d62eb682d7c0.

If I run the same for height 1407480 as in question Can't seem to reconstruct hashing blob for old block, I get the same hashing blob as mentioned in that question (0606f8e7a8ce05a5449f2dc32fff0730d58751fac5a2b9d58c59622f8a8d58d18e9886eddcdb01bb8c00006b4c34591fb7dd7d6664881f7c7e675aae7709d0acca033c6226727125c2127a17), but hashing it does not give the same hash as given for the block. Just like in that question, not able to get the correct hash.. But also do not understand how the author of that question fixed it.

What could be wrong with the hashing I use here?

1

How is block hash calculated?

You create a block hashing blob (which is the block header, the tree root hash of the blocks transactions and a varint of the transaction count), then hash this using the RandomX1 hashing algorithm.

The hash blob returned for get_block(1)["blob"] is...

That is not a block hashing blob, that's the block blob as returned from the RPC get_block.

How do I calculate the hashes for an existing block?

For an existing block, obtain the block blob (per above), parse it to extract the header and transactions, construct your hashing blob (per above) and hash that.

...then to combine it with nonce

An existing block already has the nonce data, which can be in both the block header / in the miner transaction. Thus you don't need to do anything particular with the nonce data for an existing block - rather what you need is the block blob data and a way to parse it (extract the header and transactions) so you can construct a hashing blob.

1 Note you need to use the correct PoW hashing algorithm based on the block height you are checking as the algorithm bas changed over time. The current algorithm is RandomX, but if you want to validate block 1 (which is something you mentioned) you'd need to use the original CryptoNight algorithm in that case.

UPDATE (based on comments below)

Note the term "block hash" can refer to either the PoW hash or the block ID (as used by block explorers etc). These use the same input data as described above but use different hash functions. For the latter you need to use Keccak-256.

5
  • Thanks! This gives me a high-level overview. I also found a more detailed old example at monero.stackexchange.com/questions/10663/…?, and the hashing library at pypi.org/project/py-cryptonight. Now just have to figure how the poster in that link finished the block hash calculation.. I am starting with the older blocks and will look at RandomX once have the older ones reconstructed.
    – kg_sYy
    Jan 13 at 21:57
  • I edited the question after modifying daemon to get the block hashing blob. Still, the hash I get does not match the block hash. Do you have any idea what could be wrong still?
    – kg_sYy
    Jan 15 at 6:14
  • 1
    How are you checking the resulting hash? You do realize the block hash (the one you see in a block explorer) is the block ID, not the PoW hash? Thus if you're trying to compare a blocks PoW hash to a block ID it will never match.
    – jtgrassie
    Jan 15 at 15:47
  • 1
    lol, no i did not realize this. i checked block.hash against the hash calculated with cn_slow_hash. from your comment and the answer at monero.stackexchange.com/questions/3958/… I now see that keccak is used to hash the hash blob to get block id. keccak(len(hashblob)+hashblob) now gives me the exact value of "block.hash". finally i got it, thanks a lot! is there some reason to not reuse the POW hash, since it hashes the same data?
    – kg_sYy
    Jan 15 at 17:21
  • 1
    "is there some reason to not reuse the POW hash, since it hashes the same data?" <- The internal names are a giveaway I guess: cn_slow_hash vs cn_fast_hash.
    – jtgrassie
    Jan 16 at 0:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.