From aeaf25e42dc99f7502128380c307749a558a0a2f Mon Sep 17 00:00:00 2001 From: LetitiaGilbert <139886239+LetitiaGilbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:49:48 +0530 Subject: [PATCH 1/7] Add simple blockchain mining algorithm with PoW --- blockchain/simple_blockchain.py | 159 ++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 blockchain/simple_blockchain.py diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py new file mode 100644 index 000000000000..a582130da0a0 --- /dev/null +++ b/blockchain/simple_blockchain.py @@ -0,0 +1,159 @@ +""" +A simple blockchain implementation with Proof-of-Work (PoW). + +This educational example demonstrates: +- Block structure with index, timestamp, data, previous hash, nonce, and hash +- Mining via Proof-of-Work +- Chain integrity verification + +Author: Letitia Gilbert +""" + +import hashlib +from time import time +from typing import List, Tuple + + +class Block: + """ + Represents a single block in a blockchain. + + Attributes: + index (int): Position of the block in the chain. + timestamp (float): Creation time of the block. + data (str): Data stored in the block. + previous_hash (str): Hash of the previous block. + nonce (int): Number used for mining. + hash (str): SHA256 hash of the block's content. + """ + + def __init__(self, index: int, data: str, previous_hash: str, difficulty: int = 2): + self.index = index + self.timestamp = time() + self.data = data + self.previous_hash = previous_hash + self.nonce, self.hash = self.mine_block(difficulty) + + def compute_hash(self, nonce: int) -> str: + """ + Compute SHA256 hash of the block with given nonce. + + Args: + nonce (int): Nonce to include in the hash. + + Returns: + str: Hexadecimal hash string. + """ + block_string = f"{self.index}{self.timestamp}{self.data}{self.previous_hash}{nonce}" + return hashlib.sha256(block_string.encode()).hexdigest() + + def mine_block(self, difficulty: int) -> Tuple[int, str]: + """ + Simple Proof-of-Work mining algorithm. + + Args: + difficulty (int): Number of leading zeros required in the hash. + + Returns: + Tuple[int, str]: Valid nonce and resulting hash that satisfies difficulty. + + >>> block = Block(0, "Genesis", "0", difficulty=2) + >>> block.hash.startswith('00') + True + """ + if difficulty < 1: + raise ValueError("Difficulty must be at least 1") + nonce = 0 + target = '0' * difficulty + while True: + hash_result = self.compute_hash(nonce) + if hash_result.startswith(target): + return nonce, hash_result + nonce += 1 + + +class Blockchain: + """ + Simple blockchain class maintaining a list of blocks. + + Attributes: + chain (List[Block]): List of blocks forming the chain. + """ + + def __init__(self, difficulty: int = 2): + self.difficulty = difficulty + self.chain: List[Block] = [self.create_genesis_block()] + + def create_genesis_block(self) -> Block: + """ + Create the first block in the blockchain. + + Returns: + Block: Genesis block. + + >>> bc = Blockchain() + >>> bc.chain[0].index + 0 + >>> bc.chain[0].hash.startswith('00') + True + """ + return Block(0, "Genesis Block", "0", self.difficulty) + + def add_block(self, data: str) -> Block: + """ + Add a new block to the blockchain with given data. + + Args: + data (str): Data to store in the block. + + Returns: + Block: Newly added block. + + >>> bc = Blockchain() + >>> new_block = bc.add_block("Test Data") + >>> new_block.index + 1 + >>> new_block.previous_hash == bc.chain[0].hash + True + >>> new_block.hash.startswith('00') + True + >>> bc.is_valid() + True + """ + prev_hash = self.chain[-1].hash + new_block = Block(len(self.chain), data, prev_hash, self.difficulty) + self.chain.append(new_block) + return new_block + + def is_valid(self) -> bool: + """ + Verify the integrity of the blockchain. + + Returns: + bool: True if chain is valid, False otherwise. + + >>> bc = Blockchain() + >>> new_block = bc.add_block("Test") + >>> new_block.index + 1 + >>> new_block.previous_hash == bc.chain[0].hash + True + >>> new_block.hash.startswith('00') + True + >>> bc.is_valid() + True + >>> bc.chain[1].previous_hash = "tampered" + >>> bc.is_valid() + False + + """ + for i in range(1, len(self.chain)): + current = self.chain[i] + prev = self.chain[i - 1] + if current.previous_hash != prev.hash: + return False + if not current.hash.startswith('0' * self.difficulty): + return False + if current.hash != current.compute_hash(current.nonce): + return False + return True From c7f021008bec495a0f589d2eb3c1d586a4444828 Mon Sep 17 00:00:00 2001 From: LetitiaGilbert <139886239+LetitiaGilbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 22:56:16 +0530 Subject: [PATCH 2/7] Add type hints for __init__ methods --- blockchain/simple_blockchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index a582130da0a0..8ed1543c1f82 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -27,7 +27,7 @@ class Block: hash (str): SHA256 hash of the block's content. """ - def __init__(self, index: int, data: str, previous_hash: str, difficulty: int = 2): + def __init__(self, index: int, data: str, previous_hash: str, difficulty: int = 2) -> None: self.index = index self.timestamp = time() self.data = data From 9696d2201d0819ee7306e713bbcb703514864db3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:27:50 +0000 Subject: [PATCH 3/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- blockchain/simple_blockchain.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index 8ed1543c1f82..0233b4ed233d 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -27,7 +27,9 @@ class Block: hash (str): SHA256 hash of the block's content. """ - def __init__(self, index: int, data: str, previous_hash: str, difficulty: int = 2) -> None: + def __init__( + self, index: int, data: str, previous_hash: str, difficulty: int = 2 + ) -> None: self.index = index self.timestamp = time() self.data = data @@ -44,7 +46,9 @@ def compute_hash(self, nonce: int) -> str: Returns: str: Hexadecimal hash string. """ - block_string = f"{self.index}{self.timestamp}{self.data}{self.previous_hash}{nonce}" + block_string = ( + f"{self.index}{self.timestamp}{self.data}{self.previous_hash}{nonce}" + ) return hashlib.sha256(block_string.encode()).hexdigest() def mine_block(self, difficulty: int) -> Tuple[int, str]: @@ -64,7 +68,7 @@ def mine_block(self, difficulty: int) -> Tuple[int, str]: if difficulty < 1: raise ValueError("Difficulty must be at least 1") nonce = 0 - target = '0' * difficulty + target = "0" * difficulty while True: hash_result = self.compute_hash(nonce) if hash_result.startswith(target): @@ -152,7 +156,7 @@ def is_valid(self) -> bool: prev = self.chain[i - 1] if current.previous_hash != prev.hash: return False - if not current.hash.startswith('0' * self.difficulty): + if not current.hash.startswith("0" * self.difficulty): return False if current.hash != current.compute_hash(current.nonce): return False From e5f21e66513dd2f0c258f89cc96ccd82ef6e3648 Mon Sep 17 00:00:00 2001 From: LetitiaGilbert <139886239+LetitiaGilbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 23:01:19 +0530 Subject: [PATCH 4/7] Added compute hash doctest --- blockchain/simple_blockchain.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index 0233b4ed233d..3c95fd53a4f1 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -45,12 +45,19 @@ def compute_hash(self, nonce: int) -> str: Returns: str: Hexadecimal hash string. + + >>> block = Block(0, "Genesis", "0", difficulty=2) + >>> len(block.compute_hash(0)) == 64 + True + >>> isinstance(block.compute_hash(0), str) + True """ block_string = ( f"{self.index}{self.timestamp}{self.data}{self.previous_hash}{nonce}" ) return hashlib.sha256(block_string.encode()).hexdigest() + def mine_block(self, difficulty: int) -> Tuple[int, str]: """ Simple Proof-of-Work mining algorithm. From 6514a33aa94280f549622abec077f0b61d03a82c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 2 Oct 2025 17:33:22 +0000 Subject: [PATCH 5/7] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- blockchain/simple_blockchain.py | 1 - 1 file changed, 1 deletion(-) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index 3c95fd53a4f1..8342c189170d 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -57,7 +57,6 @@ def compute_hash(self, nonce: int) -> str: ) return hashlib.sha256(block_string.encode()).hexdigest() - def mine_block(self, difficulty: int) -> Tuple[int, str]: """ Simple Proof-of-Work mining algorithm. From c44d75354ec6522958f3d5d40617a97063d5a8da Mon Sep 17 00:00:00 2001 From: LetitiaGilbert <139886239+LetitiaGilbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 23:05:02 +0530 Subject: [PATCH 6/7] Added init return --- blockchain/simple_blockchain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index 8342c189170d..57f6e05f12ad 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -90,7 +90,7 @@ class Blockchain: chain (List[Block]): List of blocks forming the chain. """ - def __init__(self, difficulty: int = 2): + def __init__(self, difficulty: int = 2) -> None: self.difficulty = difficulty self.chain: List[Block] = [self.create_genesis_block()] From 00f590b91824331121553b455bd2aa37745af589 Mon Sep 17 00:00:00 2001 From: LetitiaGilbert <139886239+LetitiaGilbert@users.noreply.github.com> Date: Thu, 2 Oct 2025 23:11:50 +0530 Subject: [PATCH 7/7] Fix type hints and add doctests for simple blockchain --- blockchain/simple_blockchain.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/blockchain/simple_blockchain.py b/blockchain/simple_blockchain.py index 57f6e05f12ad..6adaeb9883d4 100644 --- a/blockchain/simple_blockchain.py +++ b/blockchain/simple_blockchain.py @@ -11,7 +11,6 @@ import hashlib from time import time -from typing import List, Tuple class Block: @@ -57,7 +56,7 @@ def compute_hash(self, nonce: int) -> str: ) return hashlib.sha256(block_string.encode()).hexdigest() - def mine_block(self, difficulty: int) -> Tuple[int, str]: + def mine_block(self, difficulty: int) -> tuple[int, str]: """ Simple Proof-of-Work mining algorithm. @@ -92,7 +91,7 @@ class Blockchain: def __init__(self, difficulty: int = 2) -> None: self.difficulty = difficulty - self.chain: List[Block] = [self.create_genesis_block()] + self.chain: list[Block] = [self.create_genesis_block()] def create_genesis_block(self) -> Block: """