VRF-PBFT
A Rust implementation of VRF-enhanced PBFT consensus protocol
Installation
# Clone
# Build
# Run tests
# Run examples
Architecture
Consensus Flow
┌──────────────────────────────────────────────────────────┐
│ VRF Role Assignment │
│ Each node runs VRF sortition to determine its role │
│ Proposer(~1) │ Validator(~3f+1) │ Packer(~f+1) │ Normal │
└──────────────────────────┬───────────────────────────────┘
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│PrePrepare│─▶│ Prepare │─▶│ Commit │─▶│ Reply │
│ │ │ │ │ │ │ │
│ Proposer │ │Validators│ │Validators│ │ Block │
│ creates │ │ vote on │ │ confirm │ │finalized │
│ block │ │ block │ │ votes │ │ │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
needs needs
2/3+1 2/3+1
votes votes
Sortition Algorithm
VRF Proof Binomial CDF (log-space)
┌─────────┐ ┌─────────────────────────────────────┐
│ sk + α │────▶│ hash → [0,1) uniform random value │
└─────────┘ │ p = expected / total_weight │
│ Walk CDF: P(X ≤ k) for k = 0..w │
│Return first k where val < cumulative│
└──────────────────┬──────────────────┘
│
k > 0 ──▶ Selected
k = 0 ──▶ Not selected
How It Works
VRF (Verifiable Random Function)
Each node uses ECVRF (SECP256K1) to privately determine its role for each round. The selection is random but cryptographically verifiable. Higher-weight nodes are more likely to be selected, implementing proof-of-stake.
PBFT (Practical Byzantine Fault Tolerance)
The protocol tolerates up to f Byzantine (malicious) nodes out of 3f+1 total. A quorum of 2f+1 votes is required at each voting phase. Byzantine nodes can vote against valid blocks, but they cannot prevent consensus as long as enough honest validators are elected.
Per-Round Lifecycle
- VRF Sortition -- each node independently determines its role
- PrePrepare -- the elected proposer creates a block linked to the previous hash
- Prepare -- validators check
prev_hashagainst their ledger and vote - Commit -- validators confirm their votes
- Reply -- the finalized block is appended to all nodes' ledgers
Rounds where no proposer is elected or quorum is not reached are skipped.
Features
Implemented
Consensus Protocol
- PBFT 4-phase consensus (PrePrepare, Prepare, Commit, Reply)
- Stage transition validation
- Adaptive quorum threshold (2/3+1 of committee, capped at 2f+1)
VRF Sortition
- ECVRF with SECP256K1_SHA256_TAI cipher suite
- Binomial CDF in log-space for numerical stability
- Per-role expected values (separate probabilities for Proposer, Validator, Packer)
- Weight-based selection (proof-of-stake)
Byzantine Fault Tolerance
- Configurable fault tolerance parameter
f - Duplicate vote detection via voter identity tracking
- Byzantine nodes modeled as always-reject voters
- System tolerates up to
ffaults out of3f+1nodes
Block Chain Integrity
- SHA-256 block hashing
prev_hashchain linking validated by all honest nodes- Deterministic serialization via
bincode
Examples
Single Round
Runs a minimal 4-node consensus (f=1) until one round succeeds:
Running VRF-PBFT with 4 nodes (f=1)...
Consensus reached on round 4!
Proposer: node 2
Prev hash: f5a5fd42d16a2030...
Hash: fcf00efdad604ede...
Messages exchanged: 4
Simulation
Runs 100 rounds with 10 weighted nodes (f=3), showing proposer distribution:
Results:
Rounds attempted: 100
Blocks committed: 60
Success rate: 60.0%
Proposer distribution:
Node 1 (weight 500): 31 blocks
Node 2 (weight 300): 15 blocks
Node 3 (weight 200): 2 blocks
...
Ledger consistency: PASS
Byzantine
Demonstrates fault tolerance with 2 Byzantine nodes out of 7 (f=2):
Byzantine fault tolerance demo
Nodes: 7 (5 honest, 2 byzantine)
f=2: system tolerates up to 2 faults
Threshold: 2f+1 = 5
After 100 round attempts:
Blocks committed: 27
Ledger consistency: PASS
Project Structure
vrf-pbft/
├── Cargo.toml
├── README.md
├── LICENSE
├── assets/
│ └── vrf_pbft_logo.png
├── src/
│ ├── lib.rs # Crate root and re-exports
│ ├── types.rs # Hash, Block, Role, Stage types
│ ├── error.rs # Error handling with thiserror
│ ├── vrf.rs # VRF client + binomial sortition
│ ├── node.rs # Consensus node with VRF role assignment
│ ├── engine.rs # Multi-node consensus orchestrator
│ └── pbft/
│ ├── mod.rs # PBFT module re-exports
│ ├── message.rs # Protocol messages (PrePrepare, Prepare, Commit, Reply)
│ ├── stage.rs # Stage transition validation
│ └── round.rs # Round state with vote tracking
├── tests/
│ ├── integration.rs # End-to-end consensus tests
│ ├── pbft_test.rs # PBFT message and round tests
│ └── vrf_test.rs # VRF key generation, proof, and sortition tests
└── examples/
├── single_round.rs # Minimal single-round demo
├── simulation.rs # Multi-round weighted simulation
└── byzantine.rs # Byzantine fault tolerance demo
Requirements
- Rust toolchain (1.70+)
- OpenSSL (for ECVRF)
Dependencies
| Crate | Purpose |
|---|---|
vrf |
ECVRF implementation (SECP256K1) |
sha2 |
SHA-256 block hashing |
serde + bincode |
Deterministic serialization |
rand + rand_chacha |
Deterministic key generation |
thiserror |
Error type derivation |
libm |
Log-gamma for binomial CDF |
Protocol Parameters
| Parameter | Formula | Description |
|---|---|---|
| Total nodes | 3f + 1 |
Minimum nodes for fault tolerance |
| Quorum | 2f + 1 |
Votes needed per phase |
| Expected proposers | 1 |
Average proposers per round |
| Expected validators | 3f + 1 |
Average validators per round |
| Expected packers | f + 1 |
Average packers per round |
License
MIT License - Copyright (c) 2026 Adel-Ayoub
See LICENSE for details.