Bitcoin: Simple block hash validation algorithms

31.03.2022 Bitcoin
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

  1. A. M. Antonopoulos: Mastering Bitcoin: programming the open blockchain, Second edition. Sebastopol, CA: O’Reilly, 2017.
  2. Protocol documentation - Bitcoin Wiki, Jul. 30, 2021, Accessed on Mar. 30, 2022. [Online]. Available: https://en.bitcoin.it/wiki/Protocol_documentation#Block_Headers
  3. Bitcoin Core integration/staging tree. Bitcoin, 2022. Accessed on Mar. 30, 2022. [Online]. Available: https://github.com/bitcoin/bitcoin/blob/7c08d81e119570792648fe95bbacddbb1d5f9ae2/src/validation.cpp
  4. 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/.

Did you like the content? If you want to do something good for me, you can pour a little coffee into my empty pot.
Just click the PayPal.Me-Button* below. Thank you very much!

*For more information on our PayPal.Me link, see Impressum & Datenschutz