Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

2 Commits
Β 
Β 

Repository files navigation

πŸ“˜ LAB 3: Blockchain Architecture – P2P Simulation & Block Header Analysis

Duration: 2.5–3 hours | Difficulty: Intermediate | Platform: Jupyter Notebook / Google Colab
Alignment: Course Objectives 1, 3, 7 (Understand blockchain structures, analyze consensus/network models, assess scalability)
Prerequisites: Python fundamentals, familiarity with SHA-256 hashing (Week 2), basic understanding of distributed systems


πŸ“¦ FILES PROVIDED

File 1: requirements.txt

web3==6.15.1
ipywidgets
numpy

File 2: lab3_blockchain_architecture.ipynb

Copy each cell block below into a new Jupyter Notebook or Google Colab session in exact order.

🟦 Cell 1: Setup & Imports

# πŸ“¦ Install dependencies (Colab only; skip if running locally)
import sys
!{sys.executable} -m pip install web3 numpy -q

import struct
import hashlib
import random
import time
from datetime import datetime

🟦 Cell 2: Lab Context & Objectives

## 🌐 LAB 3: Blockchain Architecture & Network Simulation

**Real-World Context:**
- Bitcoin blocks use an 80-byte header structure globally recognized by ~15,000 full nodes
- P2P gossip protocols propagate blocks across continents in <5 seconds
- Architecture dictates the trust-throughput-finality trade-off: Bitcoin ~7 TPS vs PostgreSQL ~10,000+ TPS

**Your Tasks:**
1. Simulate a decentralized P2P network with gossip-based block propagation
2. Parse a real Bitcoin block header and verify cryptographic chaining
3. Calculate architectural throughput limits and compare with traditional systems
4. Run the auto-grader for immediate feedback (0–100%)

⚠️ **Do not modify grading cells.** Fill only `# YOUR CODE HERE` sections.

🟦 Cell 3: Part A – P2P Network Simulation

# === PART A: P2P GOSSIP SIMULATION ===
class P2PNode:
    def __init__(self, node_id):
        self.node_id = node_id
        self.peers = []
        self.known_blocks = set()
        self.received_at = None

    def add_peers(self, peers_list):
        self.peers = [p for p in peers_list if p.node_id != self.node_id]

    def receive_block(self, block_hash, timestamp):
        if block_hash not in self.known_blocks:
            self.known_blocks.add(block_hash)
            self.received_at = timestamp
            return True
        return False

def simulate_gossip_network(num_nodes=10, source_id=0, max_hops=5):
    """Simulate block propagation using gossip protocol."""
    random.seed(42)  # Deterministic for grading
    nodes = [P2PNode(i) for i in range(num_nodes)]
    
    # Connect nodes in a random mesh (each node connects to 3 peers)
    for node in nodes:
        candidates = [n for n in nodes if n not in node.peers and n.node_id != node.node_id]
        if candidates:
            node.peers = random.sample(candidates, min(3, len(candidates)))
            
    # Seed block
    block_hash = f"0x{hashlib.sha256(b'block_800000').hexdigest()[:16]}"
    start_time = time.time()
    
    # Queue for propagation: (node, block_hash, current_hop, timestamp)
    queue = [(nodes[source_id], block_hash, 0, start_time)]
    visited = set()
    
    while queue:
        current_node, b_hash, hops, ts = queue.pop(0)
        if current_node.receive_block(b_hash, ts) and hops < max_hops:
            # Gossip to 2 random peers
            targets = random.sample(current_node.peers, min(2, len(current_node.peers)))
            for peer in targets:
                if b_hash not in peer.known_blocks:
                    queue.append((peer, b_hash, hops + 1, time.time()))
                    
    propagation_time = time.time() - start_time
    covered = sum(1 for n in nodes if b_hash in n.known_blocks)
    return covered, propagation_time, nodes

# Quick Test
covered, prop_time, net = simulate_gossip_network(10)
print(f"Nodes receiving block: {covered}/10 | Time: {prop_time:.4f}s")

🟦 Cell 4: Part B – Block Header Parser

# === PART B: BITCOIN BLOCK HEADER PARSER ===
def parse_block_header(header_hex):
    """Parse 80-byte Bitcoin block header and return structured dict."""
    # YOUR CODE HERE: Use struct.unpack to extract version, prev_hash, merkle_root, timestamp, bits, nonce
    # Note: Bitcoin stores integers in little-endian, hashes are reversed in explorers
    pass

def verify_chain_linkage(prev_block_hash_hex, current_header_hex):
    """Verify current header's prev_hash matches actual previous block hash."""
    # YOUR CODE HERE: Parse current header, compare prev_hash field with provided prev_block_hash_hex
    pass

def verify_pow_difficulty(bits_hex, block_hash_hex):
    """Check if block hash meets difficulty target defined by 'bits' field."""
    # YOUR CODE HERE: Convert bits to target, compare block_hash <= target
    pass

# Test Data (Bitcoin Block #800,000 - April 2024 Halving)
TEST_HEADER_HEX = "200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" # Simplified for lab; real parser handles actual hex
# Note: Full lab uses deterministic mock header for grading stability

🟦 Cell 5: Part C – Architecture Throughput Analysis

# === PART C: ARCHITECTURAL TRADE-OFF CALCULATIONS ===
def calculate_theoretical_tps(block_size_mb=1.0, avg_tx_size_bytes=250, block_interval_sec=600):
    """Calculate max theoretical TPS for Bitcoin-like architecture."""
    # YOUR CODE HERE: Convert MB to bytes, divide by avg_tx_size, then by interval
    pass

def compare_architecture_throughput(blockchain_tps, traditional_tps=10000):
    """Return overhead ratio and architectural insight."""
    # YOUR CODE HERE: Compute (traditional_tps / blockchain_tps) and return ratio + brief reason
    pass

# Quick Test
tps = calculate_theoretical_tps()
print(f"Theoretical BTC TPS: {tps:.2f} | Traditional DB: ~10,000+ TPS")

🟦 Cell 6: πŸ€– AUTO-GRADER (Run Last)

# === πŸ€– AUTO-GRADER ===
class Lab3Grader:
    def __init__(self):
        self.score = 0
        self.total = 100
        self.feedback = []

    def grade(self, name, func, *args, expected, points):
        try:
            result = func(*args)
            if result == expected:
                self.score += points
                self.feedback.append(f"βœ… {name}: Passed (+{points})")
            else:
                self.feedback.append(f"❌ {name}: Expected {expected}, got {result}")
        except Exception as e:
            self.feedback.append(f"❌ {name}: Error - {str(e)}")

    def grade_p2p(self):
        covered, _, _ = simulate_gossip_network(10)
        self.grade("P2P Coverage", lambda: covered, expected=10, points=20)

    def grade_parser(self):
        # Mock deterministic header for grading
        mock_header = bytes([
            0x00, 0x00, 0x00, 0x20,  # Version 32
            0x01]*32,                 # Prev hash (simplified)
            0x02]*32,                 # Merkle root
            0x40, 0x1a, 0x11, 0x60,   # Timestamp 1611717184
            0x17, 0x0e, 0x4b, 0x1f,   # Bits
            0x85, 0x00, 0x00, 0x00    # Nonce 133
        ])
        mock_hex = mock_header.hex()
        parsed = parse_block_header(mock_hex)
        self.grade("Header Parsing - Version", lambda: parsed.get('version'), expected=32, points=10)
        self.grade("Header Parsing - Timestamp", lambda: parsed.get('timestamp'), expected=1611717184, points=10)
        self.grade("Header Parsing - Nonce", lambda: parsed.get('nonce'), expected=133, points=10)

    def grade_architecture(self):
        tps = calculate_theoretical_tps()
        self.grade("TPS Calculation", lambda: round(tps, 2), expected=6.67, points=25)
        ratio = compare_architecture_throughput(6.67)[0]
        self.grade("Throughput Ratio", lambda: ratio > 1000, expected=True, points=15)

    def report(self):
        self.grade_p2p()
        self.grade_parser()
        self.grade_architecture()
        print("\nπŸ“Š LAB 3 AUTO-GRADER RESULTS")
        print("="*30)
        for msg in self.feedback: print(msg)
        print(f"\n🎯 FINAL SCORE: {self.score}/{self.total} ({(self.score/self.total)*100:.1f}%)")
        if self.score >= 80: print("βœ… PASS - Architecture concepts verified")
        else: print("⚠️ REVIEW NEEDED - Check failed tests above")

# Run Grader
grader = Lab3Grader()
grader.report()

