Skip to content

Instantly share code, notes, and snippets.

@psiborg
Last active October 14, 2024 03:41
Show Gist options
  • Save psiborg/913f3ee96cfee0357ed5c7cfb2e54e11 to your computer and use it in GitHub Desktop.
Save psiborg/913f3ee96cfee0357ed5c7cfb2e54e11 to your computer and use it in GitHub Desktop.
Basic blockchain implementation in Python
#!/usr/bin/env python3
# Setup for Raspberry Pi
# pip3 install matplotlib
# Setup for Ubuntu
# sudo apt install python3-pip3
# sudo apt install python3-matplotlib
import hashlib
import time
import matplotlib.pyplot as plt
class Block:
def __init__(self, index, transactions, previous_hash):
self.index = index
self.timestamp = time.time()
self.transactions = transactions
self.previous_hash = previous_hash
self.hash = self.compute_hash()
def compute_hash(self):
"""
Returns the hash of the block by hashing the block's contents.
"""
block_string = f"{self.index}{self.timestamp}{self.transactions}{self.previous_hash}".encode()
return hashlib.sha256(block_string).hexdigest()
def proof_of_work(self, difficulty):
"""
Simple proof of work where the hash starts with a certain number of zeros.
"""
self.nonce = 0
computed_hash = self.compute_hash()
while not computed_hash.startswith('0' * difficulty):
self.nonce += 1
computed_hash = self.compute_hash()
return computed_hash
class Blockchain:
def __init__(self):
self.chain = []
self.transactions = [] # Initialize the list to hold transactions
self.create_genesis_block()
def create_genesis_block(self):
"""
Generates the first block in the chain, called the genesis block.
"""
genesis_block = Block(0, "Genesis Block", "0")
self.chain.append(genesis_block)
def get_latest_block(self):
return self.chain[-1]
def add_block(self, new_block):
new_block.previous_hash = self.get_latest_block().hash
new_block.hash = new_block.compute_hash()
self.chain.append(new_block)
def is_block_valid(self, block):
"""
Checks if the current block's hash matches the computed hash,
ensuring it's valid.
"""
return block.hash == block.compute_hash()
def is_chain_valid(self):
"""
Validates the blockchain by checking the hashes of each block.
"""
for i in range(1, len(self.chain)):
current_block = self.chain[i]
previous_block = self.chain[i - 1]
# Check if the current block's hash is valid
if current_block.hash != current_block.compute_hash():
return False
# Check if the current block's previous hash matches the previous block's hash
if current_block.previous_hash != previous_block.hash:
return False
return True
def add_new_transaction(self, transaction):
"""
Adds a new transaction to the list of transactions.
"""
self.transactions.append(transaction)
def mine(self):
"""
Add a new block to the chain with the list of transactions.
"""
if not self.transactions:
return False
new_block = Block(index=len(self.chain), transactions=self.transactions, previous_hash=self.get_latest_block().hash)
self.add_block(new_block)
self.transactions = [] # Reset the transaction list
return True
def print_ledger(self):
"""
Prints the entire blockchain ledger, displaying the details of each block.
"""
for block in self.chain:
print(f"Block {block.index}:")
print(f"Timestamp: {block.timestamp}")
print(f"Transactions: {block.transactions}")
print(f"Previous Hash: {block.previous_hash}")
print(f"Hash: {block.hash}")
print("-" * 40) # Separator between blocks
def visualize_ledger1(self):
"""
Visualize the blockchain using a simple block diagram.
"""
fig, ax = plt.subplots(figsize=(10, len(self.chain)))
for i, block in enumerate(self.chain):
block_str = f"Block {block.index}\nHash: {block.hash[:6]}...\nPrev: {block.previous_hash[:6]}..."
rect = plt.Rectangle((0, i), 2, 1, edgecolor='black', facecolor='lightblue', lw=2)
ax.add_patch(rect)
ax.text(1, i + 0.5, block_str, va='center', ha='center', fontsize=10)
# Draw an arrow from the previous block to the current block
if i > 0:
ax.annotate('', xy=(0, i), xytext=(0, i - 1),
arrowprops=dict(facecolor='black', shrink=0.05))
ax.set_xlim(-1, 3)
ax.set_ylim(-1, len(self.chain))
ax.set_axis_off()
plt.show()
def visualize_ledger(self):
"""
Visualize the blockchain using a simple block diagram,
highlighting valid blocks in green and invalid blocks in red.
"""
fig, ax = plt.subplots(figsize=(10, len(self.chain)))
for i, block in enumerate(self.chain):
# Check if the block is valid
is_valid = self.is_block_valid(block)
color = 'green' if is_valid else 'red'
block_str = (f"Block {block.index}\n"
f"Hash: {block.hash[:6]}...\n"
f"Prev: {block.previous_hash[:6]}...")
rect = plt.Rectangle((0, i), 2, 1, edgecolor='black', facecolor=color, lw=2)
ax.add_patch(rect)
ax.text(1, i + 0.5, block_str, va='center', ha='center', fontsize=10)
# Draw an arrow from the previous block to the current block
if i > 0:
ax.annotate('', xy=(0, i), xytext=(0, i - 1),
arrowprops=dict(facecolor='black', shrink=0.05))
ax.set_xlim(-1, 3)
ax.set_ylim(-1, len(self.chain))
ax.set_axis_off()
plt.show()
blockchain = Blockchain()
# Add transactions
blockchain.add_new_transaction({"sender": "Alice", "receiver": "Bob", "amount": 50})
blockchain.mine()
blockchain.add_new_transaction({"sender": "Bob", "receiver": "Charlie", "amount": 25})
# Mine a new block
blockchain.mine()
# Add another block with new transactions
blockchain.add_new_transaction({"sender": "Charlie", "receiver": "Dave", "amount": 100})
blockchain.mine()
# Blockchain is valid initially
print("Checking if blockchain is valid:")
print(blockchain.is_chain_valid()) # Should return True
# Tamper with the blockchain: Modify the data of a block
blockchain.chain[1].transactions = [{"sender": "Eve", "receiver": "Mallory", "amount": 1000}]
# Re-run the validation to detect the tampering
print("\nAfter tampering with the blockchain:")
print(blockchain.is_chain_valid()) # Should return False, as the block has been tampered with
# Check if the blockchain is valid
print(f"Blockchain valid? {blockchain.is_chain_valid()}")
# Output the ledger
blockchain.print_ledger()
# Visualize the ledger
blockchain.visualize_ledger()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment