© Daniel van Flymen 2020
D. van FlymenLearn Blockchain by Building Onehttps://doi.org/10.1007/978-1-4842-5171-3_4

4. Proof of Work

Daniel van Flymen1 
(1)
New York, NY, USA
 

The primary goal of this chapter is to clearly explain how blocks are mined in a blockchain. Indirectly, this explains how new currency comes into being, as well as how disparate people forming a network can all reach consensus (agree on the state of the blockchain). And so, you’ll leave this chapter with a practical understanding of Proof of Work—the protocol which achieves this.

We’ll also see that due to the peer-to-peer, decentralized nature of peer-to-peer networks, there is never one single blockchain at any given time. There are many valid chains at once, but over time, peers reach consensus on what the authoritative chain is.

In the previous chapter, we implemented the methods in our blockchain class. We also learnt how blocks are “chained” together using hashes and implemented methods to create and add new blocks. It’s crucial that you have a firm grasp of hashing as this chapter is where the real magic begins—we’ll be clarifying the concept of “mining” or, more formally, creating proofs of work.

Interacting with the blockchain class using iPython

A great tool to help with interacting with the Python interpreter is iPython. It adds tab completion and syntax highlighting to your Python interpreter, making interaction easier to see and understand. Install it by first enabling your virtual environment and using pip:
$ pip install ipython
Then invoke the Python interpreter:
$ ipython -i funcoin/examples/chapter_3/full_blockchain.py
Python 3.8.3 (default, Oct 13 2019, 18:33:25)
Type 'copyright', 'credits', or 'license' for more information.
IPython 7.12.0 -- An enhanced Interactive Python. Type '?' for help.
In [1]:
Try invoking your blockchain and using tab completion on the methods:
In [1]: bc = Blockchain()
Creating genesis block
Created block 0
In [2]: bc.h____________________
           hash
           chain
           last_block
           new_block
           pending_transactions

Going forward, you’ll need to be proactive in playing with your blockchain class to gain an intuitive understanding of the upcoming concepts.

Introduction to proof of work

In forming Bitcoin, Satoshi Nakamoto’s genius was not in creating, but rather in the amalgamation of preexisting breakthroughs by cryptographers concerned with the erosion of privacy. These breakthroughs span disparate fields and topics, such as addressing the problem of centralization and trust by leveraging peer-to-peer networks originally used to share files over the Internet (torrents); or more specifically using technology invented to combat email spam to solve the problem of “minting” money.

But Bitcoin’s biggest breakthrough was the leveraging of proofs of work to form consensus among peers on a network who don’t know each other, and more importantly, don’t trust each other. In simpler words: in a decentralized network where everyone has an equal say, we need a way of agreeing on things; things like whether a transaction is fraudulent or if a blockchain is valid. Now, we may be jumping the gun here (we’ll clarify most of these concepts soon), but it’s important to zoom out and see where the different parts of the puzzle fit.

Proof of work or, more formally, a proof-of-work (POW) algorithm describes the method in which new blocks are added to a blockchain. Bitcoin and Ethereum (at present) both use proof-of-work algorithms to add new blocks to their blockchains. Proof-of-work algorithms sound pretty complicated, but in practice they’re actually very simple. To get the cogs turning before we dive in, I’d like to pose a problem to you: How do you prove to someone that you did work?

Think about it for a minute or two. Let’s say that you’re digging holes in your backyard and you find a gold nugget. Is that gold nugget proof that effort was done to obtain it? I’d argue that it’s proof that someone did some work. Let’s switch gears here for a second—how about proof that you ran a marathon? You could probably strap a camera to your head and record the whole race, step by step. That would be sufficient, wouldn’t it? Maybe, but the footage could’ve been modified, or someone else may have been wearing the camera. Perhaps we’re grabbing at straws here, but I’m trying to draw your attention to just how difficult it is to prove that work was done without doubt (or trust), especially if you’re behind a computer, which is pretty handy at faking things.

In lay terms, proof-of-work algorithms achieve just this—they allow you to prove that your computer did some work. More technically, proof-of-work algorithms are consensus mechanisms—if you can prove that work was done, then you can show your proof to someone else who can easily verify that it is true. There are many variants of these algorithms, but we’re going to focus on a simple example to sink this in.

We’ve already explained that POW is the algorithm for mining new blocks. The actual method of the proof of work algorithm is simple—to discover a number which satisfies a small mathematical problem: the number must be difficult to find but easy to verify, computationally speaking, by anyone on the network. This is the core idea behind proof of work: difficult to do, but easy to verify. We’ll look at a very simple example to help this sink in.

