Experiment 02
Title : Cryptography in Blockchain, Merkle Root Tree Hash.
Theory :
In blockchain systems, cryptography plays a vital role in ensuring the security and
integrity of the data. One important cryptographic concept is the Merkle Root,
which is used to efficiently verify the consistency and correctness of the data in
large datasets.
A Merkle Tree is a binary tree where each leaf node represents a hash of a data
block (in this case, a transaction), and each non-leaf node is the hash of its two child
nodes. The Merkle Root is the topmost hash that uniquely represents the entire set
of transactions in a block. It is used to validate the integrity of the block’s
transactions without needing to examine all individual transactions.
In this implementation:
1. Merkle Root Construction: The build_merkle_root function constructs the
Merkle Root by recursively hashing pairs of transaction hashes. If there is an
odd number of transactions, the last one is duplicated.
2. Block Creation: When a block is created, the Merkle Root of the current
transactions is calculated and added to the block. The block's hash is then
computed based on the Merkle Root, among other properties like the nonce,
timestamp, and previous hash.
3. Proof of Work: The proof_of_work method ensures that miners must find a
valid nonce that satisfies a predefined hash condition (e.g., starting with
'000') before a block can be mined and added to the blockchain. The Merkle
Root is included in the hash calculation, ensuring the integrity of the
transactions.
4. Flask Application: The application uses Flask to provide endpoints for mining
blocks, adding transactions, and retrieving the full blockchain. Postman or a
web browser can be used to interact with these endpoints.
Code :
import hashlib
import json
from time import time
from flask import Flask, jsonify, request
class Blockchain:
def __init__(self):
self.chain = []
self.current_transactions = []
# Create the genesis block
self.create_block(previous_hash='1', nonce=100)
@staticmethod
def build_merkle_root(transactions):
"""
Builds a Merkle Tree from a list of transactions and returns the Merkle Root.
This is a simple implementation that handles an odd number of transactions
by duplicating the last one.
"""
# Return a known hash if there are no transactions
if not transactions:
return hashlib.sha256("".encode()).hexdigest()
# 1. Hash each individual transaction
transaction_hashes = []
for tx in transactions:
# Use json.dumps to create a consistent string representation of the
transaction dict
tx_string = json.dumps(tx, sort_keys=True).encode()
transaction_hashes.append(hashlib.sha256(tx_string).hexdigest())
# 2. Build the tree by repeatedly hashing pairs of hashes
while len(transaction_hashes) > 1:
# If there is an odd number of hashes, duplicate the last one
if len(transaction_hashes) % 2 != 0:
transaction_hashes.append(transaction_hashes[-1])
next_level_hashes = []
# Iterate through the hashes in pairs
for i in range(0, len(transaction_hashes), 2):
# Concatenate the pair of hashes
combined_hash_str = transaction_hashes[i] + transaction_hashes[i+1]
# Hash the combined string
new_hash = hashlib.sha256(combined_hash_str.encode()).hexdigest()
next_level_hashes.append(new_hash)
# Move to the next level of the tree
transaction_hashes = next_level_hashes
# 3. The final remaining hash is the Merkle Root
return transaction_hashes[0]
def create_block(self, nonce, previous_hash):
"""
Creates a new block and adds it to the chain.
The block now includes the Merkle Root of the transactions.
"""
# The Merkle Root is calculated from the current list of transactions
merkle_root = self.build_merkle_root(self.current_transactions)
block = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'merkle_root': merkle_root, # Added Merkle Root to the block
'nonce': nonce,
'previous_hash': previous_hash,
}
# The block's hash is calculated after all content, including the Merkle Root, is
added
block['hash'] = self.hash(block)
# Reset the current list of transactions
self.current_transactions = []
self.chain.append(block)
return block
def create_transactions(self, sender, recipient, amount):
"""
Adds a new transaction to the list of current transactions.
These will be included in the next mined block.
"""
self.current_transactions.append({
'sender': sender,
'recipient': recipient,
'amount': amount,
})
return self.last_block['index'] + 1
@staticmethod
def hash(block):
"""
Hashes a block using SHA-256.
"""
# We must make sure that the Dictionary is Ordered, or we'll have inconsistent
hashes
block_string = json.dumps(block, sort_keys=True).encode()
return hashlib.sha256(block_string).hexdigest()
@property
def last_block(self):
"""
Returns the last block in the chain.
"""
return self.chain[-1]
def proof_of_work(self, previous_hash):
"""
Simple Proof of Work Algorithm:
- Find a number 'nonce' such that hash(previous_hash, transactions, nonce)
contains leading zeros.
"""
# We calculate the Merkle Root once before starting the mining process for
efficiency.
merkle_root = self.build_merkle_root(self.current_transactions)
nonce = 0
while True:
# The block being tested must have the same structure as a final block
block_to_test = {
'index': len(self.chain) + 1,
'timestamp': time(),
'transactions': self.current_transactions,
'merkle_root': merkle_root, # Include Merkle Root in the hash calculation
'nonce': nonce,
'previous_hash': previous_hash,
}
hash_value = self.hash(block_to_test)
# Check if the hash meets the difficulty criteria (e.g., starts with '000')
if hash_value.startswith('000'):
return nonce
else:
nonce += 1
# Flask app and routes go OUTSIDE the class
app = Flask(__name__)
blockchain = Blockchain()
@app.route('/mine', methods=['GET'])
def mine():
# A block can't be mined without transactions
if not blockchain.current_transactions:
return 'No transactions to mine', 400
# Run the proof of work algorithm to get the next nonce
previous_hash = blockchain.last_block['hash']
nonce = blockchain.proof_of_work(previous_hash)
# Create the new block and add it to the chain
block = blockchain.create_block(nonce, previous_hash)
# Forge the new Block by adding it to the chain
response = {
'message': 'New block mined',
'index': block['index'],
'transactions': block['transactions'],
'merkle_root': block['merkle_root'], # Added Merkle Root to the response
'nonce': block['nonce'],
'hash': block['hash'],
'previous_hash': block['previous_hash'],
}
return jsonify(response), 200
@app.route('/transactions/new', methods=['POST'])
def new_transaction():
values = request.get_json()
required = ['sender', 'recipient', 'amount']
if not all(k in values for k in required):
return 'Missing values', 400
index = blockchain.create_transactions(values['sender'], values['recipient'],
values['amount'])
response = {'message': f'Transaction will be added to Block {index}'}
return jsonify(response), 201
@app.route('/chain', methods=['GET'])
def full_chain():
response = {
'chain': blockchain.chain,
'length': len(blockchain.chain),
}
return jsonify(response), 200
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Output :
{
"chain": [
"hash":
"82601465dafbc826807fac3027986113df9616568af4585f6a1f6332439d0ada",
"index": 1,
"nonce": 100,
"previous_hash": "1",
"timestamp": 1753244887.372169,
"transactions": []
},
"hash":
"bc6a8af133e67449758c49ba64485f67c388451fc55965c83c4d37a65a77f049",
"index": 2,
"nonce": 2607,
"previous_hash":
"82601465dafbc826807fac3027986113df9616568af4585f6a1f6332439d0ada",
"timestamp": 1753245158.832769,
"transactions": [
"amount": 10,
"recipient": "Bob",
"sender": "Alice"
},
"amount": 10,
"recipient": "Ayush",
"sender": "Vyapti"
]
},
"hash":
"f615bb9f2e2ce375a76a1608987dbe7c8b81c5838347db8deff6b13775b5f136",
"index": 3,
"nonce": 1051,
"previous_hash":
"bc6a8af133e67449758c49ba64485f67c388451fc55965c83c4d37a65a77f049",
"timestamp": 1753245702.6182272,
"transactions": [
"amount": 10,
"recipient": "Ayush",
"sender": "Vyapti"
},
"hash":
"9e79fc88af9585d3691f8b8b33494c8c4c6925c331d0e9a938e37a661ab08ce0",
"index": 4,
"nonce": 5424,
"previous_hash":
"f615bb9f2e2ce375a76a1608987dbe7c8b81c5838347db8deff6b13775b5f136",
"timestamp": 1753246333.6656342,
"transactions": [
{
"amount": 10,
"recipient": "Aishu",
"sender": "Nikhil"
],
"length": 4
"chain": [
"hash":
"d1006dd8d6f2ee7981d69dd62b58b59bf3001ca62df16098105aa146dc279872",
"index": 1,
"merkle_root": "",
"nonce": 100,
"previous_hash": "1",
"timestamp": 1753249583.7738278,
"transactions": []
},
"hash":
"27936348034de0e436a9fc9e55bd830afe17d85e9405a200b6c75344891aecd8",
"index": 2,
"merkle_root":
"ee691c458c41248bbeb3fab0e41eaf6866229d2a1135644679f9f41fc337c640",
"nonce": 2336,
"previous_hash":
"d1006dd8d6f2ee7981d69dd62b58b59bf3001ca62df16098105aa146dc279872",
"timestamp": 1753249662.7293055,
"transactions": [
"amount": 10,
"recipient": "B",
"sender": "V"
],
"length": 2
}
Conclusion :
This implementation demonstrates the use of Merkle Trees in blockchain to
enhance data security and integrity. By including the Merkle Root in each block, the
system efficiently verifies that all transactions within the block are valid. The mining
process, powered by Proof of Work, ensures that the blockchain remains secure
and tamper-resistant. The Flask app provides an interface for interacting with the
blockchain, allowing transactions to be added, blocks to be mined, and the full
blockchain to be viewed. This method improves scalability and security by reducing
the data needed for transaction verification.