Last active
October 14, 2024 03:41
-
-
Save psiborg/913f3ee96cfee0357ed5c7cfb2e54e11 to your computer and use it in GitHub Desktop.
Basic blockchain implementation in Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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