§ 07 · Blocks
Block hash
A block's ID, and why on Pearl it looks completely random
On this page 8 sections
Every block has a name: its block hashBlock hash: A block's ID: the double SHA-256 of its 108-byte header. On Pearl it looks random, because it is never compared with the target.. It’s a 32-byte fingerprint of the header, written as 64 hex digits, and it’s how everyone refers to a block.
If you know Bitcoin, you know block hashes as long strings that start with a row of zeros, because in Bitcoin the hash is the proof of work. Pearl block hashes don’t look like that. They look completely random, because on Pearl the block hash has only one job: to be the block’s ID.
Try it
Hash anything with SHA256d. Change one character and watch about half the bits of the output flip.
How is it computed?
Hash the 108-byte header with SHA-256, hash the result with SHA-256 again (SHA256dSHA256d: SHA-256 applied twice: the hash of the hash. Pearl's block hashes, transaction IDs, Merkle roots and proof commitments all use it.), and reverse the 32 bytes for display:
| Field | Bytes | Value |
|---|---|---|
| Version header.version | 4 @0 | 0x20000000 int32 LE. BIP-9 style 0x20000000 since block 1; no consensus meaning in Pearl. |
| Previous block header.prevBlock | 32 @4 | eb555c52c695cfa0b2ad27f0d1f96ca55f1dc32d864e13218b4bd9041b969c99 Hash of the parent block, stored byte-reversed; shown in display order. |
| Merkle root header.merkleRoot | 32 @36 | c6c21c0c4204b9f39e17d7f79811aff53eac88ea0d80fcdeffdad96ba3fa8963 Root of the SHA256d Merkle tree of this block’s txids, stored byte-reversed. |
| Time header.timestamp | 4 @68 | 1792513562 (2026-10-20 16:26:02 UTC) uint32 LE Unix seconds. Must be at least 1 s after the parent and at most 5 min in the future. |
| Bits header.bits | 4 @72 | 0x176af3a6 Compact target. Difficulty 40157376.49334089. The certificate's jackpot hash (not this header's hash) must meet it. |
| Proof commitment header.proofCommitment | 32 @76 | 49e67ac73638201f2815a24689b8b1df672f9176df2ee232b00c1632fda3524d SHA256d(LE32(certificate version) ‖ public data). Replaces Bitcoin’s nonce: Pearl headers have no nonce. |
For block 130,827, SHA256d of those 108 bytes comes out as b9456d3c…c1f2f21d in internal byte order. Reversed, that’s the hash every explorer shows:
1df2f2c1bf39b1a444e622c1788c11736da2f91f4e9a875a38dc23063c6d45b9
The algorithm is Bitcoin’s, unchanged. Only the input is longer: 108 bytes instead of 80.
Why doesn’t it start with zeros?
Because nothing requires it to. Here are four real Pearl block hashes:
| Block | Block hash |
|---|---|
| 1 | a09851a4761539d170f7dfe7531864ae92879b02bbe999a6c238f63745a69726 |
| 120,000 | a5f57e441de1da43a27b4bee471c7a7bdd8e36664050385bf0a1b760d2a84b7d |
| 130,825 | 3fc851763893a0135ea756d5dcbe355f375e613957930e7f401fafc8ee5533ae |
| 130,827 | 1df2f2c1bf39b1a444e622c1788c11736da2f91f4e9a875a38dc23063c6d45b9 |
In Bitcoin, a miner keeps changing the header until its hash, read as a number, is below the target. Pearl miners never do that. The number that has to beat the target is a different hash, a BLAKE3 ticketTicket: A 256-bit BLAKE3 hash of a tile's transcript. It wins a block if, read as a number, it is at most the target times the work that tile cost. called hash_jackpot, stored inside the block’s certificate. See tickets and target.
Make a guess
If Pearl did compare the block hash with the target, how many leading zero hex digits would block 130,827's hash need?
Think of your answer first, then check it.
Answer At least 18.
Block 130,827’s target is 0000000000000000006af3a6000…: 18 zero hex digits, then 6af3a6. A random hash lands that
low about once in 1.13 × 10²² tries. The real hash, 1df2f2c1…, sits about 12% of the way up the whole 256-bit range:
an utterly ordinary number. Nobody checks it, so nobody ground it down.
What does the hash cover?
Everything in the header, and through it almost everything in the block:
- The parent, through the previous-block field, and so, link by link, the whole chain back to genesis.
- The transactions, through the Merkle root, and their witnesses through the coinbase’s witness commitment (when the block has any witness data).
- The time and the target, directly.
- The mining result: through the proof commitment, the mining settings, both matrix fingerprints, the winning ticket, the matrix sizes and the tile position.
One thing is deliberately left out: the zero-knowledge proof bytes. Proofs are randomized, so the same block could carry different valid proofs, and the block hash must not depend on which one.
Where is the block hash used?
As the block’s name, everywhere:
- The next block stores it in its previous-block field.
- The certificate stores it too, in internal byte order, and a node rejects a certificate whose stored hash doesn’t match the header’s.
- Checkpoints in the node software are lists of block hashes: block 120,000 must be
a5f57e44…d2a84b7d, and so on every 10,000 blocks. - Explorers, wallets and peers ask for blocks by hash.
Why is it still SHA-256?
Because Pearl kept Bitcoin’s hash wherever it kept Bitcoin’s design. Block hashes, transaction IDs, the transaction Merkle tree and the proof commitment all use SHA256d, exactly as Bitcoin’s equivalents do.
BLAKE3 appears only inside the mining puzzle: the job key, the matrix fingerprints, the noise seeds and the tickets. There its strengths matter: it is natively a Merkle tree, so a gigabyte-sized matrix can be committed to and opened a few rows at a time; it is fast on GPUs; and it has a keyed mode. See hash functions.
Why does the certificate repeat the block hash?
So a node can pair a certificate with its header before doing any real work. The first check in certificate verification is simply “does the hash stored in the certificate equal SHA256d of this header?”. A certificate copied onto another header fails at once, before the expensive proof check. Light clients rely on the same pairing: they download each header with its certificate, verify both, and keep only the 108-byte header afterwards.
Can a miner choose its block hash?
Only by changing the block. Any change to the header gives a completely different hash (the avalanche effect), and any change to the first 76 bytes also gives the miner a completely different mining puzzle. Since no rule cares what the hash looks like, there’s no reason to try.
Code: the hash, in Go and JavaScript
BlockHash() (node/wire/blockheader.go:43-48 at v1.5.2, on GitHub) serializes the header and hashes it twice:
// BlockHash computes the block identifier hash for the given block header.
func (h *BlockHeader) BlockHash() chainhash.Hash {
return chainhash.DoubleHashRaw(func(w io.Writer) error {
return writeBlockHeader(w, 0, h)
})
} And checkProofOfWork (node/blockchain/validate.go:309-339 at v1.5.2, on GitHub) never looks at it. It checks the
target’s range, then hands the header and certificate to the zero-knowledge verifier. (Its comment still says “the
block hash must be less than the claimed target”, a leftover from btcd; the code below the comment verifies the
certificate instead.)
In JavaScript, with @noble/hashes:
import { sha256 } from '@noble/hashes/sha2.js';
import { hexToBytes, bytesToHex } from '@noble/hashes/utils.js';
const header = hexToBytes('00000020999c961b…c77ae649'); // all 108 bytes
const hash = bytesToHex(sha256(sha256(header)).reverse()); // '1df2f2c1…3c6d45b9'