Building a Blockchain in Python, Part 1 : Blocks and Blockchain

in #blockchain7 years ago (edited)

Welcome!

As an exercise in visualizing the underlying concepts of blockchain behind cryptocurrency, we're going to build a simple blockchain in Python. Let's start off by defining a block:

from hashlib import sha256
import json


class Block:
    def __init__(self, index, previousHash, timestamp, transactions, nonce):
        self.index = index
        self.previousHash = previousHash
        self.timestamp = timestamp
        self.nonce = nonce
        self.transactions = transactions

        self.hash = self.calculate_hash()

    def calculate_hash(self):
        return sha256((str(self.index) + str(self.nonce) + str(self.timestamp) + self.previousHash + 
                                       json.dumps(self.transactions).encode()).hexdigest()

    def __str__(self):
        return json.dumps({'index': self.index,
                            'previousHash': self.previousHash,
                            'timestamp': self.timestamp,
                            'nonce': self.nonce,
                            'hash': self.hash,
                            'transactions': self.transactions},
                            indent=4)

Great! Our blocks can take all the needed inputs, calculate its own hash, and also has a human readable output when you print it, using the __ str __ function. You can try it out like this:

>>> print(Block(0, "0", 0, [], 0))
{
    "index": 0,
    "previousHash": "0",
    "timestamp": 0,
    "nonce": 0,
    "hash": "2275a3de822eb41385eabe14d70c3485d769af8b8346c9e9dcd11e7c2e42475e",
    "transactions": []
}

Now that we have blocks, we need to organize them into a simple blockchain, and we can do that by adding this code:

import time

class BlockChain:
  def __init__(self, difficulty):
    self.difficulty = difficulty
    self.chain = [self.create_genesis_block()]

  def create_genesis_block(self):
    nonce = 0
    CandidateBlock = Block(0, "", int(time.time()), [], nonce)
    while not CandidateBlock.calculate_hash().startswith(self.difficulty):
      nonce += 1
      CandidateBlock = Block(0, "", int(time.time()), [], nonce)
    return CandidateBlock

  def mine_block(self):
    nonce = 0
    CandidateBlock = Block(
        len(self.chain), self.chain[-1].calculate_hash(), [], nonce)
    while not CandidateBlock.calculate_hash().startswith(self.difficulty):
      nonce += 1
      CandidateBlock = Block(
          len(self.chain), self.chain[-1].calculate_hash(), [], nonce)
    return CandidateBlock

The Blockchain object is quite simple for now, because the blockchain can currently only run on one computer, and there is no validation, but the basic ideas of generation of a genesis block and building off of that block with the mine_block function are intact. You can see the result like this:

>>> PyChain = BlockChain('1')
>>> print(PyChain.chain[0])
{
    "index": 0,
    "previousHash": "",
    "timestamp": 1525927596,
    "nonce": 32,
    "hash": "111e8ca42c9d755625d3d6068fa6c6f7c85cb2e87d6af02d5106ee75f14c8b68",
    "transactions": []
}
>>> print(PyChain.mine_block())
{
    "index": 1,
    "previousHash": "111e8ca42c9d755625d3d6068fa6c6f7c85cb2e87d6af02d5106ee75f14c8b68",
    "timestamp": 1525927606,
    "nonce": 12,
    "hash": "1a67c8fe0d0a0992c227c794d639f86de1e42d5fdc211876dc421d9c714dd566",
    "transactions": []
}

As you can see, the newly mined block links to the previous block by including its hash as its previous hash. Additionally, you can see that all blocks mined on this chain have a hash starting with '1', because I indicated I wanted that as the chain's difficulty. In reality, this difficulty is constantly in flux.

In the next part, we'll work on implementing other features of a blockchain to our very own blockchain!