🧭 STEP-BY-STEP GUIDELINES

πŸ”§ Step 1: Environment Setup (10 mins)

  1. Open Google Colab or launch Jupyter Notebook locally.
  2. Create a new notebook: lab3_blockchain_architecture.ipynb
  3. Paste Cell 1 and run (Shift+Enter). This installs required packages.
  4. Paste Cell 2 to review objectives.

🌐 Step 2: Implement P2P Gossip Simulation (25 mins)

  1. Paste Cell 3.
  2. The simulation is pre-written but requires you to understand the gossip mechanism.
  3. Modify simulate_gossip_network to track propagation hops per node (add self.hops to P2PNode).
  4. Run the cell. Verify output shows 10/10 coverage with deterministic seed.
  5. Concept Check: Why does gossip scale to O(log N) propagation time? How does this differ from centralized broadcast?

πŸ” Step 3: Implement Block Header Parser (30 mins)

  1. Paste Cell 4.
  2. Replace pass in parse_block_header with:
    b = bytes.fromhex(header_hex)
    return {
        'version': struct.unpack('<I', b[0:4])[0],
        'prev_hash': b[4:36][::-1].hex(),
        'merkle_root': b[36:68][::-1].hex(),
        'timestamp': struct.unpack('<I', b[68:72])[0],
        'bits': struct.unpack('<I', b[72:76])[0],
        'nonce': struct.unpack('<I', b[76:80])[0]
    }
  3. Implement verify_chain_linkage and verify_pow_difficulty using parsed fields.
  4. Run cell. Verify parser correctly extracts version, timestamp, nonce from mock header.

πŸ“Š Step 4: Calculate Architectural Trade-offs (15 mins)

  1. Paste Cell 5.
  2. Implement calculate_theoretical_tps:
    block_bytes = block_size_mb * 1024 * 1024
    tx_count = block_bytes / avg_tx_size_bytes
    return tx_count / block_interval_sec
  3. Implement compare_architecture_throughput to return (ratio, "Blockchain sacrifices throughput for decentralization & BFT").
  4. Run cell. Confirm TPS β‰ˆ 6.67 and ratio > 1000.

πŸ€– Step 5: Run Auto-Grader & Submit (5 mins)

  1. Paste Cell 6 at notebook bottom.
  2. Run it. Grader tests P2P coverage, header parsing accuracy, and throughput math.
  3. Submission Requirements:
    • Export notebook as PDF
    • Include screenshots of:
      • Auto-grader score β‰₯80%
      • P2P network output (nodes covered, propagation time)
      • Parsed header fields vs expected values
    • Upload as CSN4104_Week3_Lab_YourName.pdf

πŸ› οΈ TROUBLESHOOTING GUIDE

Issue Solution
ModuleNotFoundError: No module named 'web3' Run Cell 1 again. If local: pip install web3 numpy
P2P coverage < 10/10 Check random.seed(42) placement. Ensure max_hops >= 5 and peer list isn't empty
Parser returns wrong version/timestamp Bitcoin uses little-endian for integers (<I). Hashes need [::-1] reversal
TPS calculation mismatch Verify: 1MB = 1,048,576 bytes. 1,048,576 / 250 / 600 β‰ˆ 6.67
Auto-grader shows Error Ensure function signatures exactly match templates. Do not change parameter names

πŸ“Š ASSESSMENT RUBRIC (Instructor Use)

Criteria Auto-Graded Manual Review Weight
P2G Network Simulation βœ… Coverage & propagation logic Graph/diagram of gossip flow 25%
Block Header Parsing βœ… Field extraction accuracy Correct endianness handling, hash reversal 30%
Architectural Analysis βœ… TPS math, ratio calculation Insight on CAP trade-offs, real-world justification 25%
Submission & Documentation ❌ PDF quality, reflection, troubleshooting notes 20%

πŸ”— OPEN-SOURCE & REAL-WORLD ALIGNMENT

  • web3.py / bitcoinjs-lib: Industry-standard node interaction libraries
  • P2P Gossip: Modeled after Bitcoin's INV/GETDATA protocol & Ethereum's devp2p
  • Block Structure: Matches BIP 31 header specification (80 bytes, little-endian integers)
  • Throughput Math: Aligns with Cambridge CBEI & Visa/BTC architectural comparisons
  • πŸ“¦ GitHub Repos for Extension:

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors