Torpe Blockchain (तोर्पे ब्लोक्कचैन)

A minimal blockchain data structure to understand the blockchain basics like hashing, nonce, states, genesis blocks etc.

blockchain

Hash Function

A hash function is any function that can be used to map data of arbitrary size to data of a fixed size. The values returned by a hash function are called hash values, hash codes, digests, or simply hashes.

import hashlib
def hash_sha256(raw):
    raw = str(raw).encode('utf-8')
    return hashlib.sha256(raw).hexdigest() 
hash_sha256('torpe_blockchain')
'e5367197a1f12480ec761306f2fa9d15b494d5a80e5a806713df9e60943f4faf'
hash_sha256('torpe_blockchain')
'e5367197a1f12480ec761306f2fa9d15b494d5a80e5a806713df9e60943f4faf'
hash_sha256('torpe_blockchaiN')
'71fe90af906a9afa507ee54287595df6d7267df05428b3f91429371ebd27cb3e'

Hashes for the same text are the same as seen above. Even when one character of the string is changed, the hash generated as a result seems to be completely random.

SHA-256 collisions have not been found yet.

Nonce

Number that can only be used once

An arbitrary numer used in cryptography to ensure uniqueness and prevent the rerunning of transactions (known as replay attack).

def hash_sha256_nonce(raw):
    raw_bytes = str(raw).encode('utf-8')
    hashed = hashlib.sha256(raw_bytes).hexdigest() 
    nonce = 0
    while (hashed[:5] != '00000'):
        nonce = nonce+1
        raw.update({'nonce': nonce})
        raw_bytes = str(raw).encode('utf-8')
        hashed = hashlib.sha256(raw_bytes).hexdigest() 
    return raw, hashed
print (hash_sha256_nonce({'hello': 'proof_of_work', 'nonce': 0}))
({'hello': 'proof_of_work', 'nonce': 623228}, '00000a9d45728c6f4d1eff383dab4d96b753495c8b312ecb5d1858116885ee55')

Proof of work

The proof of work for this case will be to generate hashes with five leading zeros (by incrementing the nonce). This is the “mining” part.

Block

Blocks hold batches of valid transactions that are hashed and encoded into a Merkle tree Each block includes the cryptographic hash of the prior block in the blockchain, linking the two. The linked blocks form a chain. This iterative process confirms the integrity of the previous block, all the way back to the original genesis block.

Genesis Block

A genesis block or block0 is the first block of a block chain. The genesis block is almost always hardcoded into the software of the applications that utilize its block chain. It is a special case in that it does not reference a previous block

import datetime
# Lets assume 5 person were given 100 coins each
state = {
    'Person_1': 100, 
    'Person_2': 100, 
    'Person_3': 100, 
    'Person_4': 100, 
    'Person_5': 100
}
block0_data = {
    'timestamp': datetime.datetime.now(),
    'index': 0,
    'previous': None,
    'transactions': [state], 
    'nonce': 0
}

raw, hashed = hash_sha256_nonce(block0_data)
block0 = {
    'hash': hashed,
    'data': raw,
}
block0
{'hash': '0000044b11859efa71c555a87a68090f1f602cf8bcd35bb5446c3c5532f5ad5e',
 'data': {'timestamp': datetime.datetime(2020, 9, 9, 7, 37, 44, 877080),
  'index': 0,
  'previous': None,
  'transactions': [{'Person_1': 100,
    'Person_2': 100,
    'Person_3': 100,
    'Person_4': 100,
    'Person_5': 100}],
  'nonce': 2700821}}

This is the genesis block or block 0 here.

Transactions

Lets create some random transactions. The transactions for the demo purpose follow +x, -x semantic. See the examples below.

import random
def random_transaction(state):
    temp_list = list(state.keys())
    random.shuffle(temp_list)
    # randomly select two persons
    first_person = temp_list.pop()
    second_person = temp_list.pop()
    receive = random.randint(1, 10)
    give = -receive
    return {
        first_person:receive, 
        second_person:give
    }
test_transactions = [random_transaction(state) for x in range(5)]
test_transactions
[{'Person_3': 5, 'Person_5': -5},
 {'Person_3': 7, 'Person_5': -7},
 {'Person_4': 1, 'Person_1': -1},
 {'Person_2': 4, 'Person_1': -4},
 {'Person_4': 4, 'Person_5': -4}]

Updating State

def update_state(transaction, state):
    state = state.copy()
    for key in transaction:
        state[key] = state.get(key, 0) + transaction[key]
    return state
for transaction in test_transactions:
    state = update_state(transaction, state)
state
{'Person_1': 95,
 'Person_2': 104,
 'Person_3': 112,
 'Person_4': 105,
 'Person_5': 84}

Valid Transactions

def check_transaction_validity(transaction, state):
    # check neg vs pos
    if sum(transaction.values()) is not 0:
        return False
    # check if amount in wallet to give
    for key in transaction.keys():
        if state.get(key, 0) + transaction[key] < 0:
            return False
    return True
for transaction in test_transactions:
    print (check_transaction_validity(transaction, state))
True
True
True
True
True
# No balance
print (check_transaction_validity({'A': 5, 'B': -5}, {'A': 0, 'B': 0}))
False
# Bad transaction
print (check_transaction_validity({'A': 5, 'B': 5}, {'A': 50, 'B': 50}))
False

Initial State

# Let us reset
# Lets assume 5 person were given 100 coins each
state = {
    'Person_1': 100, 
    'Person_2': 100, 
    'Person_3': 100, 
    'Person_4': 100, 
    'Person_5': 100
}

blockchain = []
# Adding the genesis block
blockchain.append(block0)
blockchain
[{'hash': '0000044b11859efa71c555a87a68090f1f602cf8bcd35bb5446c3c5532f5ad5e',
  'data': {'timestamp': datetime.datetime(2020, 9, 9, 7, 37, 44, 877080),
   'index': 0,
   'previous': None,
   'transactions': [{'Person_1': 100,
     'Person_2': 100,
     'Person_3': 100,
     'Person_4': 100,
     'Person_5': 100}],
   'nonce': 2700821}}]

Non-genesis block / New block

def new_block(transactions, blockchain):
    previous_block = blockchain[-1]
    data = {
        'timestamp': datetime.datetime.now(),
        'index': previous_block['data']['index'] + 1,
        'previous': previous_block['hash'],
        'transactions': transactions,
        'nonce': 0
        }
    
    raw, hashed = hash_sha256_nonce(data)
    block = {'hash': hashed, 'data': raw}
    return block
sample_transactions = [random_transaction(state) for x in range(50)]
sample_transactions
[{'Person_5': 5, 'Person_1': -5},
 {'Person_3': 10, 'Person_1': -10},
 {'Person_2': 5, 'Person_4': -5},
 {'Person_5': 9, 'Person_3': -9},
 {'Person_3': 1, 'Person_2': -1},
 {'Person_1': 9, 'Person_3': -9},
 {'Person_1': 7, 'Person_3': -7},
 {'Person_5': 4, 'Person_3': -4},
 {'Person_5': 2, 'Person_4': -2},
 {'Person_2': 4, 'Person_3': -4},
 {'Person_3': 5, 'Person_5': -5},
 {'Person_5': 1, 'Person_1': -1},
 {'Person_1': 1, 'Person_2': -1},
 {'Person_2': 7, 'Person_1': -7},
 {'Person_2': 7, 'Person_5': -7},
 {'Person_3': 2, 'Person_1': -2},
 {'Person_3': 3, 'Person_1': -3},
 {'Person_3': 3, 'Person_2': -3},
 {'Person_3': 6, 'Person_1': -6},
 {'Person_1': 5, 'Person_3': -5},
 {'Person_2': 4, 'Person_3': -4},
 {'Person_2': 1, 'Person_5': -1},
 {'Person_1': 3, 'Person_2': -3},
 {'Person_1': 10, 'Person_2': -10},
 {'Person_3': 9, 'Person_5': -9},
 {'Person_1': 3, 'Person_4': -3},
 {'Person_4': 2, 'Person_3': -2},
 {'Person_5': 6, 'Person_3': -6},
 {'Person_2': 9, 'Person_1': -9},
 {'Person_3': 3, 'Person_4': -3},
 {'Person_3': 10, 'Person_4': -10},
 {'Person_1': 9, 'Person_4': -9},
 {'Person_2': 3, 'Person_1': -3},
 {'Person_2': 6, 'Person_3': -6},
 {'Person_4': 4, 'Person_1': -4},
 {'Person_3': 7, 'Person_1': -7},
 {'Person_3': 7, 'Person_1': -7},
 {'Person_3': 5, 'Person_2': -5},
 {'Person_3': 10, 'Person_2': -10},
 {'Person_2': 1, 'Person_1': -1},
 {'Person_1': 3, 'Person_5': -3},
 {'Person_4': 4, 'Person_5': -4},
 {'Person_1': 3, 'Person_2': -3},
 {'Person_4': 1, 'Person_1': -1},
 {'Person_5': 1, 'Person_4': -1},
 {'Person_3': 5, 'Person_2': -5},
 {'Person_1': 8, 'Person_4': -8},
 {'Person_3': 8, 'Person_4': -8},
 {'Person_3': 7, 'Person_4': -7},
 {'Person_2': 1, 'Person_1': -1}]

