βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β βββββββ βββ βββββββ ββββββββββ βββ β
β βββββββββββ ββββββββββββββββββββ ββββ β
β βββββββββββ βββ ββββββ βββββββ β
β βββββββββββ βββ ββββββ βββββββ β
β ββββββββββββββββββββββββββββββββββββ βββ β
β βββββββ ββββββββ βββββββ ββββββββββ βββ β
β β
β ββββββββββ βββ ββββββ βββββββ βββ β
β βββββββββββ βββββββββββββββββββ βββ β
β βββ βββββββββββββββββββββββββ βββ β
β βββ βββββββββββββββββββββββββββββ β
β βββββββββββ ββββββ βββββββββ ββββββ β
β ββββββββββ ββββββ βββββββββ βββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
A production-ready FastAPI blockchain with PoW, Consensus, and Multi-Node Docker
A lightweight, educational blockchain framework built with FastAPI, featuring:
- βοΈ Proof-of-Work mining with adjustable difficulty
- π SHA-256 cryptographic block hashing
- π Multi-node peer-to-peer consensus
- π‘ RESTful + OpenAPI with auto-generated docs
- π¨ Real-time HTMX/Tailwind UI dashboard
- π³ Multi-node Docker orchestration
- β Full Pydantic validation and type safety
Perfect for learning how blockchains work under the hood or as a foundation for custom distributed ledger experiments.
ββββββββββββββββββββββββββββββββββββββββββββ
β FastAPI Application β
β (Uvicorn ASGI Server - Port 5000) β
ββββββββββββββββ¬ββββββββββββββββββββββββββββ
β
ββββββββββββββββββΌβββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββ βββββββββββββββββββ
β UI Routes β β API Routesβ β Legacy Routes β
β (HTMX/Jinja2) β β (/api/*) β β (/mine, /chain) β
ββββββββββ¬ββββββββββ βββββββ¬ββββββ ββββββββββ¬βββββββββ
β β β
βββββββββββββββββββΌββββββββββββββββββ
β
ββββββββββββΌβββββββββββ
β Blockchain Core β
β (In-Memory State) β
ββββββββββββ¬βββββββββββ
β
ββββββββββββββββββββββΌβββββββββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββ
β Block Chain β β Mempool β β Peer Nodes β
β [Blockβ, Bβ...] β β [Pending Txs] β β {node1, node2} β
βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββ
β β β
β β β
βΌ βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββ
β PoW Mining β β Tx Validation β β Consensus β
β (SHA-256 hash) β β (Pydantic) β β (Longest chain)β
βββββββββββββββββββ βββββββββββββββββββ ββββββββββββββββββ
Internet/Host Machine (localhost)
β
ββββββββββββββββΌβββββββββββββββ
β β β
Port 5000 Port 5001 Port 5002
β β β
βΌ βΌ βΌ
βββββββββββ βββββββββββ βββββββββββ
β Node 1 βββββΊβ Node 2 βββββΊβ Node 3 β
β (node1) β β (node2) β β (node3) β
ββββββ¬βββββ ββββββ¬βββββ ββββββ¬βββββ
β β β
ββββββββββββββββΌβββββββββββββββ
β
Docker Bridge Network
(blockchain-net)
β
Auto-registration via PEERS env
py-blockchain-tutorial/
β
βββ π app/ # Core application package
β βββ __init__.py # Flask legacy factory (deprecated)
β βββ api.py # π FastAPI app + all endpoints
β βββ blockchain.py # βοΈ Blockchain logic (PoW, consensus)
β βββ models.py # π Pydantic schemas for validation
β βββ main.py # π¬ Uvicorn entrypoint
β βββ routes.py # (deprecated, migrated to api.py)
β
βββ π templates/ # Jinja2 HTML templates
β βββ index.html # π¨ Main dashboard (HTMX + Tailwind)
β βββ _status.html # π Live status partial
β
βββ π tests/ # Pytest test suite
β βββ test_app.py # β
API & integration tests
β βββ requirements.txt # Test dependencies
β
βββ π .github/workflows/ # CI/CD pipelines
β βββ ci.yml # GitHub Actions: lintβtestβaudit
β
βββ π³ Dockerfile # Production container image
βββ π³ docker-compose.yml # Multi-node orchestration
βββ βοΈ pyproject.toml # Project metadata + tool configs
βββ π§ .pre-commit-config.yaml # Git hooks for quality checks
βββ π requirements.txt # Legacy root deps (use pyproject.toml)
βββ π README.md # This file
# Install dependencies
pip install -e .[dev]
# Run the server
python -m app.main
# Open in browser
# UI: http://127.0.0.1:5000
# API docs: http://127.0.0.1:5000/docs
# ReDoc: http://127.0.0.1:5000/redoc# Spin up 3 interconnected nodes
docker compose up --build -d
# Access nodes
# Node 1: http://127.0.0.1:5000
# Node 2: http://127.0.0.1:5001
# Node 3: http://127.0.0.1:5002
# Example: Mine a block on node 1
curl http://localhost:5000/mine
# View the chain
curl http://localhost:5000/chain | jq
# Trigger consensus on node 2 (sync from peers)
curl http://localhost:5001/nodes/resolve | jq
# Stop nodes
docker compose downThe live dashboard (powered by HTMX + Tailwind) provides real-time updates, interactive buttons, and toast notifications:
Features:
- π Live status cards (height, difficulty, mempool, last block hash)
- β Add Tx button (green) - adds test transactions to mempool
- βοΈ Mine Block button (blue) - mines pending transactions
- π¦ Recent blocks viewer with auto-refresh
- π Mempool viewer showing pending transactions
- π Toast notifications for success/error feedback
- π Auto-refresh every 2-5 seconds
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/status |
Chain status + metrics |
| GET | /api/blocks |
Recent blocks (paginated) |
| GET | /api/mempool |
Pending transactions |
| POST | /api/tx |
Submit a transaction |
| POST | /api/mine |
Mine pending txs into a block |
| Method | Endpoint | Description |
|---|---|---|
| GET | /mine |
Mine block with reward tx |
| POST | /transactions/new |
Add transaction (validated) |
| GET | /chain |
Full chain + length |
| POST | /nodes/register |
Register peer nodes |
| GET | /nodes/resolve |
Consensus (longest chain wins) |
- Swagger UI: http://127.0.0.1:5000/docs
- ReDoc: http://127.0.0.1:5000/redoc
# 1. Add transactions to mempool
curl -X POST http://127.0.0.1:5000/api/tx \
-H "Content-Type: application/json" \
-d '{"from": "alice", "to": "bob", "amount": 10}'
curl -X POST http://127.0.0.1:5000/api/tx \
-H "Content-Type: application/json" \
-d '{"from": "charlie", "to": "dave", "amount": 5}'
# 2. Check mempool
curl http://127.0.0.1:5000/api/mempool | jq
# 3. Mine a block
curl -X POST http://127.0.0.1:5000/api/mine | jq
# 4. Verify chain
curl http://127.0.0.1:5000/chain | jq '.chain[-1]'# Node 1: Mine several blocks
for i in {1..3}; do
curl -X POST http://127.0.0.1:5000/api/tx \
-H "Content-Type: application/json" \
-d "{\"from\":\"miner\",\"to\":\"user$i\",\"amount\":1}"
curl http://127.0.0.1:5000/mine
done
# Node 2: Check initial state
curl http://127.0.0.1:5001/chain | jq '.length'
# Output: 1 (genesis only)
# Node 2: Resolve conflicts (sync from node 1)
curl http://127.0.0.1:5001/nodes/resolve | jq
# Node 2: Verify sync
curl http://127.0.0.1:5001/chain | jq '.length'
# Output: 4 (genesis + 3 mined blocks)# Run all tests
pytest
# With coverage
pytest --cov=app
# Type check
mypy app
# Lint & format
ruff check --fix .
ruff format .
black .
isort .
# Security audit
pip-auditTest Coverage:
- β Status endpoint
- β Transaction submission + mining flow
- β
Legacy
/mineendpoint - β Transaction validation (Pydantic)
- β Chain retrieval
- β Node registration
- β Consensus resolution
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Layer β Technology β
βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββ€
β Web Framework β FastAPI 0.121+ (ASGI) β
β Server β Uvicorn (asyncio event loop) β
β Validation β Pydantic 2.12+ β
β Templates β Jinja2 3.1+ β
β Frontend β HTMX 1.9 + Tailwind CSS (CDN) β
β Testing β Pytest 9.0+, Hypothesis 6.147+ β
β Type Checking β Mypy 1.18+ (strict mode) β
β Linting β Ruff 0.14+, Black, Isort β
β Security β Bandit, pip-audit β
β Containerizationβ Docker + Docker Compose β
β CI/CD β GitHub Actions β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- π SHA-256 hashing for block integrity
- β Pydantic validation for all inputs
- π‘οΈ Request size limits (FastAPI defaults)
- π Dependency auditing via pip-audit
- π No secrets in code (env var best practices)
- π Type safety with mypy strict mode
Block {
index: int # Sequential block number
timestamp: float # Unix timestamp
previous_hash: str # SHA-256 of previous block
nonce: int # Proof-of-Work solution
transactions: [...] # List of transactions
hash: str # SHA-256 of this block
}βββββββββββββββ βββββββββββββββ βββββββββββββββ
β Block 0 β β Block 1 β β Block 2 β
β (Genesis) ββββββ ββββββ β
βββββββββββββββ€ βββββββββββββββ€ βββββββββββββββ€
β idx: 0 β β idx: 1 β β idx: 2 β
β prev: 000...β β prev: abc...β β prev: def...β
β nonce: 0 β β nonce: 1234 β β nonce: 5678 β
β txs: [gen] β β txs: [tx1] β β txs: [tx2] β
β hash: abc...β β hash: def...β β hash: ghi...β
βββββββββββββββ βββββββββββββββ βββββββββββββββ
| Variable | Description | Default |
|---|---|---|
PORT |
Server port | 5000 |
NODE_ID |
Unique identifier for this node | Random UUID |
PEERS |
Comma-separated peer addresses | (empty) |
Nodes auto-register peers via PEERS env:
node1:
environment:
- NODE_ID=node1
- PEERS=blockchain-node2:8000,blockchain-node3:8000- β Run locally and explore the UI
- β
Submit transactions via
/api/tx - β
Mine blocks with
/api/mine - β
Inspect chain structure at
/chain
- β
Study
blockchain.pyPoW algorithm - β Add custom transaction fields
- β Modify mining difficulty
- β Run multi-node Docker setup
- β Implement Merkle tree for transactions
- β Add ECDSA signatures (secp256k1)
- β Persistent storage (SQLite/LevelDB)
- β WebSocket live updates
- β UTXO model with double-spend prevention
Contributions welcome! Please:
- Fork the repo
- Create a feature branch
- Run tests + linting:
pytest && mypy app && ruff check . - Submit a PR
MIT License - feel free to use for learning or commercial projects.
- Original tutorial inspired by classic blockchain educational resources
- Built with modern Python best practices (2025)
- Community feedback and contributions
β Star this repo if you found it helpful!
Made with β€οΈ for the blockchain learning community
