Blockchain in Python

When I read an article about

JavaScript blockchain

, I was interested to get acquainted with ideas about blockchain development that are different from those that I already know. And as soon as I started reading the code, I wanted to compare it with similar Python code in order to also understand its differences from the code written in JavaScript.

The purpose of this material is to identify differences in languages. Think of it as a Python add-on to the original article.

Despite the fact that the original article appeared after its author got acquainted with example blockchain development in Python, I wanted to write Python code that reproduces the JavaScript code from the article as closely as possible. This will allow comparison of blockchain implementation in different languages.

I am also going to make my blockchain implementation, like in the JavaScript article, also fit in 60 lines.

About blockchain

Although I was going to repeat the structure of that material in order to arrive at the finished code in the same way as its author, I will nevertheless include something of my own here. In particular, I prefer a different definition of blockchain. It reads like this: “Blockchain is a system for registering information, performed in a way that makes it difficult or impossible to change information, hack the system or fraudulent information.”

Preparing the development environment

In this project we will be using Python, so if you don’t have it installed, find a distribution that suits your OS and install it.

Block creation

A block is an object that contains some information. Therefore, let’s start by creating a class

Block

:

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        # В this.data должна храниться информация, вроде сведений о транзакциях.
        self.data = [] if data is None else data

Class definitions

Block

Python and JavaScript are very similar. In Python, instead of

this

used by

self

, and the analogue of the method

constructor

is an

init

Comments are executed in a similar way too. Python uses the symbol to style single-line comments #, and in JavaScript – the construction //

Algorithm implementation sha256 I took from the library hashlib… In a JS project, it is taken from the package crypto

from hashlib import sha256

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.hash = self.getHash()
        self.prevHash = None # Хеш предыдущего блока

    def getHash(self):
        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        return hash.hexdigest()

In method

getHash

it all starts with an empty hash, which we form using the data stored in the block. The hash is calculated based on the hash of the previous block, the time stamp, and the data stored in the block. All of this is converted into a sequence of bytes using the method

.encode('utf-8')

Blockchain

Let’s take a class

Blockchain

class Blockchain:
    def __init__(self):
        # В этом свойстве будут содержаться все блоки.
        self.chain = []

The blockchain classes are similar in both languages.

To create a primary block, we simply call Block with the current time stamp, for which we use time()… To do this, we need to import the library time

Converting a number to a string is done in Python using the function str()rather than using the method toString()as done in JavaScript.

from time import time

class Blockchain:
    def __init__(self):
        # Создаём первичный блок
        self.chain = [Block(str(int(time())))]

The method for getting the freshest block looks similar. Only here, unlike a JavaScript project, to find out the length of the block chain, instead of the property

length

function is used

len()

    def getLastBlock(self):
        return self.chain[len(self.chain) - 1]

To add a block to the blockchain, we simply call the method

addBlock

… The code turned out to be almost the same as in the JS project, only here, instead of the method

push()

, the method is used

append()

def addBlock(self, block):
        # Так как мы добавляем новый блок, prevHash будет хешем предыдущего последнего блока.
        block.prevHash = self.getLastBlock().hash
        # Так как теперь в prevHash имеется значение, мы должны пересчитать хеш блока.
        block.hash = block.getHash()
        self.chain.append(block)

Blockchain verification

In the method used to validate the blockchain, we use the function

range()

… This is a major difference between our code and the JS project code. And besides, since we don’t use constants in Python, we just use regular variables here.

When checking conditions in Python, instead of ||, used or

def isValid(self):
    # Перед перебором цепочки блоков нужно установить i в 1, так как до первичного блока никаких блоков нет. В результате мы начинаем со второго блока.
    for i in range(1, len(self.chain)):
        currentBlock = self.chain[i]
        prevBlock = self.chain[i - 1]

        # Проверка
        if (currentBlock.hash != currentBlock.getHash() or prevBlock hash != currentBlock.prevHash):
            return False

    return True

Proof of Work Algorithm

We can implement a proof-of-work algorithm by starting by adding to the class

Block

method

mine()

and properties

nonce

… It is worth being careful here, since the property

nonce

must be declared before calling the method

self.getHash()

… Otherwise, an error will be thrown.

