A minimal blockchain implementation in Rust, built from scratch to explore core cryptographic and data structure concepts behind Bitcoin and Ethereum.
- Proof of Work — SHA-256 based mining with configurable difficulty (leading zeros)
- Transaction mempool — pending transactions are batched and included in the next mined block
- Chain validation — full integrity check (hash consistency + previous-hash linkage)
- Balance lookup — account-model balance computed by scanning the full chain
- JSON persistence — the chain is serialized to disk and reloaded on startup
- CLI interface — interact with the chain entirely from the command line
src/
├── main.rs # CLI entry point
├── block.rs # Block struct and getters/setters
├── blockchain.rs # Chain logic: mining, validation, balance
├── transaction.rs # Transaction and Mempool
├── proof_of_work.rs # PoW loop and Difficulty struct
├── utils.rs # SHA-256 hashing, hex helpers, JSON I/O
└── error.rs # Custom error types (thiserror)
| Crate | Purpose |
|---|---|
sha2 |
SHA-256 hashing |
hex |
Bytes ↔ hex string conversion |
serde / serde_json |
Serialization / deserialization |
chrono |
UTC timestamps on blocks and transactions |
thiserror |
Ergonomic custom error types |
git clone <repo>
cd rusty_chain
mkdir -p cache
cargo build --releaseAll commands load the chain from ./cache/blockchain.json on startup and save it back on exit.
Creates the genesis block (difficulty 4).
cargo run -- initAddresses are 32-byte values encoded as 64-character hex strings.
cargo run -- add-transaction <from_hex> <to_hex> <amount>Example:
cargo run -- add-transaction \
0000000000000000000000000000000000000000000000000000000000000001 \
0000000000000000000000000000000000000000000000000000000000000002 \
1000Takes up to 100 pending transactions from the mempool, runs proof of work, and appends the block.
cargo run -- minecargo run -- balance <address_hex>cargo run -- chaincargo run -- validateEach block is hashed with SHA-256 over: index || nonce || tx_count || (from || to || amount || date)* || timestamp || previous_hash.
The mining loop increments a u128 nonce until the hex-encoded hash starts with difficulty_level zeros (default: 4). Higher difficulty increases mining time exponentially.
Transactions are queued in a Mempool (max 1000 entries). Duplicates are rejected. When a block is mined, the included transactions are removed from the mempool.
is_valid() checks every block:
- Recomputes the hash and compares it to the stored hash.
- Verifies
previous_hashmatches the preceding block's hash. - Verifies the genesis block's
previous_hashis all zeros.
cargo testThe test in utils.rs verifies hash determinism and hex round-trip correctness.
- No cryptographic signatures — addresses are raw 32-byte values with no key-pair enforcement.
- Single-threaded mining — no parallelism or nonce sharding.
- Simple balance model — scans the full chain on every lookup (no UTXO set or cached state).
- No networking — single-node only.