-
-
Notifications
You must be signed in to change notification settings - Fork 48.5k
Add simple blockchain mining algorithm with PoW #13114
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
aeaf25e
c7f0210
9696d22
e5f21e6
6514a33
c44d753
00f590b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
Check failure on line 14 in blockchain/simple_blockchain.py
|
||
|
||
|
||
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: | ||
LetitiaGilbert marked this conversation as resolved.
Show resolved
Hide resolved
LetitiaGilbert marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
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): | ||
LetitiaGilbert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
LetitiaGilbert marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
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 |
Uh oh!
There was an error while loading. Please reload this page.