A trivial example of Proof of Work

Let’s decide that the hash of some integer x multiplied by another integer y must end in 0. So, hash(x * y) = dedb2ac23dc...0. And for this simplified example, let’s fix x = 5. Implementing this in Python
 1  from hashlib import sha256
 2
 3
 4  x = 5
 5  y = 0  # We don't know what y should be yet...
 6
 7  while  sha256(f'{x*y}'.encode()).hexdigest()[-1] != "0":
 8      y += 1
 9
10  print(f'The solution is y = {y}')
The solution here is y = 21. Since the produced hash ends in 0
>>> sha256(f"{5 * 21}".encode()).hexdigest()
'1253e9373e781b7500266caa55150e08e210bc8cd8cc70d89985e3600155e860'

In Bitcoin, the proof-of-work algorithm has a name; it is called Hashcash . And it’s not too different from the preceding basic example. It’s the algorithm that miners race to solve in order to create a new block. In general, the difficulty is determined by the number of zeros at the beginning of a string; the miners are then rewarded for their solution by receiving a set number of Bitcoins—in a transaction; and the network is able to easily verify their solution, as we did earlier.

An analogy: Jigsaw puzzles

Jigsaw puzzles are a good example of a proof-of-work algorithm, because the proof of work is in the unanimous (human) identification of an image. If we told a computer to unscramble a jigsaw puzzle
../images/475256_1_En_4_Chapter/475256_1_En_4_Figa_HTML.jpg
it would have to brute-force every combination
../images/475256_1_En_4_Chapter/475256_1_En_4_Figb_HTML.jpg
and it would keep this up forever, until we hit “stop” when we saw an image appear:
../images/475256_1_En_4_Chapter/475256_1_En_4_Figc_HTML.jpg

And you could now show this image to someone, and they would know beyond a doubt that work was done.

Implementing Proof of Work

Let’s implement a similar algorithm for our blockchain. Our rule will be similar to the preceding example:

Find a number p that when hashed with the previous block’s solution, a hash with four leading 0s is produced.

Continuing our example from the previous chapter, let’s add two new methods to our Blockchain class, proof_of_work and valid_hash, for mining and validating hashes, respectively:
 1  import json
 2
 3  from datetime import datetime
 4  from hashlib import sha256
 5
 6
 7  class Blockchain(object):
 8      def __init__ (self):
 9          self.chain = []
10          self.pending_transactions = []
11
12          # Create the genesis block
13          print("Creating genesis block")
14          self.new_block()
15
16      def new_block(self, previous_hash=None):
17          block = {
18              'index': len(self.chain),
19              'timestamp': datetime.utcnow().isoformat(),
20              'transactions': self.pending_transactions,
21              'previous_hash':  previous_hash,
22              'nonce': None,
23          }
24
25          # Get the hash of this new block, and add it to the block
26          block_hash = self.hash(block)
27          block["hash"] = block_hash
28
29          # Reset the list of pending transactions
30          self.pending_transactions = []
31          # Add the block to the chain
32          self.chain.append(block)
33
34          print(f"Created block {block['index']}")
35          return block
36
37      @staticmethod
38      def hash(block):
39          # We ensure the dictionary is sorted or we'll have inconsistent hashes
40          block_string = json.dumps(block, sort_keys=True).encode()
41          return sha256(block_string).hexdigest()
42
43      def last_block(self):
44          # Returns the last block in the chain (if there are blocks)
45          return self.chain[-1] if self.chain else None
46
47      def proof_of_work(self):
48          pass
49
50      def valid_hash(self):
51          pass

We’ve also added a new field to our block structure called nonce, which means a nonsense string. Think of a nonce as a once-off random number, which will be used as an important source of randomness for our blocks. For these nonces, we’ll generate a random 64-bit hex number using Python’s random module:

import random
>>> format(random.getrandbits(64), "x")
'828ad30173db207b'
Let’s flesh out our two methods, proof_of_work and valid_hash, starting with the latter. valid_hash is simple; it needs to check if the hash of a block begins with a certain number of zeros, let’s say 4:
@staticmethod
def valid_block(block):
    return block["hash"].startswith("0000")
Now, let’s implement a simple proof-of-work algorithm which creates a new block and checks if it has a valid hash:
1  def proof_of_work(self):
2      while True:
3          new_block = self.new_block()
4          if self.valid_block(new_block):
5              break
6
7      self.chain.append(new_block)
8      print("Found a new block: ", new_block)
The code is self-explanatory:
  1. 1.

    We create a new block (which contains a random nonce).

     
  2. 2.

    Then hash the block to see if it’s valid.

     
  3. 3.

    If it’s valid, then we return it; else we repeat from 1. forever.

     
