Bitcoin: Simple block hash validation algorithms

Mining bitcoins is a computationally intensive process and forms the basis of the proof of work. Validating a block of the blockchain, on the other hand, is quite easy.
In order to validate a block of the Bitcoin blockchain, we need the information from the block header of the block to be validated. The block header contains the following information [2]:
- Version
The block version number. - Previous block hash
SHA256 hash of the previous block header. - Merkel root hash
SHA256 hash based on all the transactions in the block. In short: All Transaction hashes are paired, hashed, paired, hashed, ... until just one hash remains, the merkel root. - Timestamp
Timestamp of the block. - Difficulty bits
The proof-of-work algorithm difficulty target for this block. The target determines the difficulty and therefore affects how long it takes to find a solution to the proof-of-work algorithm [1,4]. - Nonce
During the process of mining, the nonce is altered to find a value that results in a block header hash that is less than the number represented by the difficulty target. Since the hash algorithm is a one-way function, the condition can only be satisfied by simple guessing nonce values [1,4].
In order to calculate a practical example, we need the data of already existing blocks. At the time of writing this article, blocks 729574 and 729575 are reasonably current. Just follow the links to see the details. To validate block 729575 we use the following values:
Header field | Value |
---|---|
Version | 0x20800000 |
Previous block hash | 00000000000000000009dfd5884c80b52f989baf44e6faaa04bf97f255f41b84 |
Merkel root hash | 9e2474d4f54cbfc5ee12ac4e8ae9e9426b41342607c3af9d42e7c9af521407e3 |
Timestamp | 2022-03-29 17:51:17 GMT +2 |
Difficulty bits | 0x170a40c0 |
Nonce | 0xcce7276f |
Hash to validate (729575) | 00000000000000000002468013524b804a49edc02e2100772d046f010006699c |
Below are some programs that validate the hash of block 729575, for demonstration purposes and as a starting point for your own experiments.
PHP
function swapOrder($input) {
$targetLength = ceil(strlen($input) / 8) * 8;
$input = str_pad($input, $targetLength, '0', STR_PAD_LEFT);
return implode('', array_reverse(str_split($input, 2)));
}
// Hash of block 729575 (the one to be validated)
$hashToValidate = '00000000000000000002468013524b804a49edc02e2100772d046f010006699c';
$headers = [
'version' => '20800000',
'prevHash' => '00000000000000000009dfd5884c80b52f989baf44e6faaa04bf97f255f41b84',
'merkleRoot' => '9e2474d4f54cbfc5ee12ac4e8ae9e9426b41342607c3af9d42e7c9af521407e3',
'timestamp' => dechex(strtotime('2022-03-29 17:51:17+2')),
'bits' => '170a40c0',
'nonce' => 'cce7276f'
];
$header = "";
foreach ($headers as $h) {
// Convert header params and concatenate
$header .= swapOrder($h);
}
// convert $header from hex to binary and hash it two times
$hash1 = hash('sha256', hex2bin($header));
$hash2 = hash('sha256', hex2bin($hash1));
$calcHash = swapOrder($hash2);
if ($calcHash == $hashToValidate) {
echo "Verified hash $calcHash";
} else {
echo "Could NOT verify hash $calcHash";
}
Python
import hashlib
import codecs # library to encode and decode from hex to string and string to hex
import time
import datetime
def swapOrder(input):
split = [str(input)[i:i + 2] for i in range(0, len(str(input)), 2)]
split.reverse()
return "".join(split)
hashToValidate: str = "00000000000000000002468013524b804a49edc02e2100772d046f010006699c"
timestamp = time.mktime(datetime.datetime.strptime("2022-03-29 17:51:17+0200", "%Y-%m-%d %H:%M:%S%z").timetuple())
headers = ["20800000",
"00000000000000000009dfd5884c80b52f989baf44e6faaa04bf97f255f41b84",
"9e2474d4f54cbfc5ee12ac4e8ae9e9426b41342607c3af9d42e7c9af521407e3",
hex(int(timestamp))[2:],
"170a40c0",
"cce7276f"]
headers = "".join(map(swapOrder, headers))
headers = codecs.decode(headers, "hex")
hash1 = hashlib.sha256(headers).digest()
hash2 = hashlib.sha256(hash1).digest()
hash2 = codecs.encode(hash2, 'hex_codec').decode('ascii')
calcHash = swapOrder(hash2)
if calcHash == hashToValidate:
print("Verified hash ", calcHash)
else:
print("Could NOT verify hash ", calcHash)
Java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;
class BlockValidator {
private static String swapOrder(String input) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < input.length(); i += 2) {
String s = input.substring(i, i + 2);
sb.insert(0, s);
}
return sb.toString();
}
public static String sha256AsHex(String hexString) throws NoSuchAlgorithmException {
MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] encodedhash = digest.digest(fromHexString(hexString));
return toHexString(encodedhash);
}
private static String toHexString(byte[] hash) {
StringBuilder sb = new StringBuilder();
for (byte b : hash) {
sb.append(Integer.toString((b & 0xff) + 0x100, 16).substring(1));
}
return sb.toString();
}
private static byte[] fromHexString(String text) {
assert text.length() % 2 == 0 : "invalid text length";
byte[] result = new byte[text.length()/2];
for (int i = 0; i < text.length(); i += 2) {
result[i/2] = (byte) Integer.parseInt(text.substring(i, i+2), 16);
}
return result;
}
public static void main(String[] args) throws ParseException, NoSuchAlgorithmException {
String hashToValidate = "00000000000000000002468013524b804a49edc02e2100772d046f010006699c";
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ssZ");
Date parsedDate = dateFormat.parse("2022-03-29 17:51:17+0200");
String timestamp = Long.toHexString(parsedDate.getTime() / 1000);
String[] headers = {
"20800000",
"00000000000000000009dfd5884c80b52f989baf44e6faaa04bf97f255f41b84",
"9e2474d4f54cbfc5ee12ac4e8ae9e9426b41342607c3af9d42e7c9af521407e3",
timestamp,
"170a40c0",
"cce7276f"
};
StringBuilder header = new StringBuilder();
for (String h : headers) {
header.append(swapOrder(h));
}
String hash1 = sha256AsHex(header.toString());
String hash2 = sha256AsHex(hash1);
String calcHash = swapOrder(hash2);
if (calcHash.equals(hashToValidate)) {
System.out.println("Verified hash " + calcHash);
} else {
System.out.println("Could NOT verify hash " + calcHash);
}
}
}
References
- A. M. Antonopoulos: Mastering Bitcoin: programming the open blockchain, Second edition. Sebastopol, CA: O’Reilly, 2017.
- Protocol documentation - Bitcoin Wiki, Jul. 30, 2021, Accessed on Mar. 30, 2022. [Online]. Available: https://en.bitcoin.it/wiki/Protocol_documentation#Block_Headers
- Bitcoin Core integration/staging tree. Bitcoin, 2022. Accessed on Mar. 30, 2022. [Online]. Available: https://github.com/bitcoin/bitcoin/blob/7c08d81e119570792648fe95bbacddbb1d5f9ae2/src/validation.cpp
- M. Oettler: How To Calculate and Verify a Hash Of a Block, Blockchain Academy, Oct 12, 2021. [Online]. Available: https://blockchain-academy.hs-mittweida.de/courses/blockchain-introduction-technical-beginner-to-intermediate/lessons/lesson-13-bitcoin-block-hash-verification/topic/how-to-calculate-and-verify-a-hash-of-a-block/.