Educational project demonstrating a minimal blockchain with Proof of Work and an HTTP API using Flask. The project supports running multiple nodes (e.g., ports 5000, 5001, 5002) from a shared app factory without duplicating code.
- Simple blockchain in Python (
src/blockchain.py). - Reusable Flask app factory (
src/app_factory.py) to avoid code duplication. - CLI runner (
src/serve_chain.py) to start a node on any port. - REST endpoints:
GET /mine_blockGET /get_chainGET /is_validPOST /add_transactionPOST /connect_nodeGET /replace_chain
- Basic Proof of Work: valid hashes start with
0000.
- Python 3.x
- Flask (HTTP API)
- hashlib (SHA-256)
- json
- requests (for node-to-node communication)
- urllib.parse (for URL parsing)
- Python 3.10+
- pip
- Optional: Python virtual environment (venv)
Recommended requirements.txt
Create and activate a virtual environment (optional), then install dependencies.
- Create venv (PowerShell):
python -m venv venv- Activate venv (PowerShell):
.\venv\Scripts\activatepip install -r requirements.txt
Start three nodes in separate terminals using different ports:
- Port 5000:
python src/serve_chain.py --port 5000- Port 5001:
python src/serve_chain.py --port 5001- Port 5002:
python src/serve_chain.py --port 5002Connect node 5000 to 5001 and 5002:
curl -X POST http://localhost:5000/connect_node -H "Content-Type: application/json" -d "{"nodes": ["127.0.0.1:5001", "127.0.0.1:5002"]}"curl -X GET http://localhost:5000/replace_chain- Method:
GET - URL:
/mine_block - Response
200 OK:
{
"message": "Block mined!",
"index": 2,
"timestamp": "2025-01-01 12:34:56.789012",
"proof": 533,
"previous_hash": "0000ab12...",
"transactions": [
{
"sender": "node_address",
"receiver": "Lopes",
"amount": 10
}
]
}- Method:
GET - URL:
/get_chain - Response
200 OK:
{
"chain": [
{
"index": 1,
"timestamp": "2025-01-01 12:00:00.000000",
"proof": 1,
"previous_hash": "0",
"transactions": []
}
],
"length": 1
}- Method:
GET - URL:
/is_valid - Response
200 OK:
{ "message": "All good. The Blockchain is valid." }- Method:
POST - URL:
/add_transaction - Body (JSON):
{ "sender": "node_1", "receiver": "node_2", "amount": 10 }- Response
201 Created:
{ "message": "This transaction will be added to Block 2" }- Method:
POST - URL:
/connect_node - Body (JSON):
{ "nodes": ["127.0.0.1:5001", "127.0.0.1:5002"] }- Response
201 Created:
{
"message": "All nodes are now connected. The chain now contains the following nodes:",
"total_nodes": ["127.0.0.1:5001", "127.0.0.1:5002"]
}- Method:
GET - URL:
/replace_chain - Response
200 OK:
{
"message": "The nodes had different chains so the chain was replaced by the longest one.",
"new_chain": [
/* ... */
]
}templates/transactions.json
{
"sender": "node_1",
"receiver": "node_2",
"amount": 10
}templates/nodes.json
{
"nodes": ["127.0.0.1:5001", "127.0.0.1:5002"]
}create_block(proof, previous_hash): Builds and appends a block with index, timestamp, proof, previous hash, and pending transactions; clears pending transactions.proof_of_work(previous_proof): Findsnew_proofsuch thatSHA256(str(new_proof**2 - previous_proof**2))starts with0000.hash(block): Computes SHA-256 over a JSON-serialized block (sort_keys=True).is_chain_valid(chain): Checks chain linkage and PoW validity.add_transaction(sender, receiver, amount): Queues a transaction for the next mined block.add_node(address): Adds a node (expectshost:port).replace_chain(): Fetches chains from connected nodes and replaces the local chain with the longest valid one.
This project is for learning purposes only and is not production-ready. It lacks security features, P2P networking, persistence, and robust distributed consensus.