Let’s add these methods to our Blockchain class:
 1  import json
 2  import random
 3
 4  from datetime import datetime
 5  from hashlib import sha256
 6
 7
 8  class Blockchain(object):
 9      def __init__ (self):
10          self.chain = []
11          self.pending_transactions = []
12
13          # Create the genesis block
14          print("Creating genesis block")
15          self.chain.append(self.new_block())
16
17      def new_block(self):
18          block = {
19              'index': len(self.chain),
20              'timestamp': datetime.utcnow().isoformat(),
21              'transactions': self.pending_transactions,
22              'previous_hash': self.last_block["hash"] if self.last_block else None,
23              'nonce': format(random.getrandbits(64), "x"),
24          }
25
26          # Get the hash of this new block, and add it to the block
27          block_hash = self.hash(block)
28          block["hash"] = block_hash
29
30          # Reset the list of pending transactions
31          self.pending_transactions = []
32
33          return block
34
35      @staticmethod
36      def hash(block):
37          # We ensure the dictionary is sorted, or we'll have inconsistent hashes
38          block_string = json.dumps(block, sort_keys=True).encode()
39          return sha256(block_string).hexdigest()
40
41      @property
42      def last_block(self):
43          # Returns the last block in the chain (if there are blocks)
44          return self.chain[-1] if self.chain else None
45
46      @staticmethod
47      def valid_block(block):
48          # Checks if a block's hash starts with 0000
49          return block["hash"].startswith("0000")
50
51      def proof_of_work(self):
52          while True:
53              new_block  =  self.new_block()
54              if self.valid_block(new_block):
55                  break
56
57          self.chain.append(new_block)
58          print("Found a new block: ", new_block)
We need to make two important deletions:
  • Delete the print statement inside the new_block() method, or you’ll land up with a lot of unwanted output in your console when mining.

  • On line 33, inside the new_block() method, delete the line self.chain.append(block) which adds the new block to the chain (we don’t want unvalidated blocks) being added.

Now it’s time to fire up ipython, instantiate the Blockchain class, and let’s mine some blocks:
In [1]: from blockchain import Blockchain
In [2]: bc = Blockchain()
Creating genesis block
In [3]: bc.proof_of_work()
Found a new block:
{
  "index": 1,
  "timestamp": "2020-02-07T04:39:03.920459",
  "transactions": [],
  "previous_hash": "1a93bd623f3e3aba2c9d4ee9193d36904382e7416b2fb854dbf3fc6a13cab702",
  "nonce":  "8b9991ef3ea9eb19",
  "hash": "0000075c4a6d3ae6ab06677887618cf41255d7a1ba10220bb3e9bc8c8ecec80e"
}
In [4]: bc.proof_of_work()
Found a new block:
{
  "index": 2,
  "timestamp": "2020-02-07T04:39:14.440383",
  "transactions": [],
  "previous_hash": "0000075c4a6d3ae6ab06677887618cf41255d7a1ba10220bb3e9bc8c8ecec80e",
  "nonce": "b9a579d2cfa587b0",
  "hash": "00009d974a58fb689ad280c121897eb2f6da699db2c4bd2a3312dc41d478c182"
}
In [5]: bc.proof_of_work()
Found a new block:
{
  "index": 3,
  "timestamp": "2020-02-07T04:39:20.744179",
  "transactions": [],
  "previous_hash": "00009d974a58fb689ad280c121897eb2f6da699db2c4bd2a3312dc41d478c182",
  "nonce":  "f4de39bb07bce043",
  "hash": "000003910a7ad733f89a9ff13ffea5b5e08fe69d581a4ecd30f237f95800221e"
}

To adjust the difficulty of our mining, we could modify the number of leading zeros. But four is sufficient. You’ll find out that the addition of a single leading zero makes an exponential difference to the time required to find a solution. In Bitcoin, the difficulty is adjusted every 2016 blocks by consensus on the network. The difficulty is made harder to stave off the inevitable acceleration of hardware.

Monetary Supply

Now you have a fully functioning blockchain class that implements mining to create new blocks. This is extremely close to how mining happens in production blockchains like Bitcoin. It’s the mining algorithm which generates new Bitcoin: a miner, when completing the mining of a block inserts a transaction from nobody to himself with an amount of Bitcoin that halves every 210,000 blocks. This is how new Bitcoins come into existence.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.222.67.251