AttributeError: 'Block' object has no attribute 'nonce'

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.prevHash = None # хеш предыдущего блока
        self.nonce = 0
        self.hash = self.getHash()

    # Наша хеш-функция.
    def getHash(self):

        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        hash.update(str(self.nonce).encode('utf-8'))
        return hash.hexdigest()

    def mine(self, difficulty):
        # Тут запускается цикл, работающий до тех пор, пока хеш не будет начинаться со строки
        # 0...000 длины <difficulty>.
        while self.hash[:difficulty] != '0' * difficulty:
            # Инкрементируем nonce, что позволяет получить совершенно новый хеш.
            self.nonce += 1
            # Пересчитываем хеш блока с учётом нового значения nonce.
            self.hash = self.getHash()

Let’s create a property that will store the complexity:

self.difficulty = 1

Let’s edit the method

addBlock

:

    def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self.chain.append(block)

Blockchain testing

Let’s import the module and use the class

Blockchain

just like the same class in a JS project:

from blockchain import Block
from blockchain import Blockchain
from time import time

JeChain = Blockchain()

# Добавим новый блок
JeChain.addBlock(Block(str(int(time())), ({"from": "John", "to": "Bob", "amount": 100})))
# (Это - всего лишь интересный эксперимент, для создания настоящей криптовалюты обычно нужно сделать намного больше, чем сделали мы).

# Вывод обновлённого блокчейна
print(JeChain)

The output from this code should look something like this:

[
    {
        "data": [],
        "timestamp": "1636153236",
        "nonce": 0,
        "hash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678",
        "prevHash": null
    },
    {
        "data": {
            "from": "John",
            "to": "Bob",
            "amount": 100
        },
        "timestamp": "1636153236",
        "nonce": 14,
        "hash": "038f82c6e6605acfcad4ade04e454eaa1cfa3d17f8c2980f1ee474eefb9613e9",
        "prevHash": "4caa5f684eb3871cb0eea217a6d043896b3775f047e699d92bd29d0285541678"
    }
]

But all this will work only after adding it to the class.

Blockchain

method

__repr__()

:

import json

    def __repr__(self):
        return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item.prevHash} for item in self.chain], indent=4)

Addition: difficulty and block time

To set the block time, we need the corresponding property:

self.blockTime = 30000

Let’s take a look at the ternary operator used in the difficulty setting system. If we rewrite the JS construct with the ternary operator in Python, we get the following:

(if_test_is_false, if_test_is_true)[test]

:

def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self.chain.append(block)

        self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime]

The final Python code (without normal formatting) fits into the promised 60 lines:

# -*- coding: utf-8 -*-

from hashlib import sha256
import json
from time import time

class Block:

    def __init__(self, timestamp=None, data=None):
        self.timestamp = timestamp or time()
        self.data = [] if data is None else data
        self.prevHash = None
        self.nonce = 0
        self.hash = self.getHash()

    def getHash(self):

        hash = sha256()
        hash.update(str(self.prevHash).encode('utf-8'))
        hash.update(str(self.timestamp).encode('utf-8'))
        hash.update(str(self.data).encode('utf-8'))
        hash.update(str(self.nonce).encode('utf-8'))
        return hash.hexdigest()

    def mine(self, difficulty):
        while self.hash[:difficulty] != '0' * difficulty:
            self.nonce += 1
            self.hash = self.getHash()

class Blockchain:

    def __init__(self):
        self.chain = [Block(str(int(time())))]
        self.difficulty = 1
        self.blockTime = 30000

    def getLastBlock(self):
        return self.chain[len(self.chain) - 1]

    def addBlock(self, block):
        block.prevHash = self.getLastBlock().hash
        block.hash = block.getHash()
        block.mine(self.difficulty)
        self.chain.append(block)

        self.difficulty += (-1, 1)[int(time()) - int(self.getLastBlock().timestamp) < self.blockTime]

    def isValid(self):
        for i in range(1, len(self.chain)):
            currentBlock = self.chain[i]
            prevBlock = self.chain[i - 1]

            if (currentBlock.hash != currentBlock.getHash() or prevBlock.hash != currentBlock.prevHash):
                return False

        return True

    def __repr__(self):
        return json.dumps([{'data': item.data, 'timestamp': item.timestamp, 'nonce': item.nonce, 'hash': item.hash, 'prevHash': item.prevHash} for item in self.chain], indent=4)

I hope you enjoyed both content and found something useful in them.

If you needed to create a blockchain system – what tools would you use?

Similar Posts

Leave a Reply