Skip to content

Commit aeaf25e

Browse files
Add simple blockchain mining algorithm with PoW
1 parent a71618f commit aeaf25e

File tree

1 file changed

+159
-0
lines changed

1 file changed

+159
-0
lines changed

blockchain/simple_blockchain.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
A simple blockchain implementation with Proof-of-Work (PoW).
3+
4+
This educational example demonstrates:
5+
- Block structure with index, timestamp, data, previous hash, nonce, and hash
6+
- Mining via Proof-of-Work
7+
- Chain integrity verification
8+
9+
Author: Letitia Gilbert
10+
"""
11+
12+
import hashlib
13+
from time import time
14+
from typing import List, Tuple
15+
16+
17+
class Block:
18+
"""
19+
Represents a single block in a blockchain.
20+
21+
Attributes:
22+
index (int): Position of the block in the chain.
23+
timestamp (float): Creation time of the block.
24+
data (str): Data stored in the block.
25+
previous_hash (str): Hash of the previous block.
26+
nonce (int): Number used for mining.
27+
hash (str): SHA256 hash of the block's content.
28+
"""
29+
30+
def __init__(self, index: int, data: str, previous_hash: str, difficulty: int = 2):
31+
self.index = index
32+
self.timestamp = time()
33+
self.data = data
34+
self.previous_hash = previous_hash
35+
self.nonce, self.hash = self.mine_block(difficulty)
36+
37+
def compute_hash(self, nonce: int) -> str:
38+
"""
39+
Compute SHA256 hash of the block with given nonce.
40+
41+
Args:
42+
nonce (int): Nonce to include in the hash.
43+
44+
Returns:
45+
str: Hexadecimal hash string.
46+
"""
47+
block_string = f"{self.index}{self.timestamp}{self.data}{self.previous_hash}{nonce}"
48+
return hashlib.sha256(block_string.encode()).hexdigest()
49+
50+
def mine_block(self, difficulty: int) -> Tuple[int, str]:
51+
"""
52+
Simple Proof-of-Work mining algorithm.
53+
54+
Args:
55+
difficulty (int): Number of leading zeros required in the hash.
56+
57+
Returns:
58+
Tuple[int, str]: Valid nonce and resulting hash that satisfies difficulty.
59+
60+
>>> block = Block(0, "Genesis", "0", difficulty=2)
61+
>>> block.hash.startswith('00')
62+
True
63+
"""
64+
if difficulty < 1:
65+
raise ValueError("Difficulty must be at least 1")
66+
nonce = 0
67+
target = '0' * difficulty
68+
while True:
69+
hash_result = self.compute_hash(nonce)
70+
if hash_result.startswith(target):
71+
return nonce, hash_result
72+
nonce += 1
73+
74+
75+
class Blockchain:
76+
"""
77+
Simple blockchain class maintaining a list of blocks.
78+
79+
Attributes:
80+
chain (List[Block]): List of blocks forming the chain.
81+
"""
82+
83+
def __init__(self, difficulty: int = 2):
84+
self.difficulty = difficulty
85+
self.chain: List[Block] = [self.create_genesis_block()]
86+
87+
def create_genesis_block(self) -> Block:
88+
"""
89+
Create the first block in the blockchain.
90+
91+
Returns:
92+
Block: Genesis block.
93+
94+
>>> bc = Blockchain()
95+
>>> bc.chain[0].index
96+
0
97+
>>> bc.chain[0].hash.startswith('00')
98+
True
99+
"""
100+
return Block(0, "Genesis Block", "0", self.difficulty)
101+
102+
def add_block(self, data: str) -> Block:
103+
"""
104+
Add a new block to the blockchain with given data.
105+
106+
Args:
107+
data (str): Data to store in the block.
108+
109+
Returns:
110+
Block: Newly added block.
111+
112+
>>> bc = Blockchain()
113+
>>> new_block = bc.add_block("Test Data")
114+
>>> new_block.index
115+
1
116+
>>> new_block.previous_hash == bc.chain[0].hash
117+
True
118+
>>> new_block.hash.startswith('00')
119+
True
120+
>>> bc.is_valid()
121+
True
122+
"""
123+
prev_hash = self.chain[-1].hash
124+
new_block = Block(len(self.chain), data, prev_hash, self.difficulty)
125+
self.chain.append(new_block)
126+
return new_block
127+
128+
def is_valid(self) -> bool:
129+
"""
130+
Verify the integrity of the blockchain.
131+
132+
Returns:
133+
bool: True if chain is valid, False otherwise.
134+
135+
>>> bc = Blockchain()
136+
>>> new_block = bc.add_block("Test")
137+
>>> new_block.index
138+
1
139+
>>> new_block.previous_hash == bc.chain[0].hash
140+
True
141+
>>> new_block.hash.startswith('00')
142+
True
143+
>>> bc.is_valid()
144+
True
145+
>>> bc.chain[1].previous_hash = "tampered"
146+
>>> bc.is_valid()
147+
False
148+
149+
"""
150+
for i in range(1, len(self.chain)):
151+
current = self.chain[i]
152+
prev = self.chain[i - 1]
153+
if current.previous_hash != prev.hash:
154+
return False
155+
if not current.hash.startswith('0' * self.difficulty):
156+
return False
157+
if current.hash != current.compute_hash(current.nonce):
158+
return False
159+
return True

0 commit comments

Comments
 (0)