A lightweight, modular, and production-grade Bitcoin Simplified Payment Verification (SPV) header validation engine written in Rust. This library implements the core consensus rules for validating Bitcoin block headers, tracking chain state, and resolving forks based on cumulative proof-of-work.
The system is designed as a standalone validation engine, decoupled from networking and persistent storage logic.
+---------------------+ +----------------------+
| BlockHeader | | HeaderChain |
|---------------------| |----------------------|
| - version | <---- | - headers (HashMap) |
| - prev_block_hash | | - best_tip |
| - merkle_root | | |
| - timestamp | +----------+-----------+
| - bits (difficulty) | |
| - nonce | v
+----------+----------+ +----------------------+
| | Consensus Validation |
+----------------> | - Proof-of-Work |
| - Difficulty Target |
| - Chain Linking |
+----------------------+
Bitcoin uses the "Heaviest Chain" rule, not the "Longest Chain" rule. While often synonymous, a shorter chain with significantly higher difficulty can override a longer chain with lower difficulty.
This implementation calculates work for each block as:
Work = 2^256 / (Target + 1)
The HeaderChain tracks the cumulative work of every valid header and atomically updates the chain tip only when a new candidate chain exceeds the total work of the current best chain.
Bitcoin encodes the 256-bit target threshold into a 32-bit integer ("bits").
- Exponent: Top 8 bits (
bits >> 24). - Mantissa: Bottom 23 bits (
bits & 0x7fffff).Target = Mantissa * 256^(Exponent - 3)
# Build the project
cargo build --release
# Run unit tests
cargo testuse mini_spv_node::{BlockHeader, crypto::double_sha256};
use hex;
fn main() {
// Bitcoin Testnet Genesis Header
let raw_header = "0100000000000000000000000000000000000000000000000000000000000000000000003ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4adae5494dffff001d1aa4ae18";
let bytes = hex::decode(raw_header).expect("Invalid hex");
match BlockHeader::from_bytes(&bytes) {
Ok(header) => {
if header.validate_pow() {
println!("Valid Block: {}", hex::encode(header.hash()));
} else {
eprintln!("Invalid Proof-of-Work");
}
}
Err(e) => eprintln!("Parse Error: {}", e),
}
}- Rust: Chosen for memory safety guarantees without a garbage collector, essential for low-level consensus code where performance and correctness are critical.
num-bigint: Used for arbitrary-precision arithmetic required for work calculations (2^256), avoiding overflow issues present in standard primitives.- Strict Serialization: The engine enforces the legacy 80-byte header format. Any deviation constitutes a consensus failure.
The test suite relies on known vectors from the Bitcoin Testnet blockchain to ensure compliance with the actual network rules.
- Genesis Validation: Verifies strict binary parsing and hashing against the genesis block.
- Fork Resolution: Simulates competitive fork scenarios to verify the Cumulative Work implementation correctly prioritizes "heavier" chains over "longer" ones.
- Orphan Detection: Ensures headers without a known parent are rejected immediately.
SPV nodes verify block headers but do not validate transactions. They operate under the following assumptions:
- Honest Majority: The chain with the most cumulative work is generated by an honest majority of miners.
- Cost of Attack: Forging a block header requires expending real-world energy (Proof-of-Work), making it computationally expensive to spoof the chain history.
- Data Availability: An SPV node assumes that if a block header is valid, the underlying transaction data exists and is available from full nodes.
- P2P Networking: Implement the Bitcoin wire protocol to download headers from peers.
- Merkle Proof Verification: Add validation for transaction inclusion proofs (SPV proofs).
- Persistent Storage: Integrate a database (e.g., SQLite or LevelDB) for storing the header chain on disk.