Proof of work

So, we have three participants in this case: Nelson, Marie, and Sky. But there is another type of participant too: the one who writes into the blockchain is called—in blockchain parlance—the miner. In order to put the transaction into the blockchain, the miner is required to do some work first.

Previously, we had three blocks (block_A, block_B, and block_C), but now we have a candidate block (block_D), which we want to add into the blockchain as follows:

block_D = Block()
block_D.id = 4
block_D.history = 'Sky loves turtle'
block_D.parent_id = block_C.id

But instead of adding block_D to the blockchain just like that, we first require the miner to do some puzzle work. We serialize that block and ask the miner to apply an extra string, which, when appended to the serialization string of that block, will show the hash output with at least five zeros in the front, if it is hashed.

Those are a lot of words to chew on. First things first, we serialize the block:

import json
block_serialized = json.dumps(block_D.__dict__).encode('utf-8')
print(block_serialized)
b'{"history": "Sky loves turtle", "parent_id": 3, "id": 4}'

If the serialized block is hashed, what does it mean if we want the hash output to have at least five zeros at the front? It means that we want the output to look like this:

00000aa21def23ee175073c6b3c89b96cfe618b6083dae98d2a92c919c1329be

Alternatively, we want it to look like this:

00000be7b5347509c9df55ca35d27091b41a93acb2afd1447d1cc3e4b70c96ab

So, the puzzle is something like this:

string serialization + answer = hash output with (at least) 5 leading zeros

The miner needs to guess the correct answer. If this puzzle is converted to Python code, it would be something like this:

answer = ?
input = b'{"history": "Sky loves turtle", "parent_id": 3, "id": 4}' + answer
output = hashlib.sha256(input).hexdigest()
// output needs to be 00000???????????????????????????????????????????????????????????

So, how could the miner solve a problem like this? We can use brute force:

import hashlib

payload = b'{"history": "Sky loves turtle", "parent_id": 3, "id": 4}'
for i in range(10000000):
nonce = str(i).encode('utf-8')
result = hashlib.sha256(payload + nonce).hexdigest()
if result[0:5] == '00000':
print(i)
print(result)
break

The result would therefore be as follows:

184798
00000ae01f4cd7806e2a1fccd72fb18679cb07ede3a2a7ef028a0ecfd4aec153

This means that the answer is 184798, or the hash output of {"history": "Sky loves turtle", "parent_id": 3, "id": 4}184798 is the one that has five leading zeros. In that simple script, we iterate from 0 to 9999999 and append that into the input. This is a naive method, but it works. Of course, you could also append with characters other than numbers, such as a, b, or c.

Now, try to increase the number of leading zeros to six, or even ten. In this case, can you find the hash output? If there is no output, you could increase the range limit from 10000000 to an even higher number, such as 1000000000000. Once you get an appreciation of the hard work that goes into this, try to comprehend this: Bitcoin required around 18 leading zeros in the hash output at the time that this book was being written. The number of leading zeros is not static and changes according to the situation (but you don't need to worry about this).

So, why do we need proof of work? We need to take a look at the idea of consensus first.