A post-quantum secure, Event-Driven HotStuff BFT consensus engine written in Rust.
Built as the successor to libhotstuff (C++), fulfilling every item on the original author's TODO list — plus hardening the protocol against quantum-era attacks.
| libhotstuff (original) | realhotstuff | |
|---|---|---|
| Language | C++14 with template metaprogramming | Rust 2021 — memory-safe, no undefined behaviour |
| Signatures | secp256k1 ECDSA (broken by quantum computers) | Dilithium2 — NIST PQC standard, ~128-bit post-quantum security |
| Key exchange | None (plaintext TCP) | Kyber512 KEM available for encrypted replica channels |
| Hashing | SHA-256 | BLAKE3 — 2-4x faster, SIMD-accelerated |
| Async model | libuv callbacks (can overflow the stack on catch-up) | Tokio channels — bounded, no recursive callbacks |
| PaceMaker | Round-robin only | Round-robin + PoW-based leader election for hybrid consensus |
| Memory | Entire chain in RAM, never freed | Branch pruning — auto-prunes after each commit, keeps only a sliding window |
| Crash recovery | None — full re-sync required | Persistent state — atomic JSON snapshots, restore on restart |
| Library API | Must fork and modify C++ code | StateMachine trait — implement one method, zero consensus knowledge needed |
| Handshake auth | Unauthenticated TCP | Dilithium2-signed peer handshakes |
| Block delivery | Fetch with timeout + retry | Fetch with parent-queue pipeline — proposals auto-unblock when parents arrive |
- 10,000+ blocks committed in ~12 seconds (wall clock, release build)
- Branch pruning caps memory at ~55 blocks regardless of chain length
- Three-step commit latency: block at height h finalized when height h+3 is certified
Classical ECDSA signatures will be broken by Shor's algorithm on a sufficiently large quantum computer. realhotstuff2 replaces every cryptographic signature in the protocol — proposals, votes, QCs, handshakes — with CRYSTALS-Dilithium2, one of the four algorithms standardised by NIST for post-quantum use. Kyber512 key encapsulation is available for establishing encrypted channels between replicas.
┌─────────────────────────────────────────────┐
│ Application │
│ implements StateMachine │
└──────────────────┬──────────────────────────┘
│ execute(commands, height)
┌──────────────────▼──────────────────────────┐
│ HotStuffCore │
│ propose · vote · QC · 3-step commit rule │
│ branch pruning · persistence snapshots │
└──────┬───────────────────────┬──────────────┘
│ ConsensusOutput │ ConsensusEvent
┌──────▼──────┐ ┌─────▼──────┐
│ PaceMaker │ │ Network │
│ RR / PoW │ │ TCP + auth │
└─────────────┘ └────────────┘
Key design principle: the consensus core is synchronous — it takes events in, emits outputs. All async I/O lives in the network and pacemaker layers. Safety is never coupled to liveness.
# Build
cargo build --release
# Generate a 4-replica testnet
./target/release/realhotstuff2 keygen --replicas 4 --output ./testnet
# Run each replica in a separate terminal
./target/release/realhotstuff2 run --id 0 --config ./testnet/config.json --key ./testnet/key_0.json
./target/release/realhotstuff2 run --id 1 --config ./testnet/config.json --key ./testnet/key_1.json
./target/release/realhotstuff2 run --id 2 --config ./testnet/config.json --key ./testnet/key_2.json
./target/release/realhotstuff2 run --id 3 --config ./testnet/config.json --key ./testnet/key_3.json
# Or run the demo script
./scripts/run_demo.sh./target/release/realhotstuff2 run --id 0 --config ./testnet/config.json --key ./testnet/key_0.json --pow --pow-difficulty 16# State is saved to ./data_<id>/state.json every 5 seconds
./target/release/realhotstuff2 run --id 0 --config ./testnet/config.json --key ./testnet/key_0.json --data-dir ./data_0
# Kill and restart — the replica resumes from its last snapshotImplement the StateMachine trait to build your own application on top of HotStuff consensus:
use realhotstuff2::traits::StateMachine;
use realhotstuff2::types::{Command, Height};
struct MyApp { /* your state */ }
impl StateMachine for MyApp {
fn execute(&mut self, commands: &[Command], height: Height) {
for cmd in commands {
// Deserialise and apply your application logic.
// Called once per committed block, in strict height order.
println!("height {height}: {}", String::from_utf8_lossy(&cmd.data));
}
}
}src/
├── main.rs CLI, node startup, event loop wiring
├── lib.rs Public module re-exports
├── types.rs Block, Vote, QC, Proposal, Config
├── crypto/
│ ├── dilithium.rs Dilithium2 sign/verify (PQC)
│ └── kyber.rs Kyber512 KEM (PQC)
├── consensus.rs HotStuffCore — the protocol state machine
├── pacemaker.rs Round-robin leader rotation
├── pacemaker_pow.rs PoW-based leader election
├── network.rs TCP P2P with authenticated handshakes
├── storage.rs In-memory BlockStore with branch pruning
├── persistence.rs Crash-recovery snapshots
└── traits.rs StateMachine + PaceMakerStrategy traits
- HotStuff: BFT Consensus in the Lens of Blockchain — Yin et al., PODC 2019
- CRYSTALS-Dilithium — NIST PQC digital signature standard
- CRYSTALS-Kyber — NIST PQC key encapsulation standard
- Original libhotstuff — C++ prototype by Ted Yin
Apache-2.0