Verifying Bitcoin Proof-of-Work (PoW)
29 Nov 2025
I’ve been running a full Bitcoin node for some time now. At some point, after getting bored looking at the incoming transactions, I started wondering what is actually happening when my node receives new blocks. I knew the high-level idea ”a full node validates PoW”, but I wanted to understand how does the node verify PoW for a block header so I could verify it myself.
The code is here: https://gist.github.com/msolomatin/6d60b525fab2ad7012e5d76800b60574
What verifying PoW for a block header means in practice
Every Bitcoin block header is 80 bytes long. If you hash these 80 bytes twice with SHA-256, you get a 32-byte number. For the block to be valid, this number must be smaller than or equal to a network-defined target value. In other words, sha256(sha256(header)) ≤ target .
The target comes from the nBits field inside the block header. Bitcoin docs define nBits field as:
An encoded version of the target threshold this block’s header hash must be less than or equal to.
Here’s the coolest part which took me ages to digest - despite nBits is a 4-byte (32-bit) field, it’s a compressed form of a 256-bit difficulty value.
So verifying PoW for a single block boils down to two steps:
- Expand
nBitsinto a full 256-bit target - Double hash the header and compare the numbers
If the hash is too large, the block is invalid. If it’s below the target, the PoW checks out.
Calculating target is where things get interesting
Let’s get a hex-encoded data of a block header:
bitcoin-cli getblockheader 000000000000000000019549da0db5548e865c1f064b2a5336182aa790019858 false
00800a2038a1a28d2d0c41014e022e0fef3f6631ce794df4ea5c00000000000000000000af016ef8a32ce91447d0823894ad4b97bc0fb42b6d8fe98a574548cc69e9bee351332869a0e201171d4999b5
The nBits field sits at byte offset 72 in the 80-byte header and is 4 bytes long. In the hex-encoded header those 4 bytes appear as a0e20117, which is stored in little-endian. If we flip the byte order to big-endian - the way we normally read integers - we get 1701d936.
Convert nBits into the target
nBits field value 1701d936 has two parts. In Bitcoin terminology 0x17 is an exponent, 0x01e2a0 is a mantissa. There’s a formula for target calculation in Bitcoin docs:

Which in simple terms means:
To calculate the target, shift mantissa to the left (exponent - 3) number of bytes.
nBits: 0x1701e2a0
mantissa (01e2a0)
------
Target: 00000000000000000001e2a00000000000000000000000000000000000000000
<---------------------------------------------
exponent (0x17 = 23 bytes)
shift mantissa 20 number of bytes
When you double-hash the header, you’ll get a number below this target — which is why the block is valid.
What I Found Interesting
- The process is extremely simple: no signatures, no cryptography beyond SHA-256.
- All nodes can verify PoW independently - no trust required.
- Difficulty isn’t enforced only by hash comparison - the node also checks that
nBitsis correct for the given block height, but that’s a story for another post.
But most importantly: anyone can verify Bitcoin’s work independently with a few dozen lines of code.