2.9 PROOF OF WORK

EXERCISE 2.9 PROOF OF WORK

Write a program that feeds a counter in SHA-256, taking the output hash and converting it to an integer (we did this earlier before converting to binary). Have the program repeat until it finds a hash that is less than a target number. The target number should start out pretty big, like \(2^{255}\). To make this more like blockchain, include some arbitrary bytes to be combined with the counter.


# ex2_9.py

import hashlib
import secrets

def convert_to_decimal(number: str, base: int) -> int:
    '''
    Taken from https://github.com/noahabe/numrep/blob/master/numrep.py

    Changes the first parameter (which is expected to be a hexadecimal number)
    into a base 10 integer. 
    '''
    ########### [FOR HEXADECIMAL ONLY]############
    if base == 16:
        convertion_table = {
            'A': 10,
            'B': 11,
            'C': 12,
            'D': 13,
            'E': 14,
            'F': 15,
        }
        def CONVERT(x: str): return convertion_table[x.upper()]
    #############################################
    decimal = 0
    position = len(number)-1
    for single_digit in number:
        if single_digit.isalpha():
            single_digit = CONVERT(single_digit)
        decimal += int(single_digit) * base**position
        position -= 1
    return decimal

def proof_of_work(random_block: bytes, target_number_exp: int) -> dict[str, str | bytes]:
    counter = 0
    target_number = 2 ** target_number_exp
    try: 
        while True: 
            h = hashlib.sha256()
            h.update(random_block)
            h.update(str(counter).encode('utf-8'))
            if convert_to_decimal(h.hexdigest(), 16) < target_number: 
                return {
                    "nonce": str(counter), 
                    "hash": h.hexdigest(),
                    "random_block": random_block[0:3], 
                    "target_number": f"2^{target_number_exp}"
                }
            counter += 1 
    except KeyboardInterrupt: 
        print("Exiting...")
        exit()

def main(start_target_number_exponent: int = 255, end_target_number_exponent: int = 250):
    # the random_block makes it look more like blockchain
    random_block = secrets.token_bytes(1000) # 1 Kilobyte of random bytes.
    for target_number_exp in range(start_target_number_exponent, end_target_number_exponent, -1): 
        retval = proof_of_work(random_block, target_number_exp)
        print(retval)

if __name__ == '__main__':
    start_target_number_exponent = int(input("Start target number exponent: "))
    end_target_number_exponent = int(input("End target number exponent: "))
    main(start_target_number_exponent, end_target_number_exponent)

The following video shows the execution of the above script: