Torpe Blockchain (तोर्पे ब्लोक्कचैन)
A minimal blockchain data structure to understand the blockchain basics like hashing, nonce, states, genesis blocks etc.
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}