Transactions per block

Bitcoin blocks used to contain fewer than 200 transactions and the largest number of transactions in a block was 1,976 at the time this answer was originally written (May 2013). In meanwhile (November 2017) the average number of transaction per block is well above 1500 with peaks above 2200.

# Assume block size is 5
transactions_per_block = 5
transaction_block = []

for transaction in sample_transactions:
    if check_transaction_validity(transaction, state):
        state = update_state(transaction, state)
        transaction_block.append(transaction)
        
        if len(transaction_block) >= transactions_per_block:
            blockchain.append(new_block(transaction_block, blockchain))
            transaction_block = []
import pprint
pp = pprint.PrettyPrinter()
for block in blockchain:
    pp.pprint(block)
    print('\n************************************************************************************\n')
{'data': {'index': 0,
          'nonce': 2700821,
          'previous': None,
          'timestamp': datetime.datetime(2020, 9, 9, 7, 37, 44, 877080),
          'transactions': [{'Person_1': 100,
                            'Person_2': 100,
                            'Person_3': 100,
                            'Person_4': 100,
                            'Person_5': 100}]},
 'hash': '0000044b11859efa71c555a87a68090f1f602cf8bcd35bb5446c3c5532f5ad5e'}

************************************************************************************

{'data': {'index': 1,
          'nonce': 2395688,
          'previous': '0000044b11859efa71c555a87a68090f1f602cf8bcd35bb5446c3c5532f5ad5e',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 37, 58, 781195),
          'transactions': [{'Person_1': -5, 'Person_5': 5},
                           {'Person_1': -10, 'Person_3': 10},
                           {'Person_2': 5, 'Person_4': -5},
                           {'Person_3': -9, 'Person_5': 9},
                           {'Person_2': -1, 'Person_3': 1}]},
 'hash': '00000303c468fe76fe73dfc089b856af02eda615c296ddde95c0c60999561048'}

************************************************************************************

{'data': {'index': 2,
          'nonce': 2475862,
          'previous': '00000303c468fe76fe73dfc089b856af02eda615c296ddde95c0c60999561048',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 38, 16, 454296),
          'transactions': [{'Person_1': 9, 'Person_3': -9},
                           {'Person_1': 7, 'Person_3': -7},
                           {'Person_3': -4, 'Person_5': 4},
                           {'Person_4': -2, 'Person_5': 2},
                           {'Person_2': 4, 'Person_3': -4}]},
 'hash': '00000804a078686673c26bd3d391649da822c27a9eb87c2a86f07be5be7667c0'}

************************************************************************************

{'data': {'index': 3,
          'nonce': 843595,
          'previous': '00000804a078686673c26bd3d391649da822c27a9eb87c2a86f07be5be7667c0',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 38, 34, 360082),
          'transactions': [{'Person_3': 5, 'Person_5': -5},
                           {'Person_1': -1, 'Person_5': 1},
                           {'Person_1': 1, 'Person_2': -1},
                           {'Person_1': -7, 'Person_2': 7},
                           {'Person_2': 7, 'Person_5': -7}]},
 'hash': '0000004fdf3bc704c17b3f38c8bcb0306443e24db0c5971c7494cea67e618fcd'}

************************************************************************************

{'data': {'index': 4,
          'nonce': 456491,
          'previous': '0000004fdf3bc704c17b3f38c8bcb0306443e24db0c5971c7494cea67e618fcd',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 38, 40, 443823),
          'transactions': [{'Person_1': -2, 'Person_3': 2},
                           {'Person_1': -3, 'Person_3': 3},
                           {'Person_2': -3, 'Person_3': 3},
                           {'Person_1': -6, 'Person_3': 6},
                           {'Person_1': 5, 'Person_3': -5}]},
 'hash': '000001ea0d5ee3360f087e8fe25e643a8e749af6b42bf8c3045303a3e4dc80a5'}

************************************************************************************

{'data': {'index': 5,
          'nonce': 1793595,
          'previous': '000001ea0d5ee3360f087e8fe25e643a8e749af6b42bf8c3045303a3e4dc80a5',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 38, 43, 617133),
          'transactions': [{'Person_2': 4, 'Person_3': -4},
                           {'Person_2': 1, 'Person_5': -1},
                           {'Person_1': 3, 'Person_2': -3},
                           {'Person_1': 10, 'Person_2': -10},
                           {'Person_3': 9, 'Person_5': -9}]},
 'hash': '00000101ed3a68b2340f553e1a373e7a590823fb34ee1e2f3c2e4ed44b647c2a'}

************************************************************************************

{'data': {'index': 6,
          'nonce': 618433,
          'previous': '00000101ed3a68b2340f553e1a373e7a590823fb34ee1e2f3c2e4ed44b647c2a',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 38, 56, 256050),
          'transactions': [{'Person_1': 3, 'Person_4': -3},
                           {'Person_3': -2, 'Person_4': 2},
                           {'Person_3': -6, 'Person_5': 6},
                           {'Person_1': -9, 'Person_2': 9},
                           {'Person_3': 3, 'Person_4': -3}]},
 'hash': '00000b2395e1efb034610e89196aafe9407417bbac1cd60f300adccea3cd880d'}

************************************************************************************

{'data': {'index': 7,
          'nonce': 4087255,
          'previous': '00000b2395e1efb034610e89196aafe9407417bbac1cd60f300adccea3cd880d',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 39, 0, 705135),
          'transactions': [{'Person_3': 10, 'Person_4': -10},
                           {'Person_1': 9, 'Person_4': -9},
                           {'Person_1': -3, 'Person_2': 3},
                           {'Person_2': 6, 'Person_3': -6},
                           {'Person_1': -4, 'Person_4': 4}]},
 'hash': '00000ef1b9a090a4b6636f86fa3d264dbcab13f5cf87d994637e656b6ec4abad'}

************************************************************************************

{'data': {'index': 8,
          'nonce': 991443,
          'previous': '00000ef1b9a090a4b6636f86fa3d264dbcab13f5cf87d994637e656b6ec4abad',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 39, 29, 983211),
          'transactions': [{'Person_1': -7, 'Person_3': 7},
                           {'Person_1': -7, 'Person_3': 7},
                           {'Person_2': -5, 'Person_3': 5},
                           {'Person_2': -10, 'Person_3': 10},
                           {'Person_1': -1, 'Person_2': 1}]},
 'hash': '0000021d8da5dab8b274a0146db6ddd6b1c12f34d751f84e9755fae1ed6a42b3'}

************************************************************************************

{'data': {'index': 9,
          'nonce': 24885,
          'previous': '0000021d8da5dab8b274a0146db6ddd6b1c12f34d751f84e9755fae1ed6a42b3',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 39, 37, 55377),
          'transactions': [{'Person_1': 3, 'Person_5': -3},
                           {'Person_4': 4, 'Person_5': -4},
                           {'Person_1': 3, 'Person_2': -3},
                           {'Person_1': -1, 'Person_4': 1},
                           {'Person_4': -1, 'Person_5': 1}]},
 'hash': '000002090602b338740f28ac66dfdc2f9949c8c40303574e0be08ad7033550bb'}

************************************************************************************

{'data': {'index': 10,
          'nonce': 545615,
          'previous': '000002090602b338740f28ac66dfdc2f9949c8c40303574e0be08ad7033550bb',
          'timestamp': datetime.datetime(2020, 9, 9, 7, 39, 37, 232552),
          'transactions': [{'Person_2': -5, 'Person_3': 5},
                           {'Person_1': 8, 'Person_4': -8},
                           {'Person_3': 8, 'Person_4': -8},
                           {'Person_3': 7, 'Person_4': -7},
                           {'Person_1': -1, 'Person_2': 1}]},
 'hash': '00000d59159d699830d454d68d2077ed40da8ae060ec67c19be0814d35a61e6f'}

************************************************************************************

The current state

Syncing for the first time

def validate_block(block, parent, state):    
    error_msg = 'Error in %d' % block['data']['index']

    # check block hash
    assert block['hash'] == hash_sha256(block['data']), error_msg

    # check block indices
    assert block['data']['index'] == parent['data']['index'] + 1, error_msg

    # check previous hash
    assert block['data']['previous'] == parent['hash'], error_msg
    
    # validate all transactions
    for transaction in block['data']['transactions']:
        assert check_transaction_validity(transaction, state), error_msg
        state = update_state(transaction, state)
        
    return state
def check_chain(blockchain):
    state = {}

    for transaction in blockchain[0]['data']['transactions']:
        state = update_state(transaction, state)

    parent = blockchain[0]
    
    for block in blockchain[1:]:
        state = validate_block(block, parent, state)
        parent = block

    return state

check_chain(blockchain)
{'Person_1': 94,
 'Person_2': 107,
 'Person_3': 145,
 'Person_4': 55,
 'Person_5': 99}