15 unstable releases (3 breaking)
| new 0.22.0-beta.2 | Feb 16, 2026 |
|---|---|
| 0.21.5 | Jan 12, 2026 |
| 0.21.3 | Dec 3, 2025 |
| 0.21.2 | Nov 27, 2025 |
#2 in #proposer
125KB
2.5K
SLoC
Pathfinder Consensus
A Byzantine Fault Tolerant (BFT) consensus engine for Starknet nodes, built on top of the Malachite consensus engine.
Overview
Pathfinder Consensus provides a robust consensus engine for Starknet nodes that wraps the Malachite implementation of the Tendermint BFT consensus algorithm. It's designed to be generic over validator addresses and consensus values, making it suitable for Starknet's consensus requirements.
Quick Start
use pathfinder_consensus::*;
// Create and start consensus
let config = Config::new(my_address);
let mut consensus: DefaultConsensus<MyValue, MyAddress> = Consensus::new(config);
consensus.handle_command(ConsensusCommand::StartHeight(1, validator_set));
// Main loop
loop {
if let Some(event) = consensus.next_event().await {
match event {
ConsensusEvent::RequestProposal { height, round } => {
// Build and submit proposal
consensus.handle_command(ConsensusCommand::Propose(proposal));
}
ConsensusEvent::Decision { height, round, value } => {
// Commit the decided value
}
ConsensusEvent::Gossip(msg) => {
// Broadcast to peers
}
ConsensusEvent::Error(e) => {
// Handle error
}
}
}
}
API Overview
Commands (input via handle_command)
| Command | Description |
|---|---|
StartHeight(height, validator_set) |
Begin consensus at a new height |
Propose(proposal) |
Submit your proposal (when you're the proposer) |
Proposal(signed_proposal) |
Inject a proposal received from the network |
Vote(signed_vote) |
Inject a vote received from the network |
For detailed information about commands, refer to the API documentation.
Events (output via next_event)
| Event | Description |
|---|---|
RequestProposal { height, round } |
You're requested to build a proposal |
Decision { height, round, value } |
Consensus reached |
Gossip(NetworkMessage) |
Broadcast this message to peers |
Error(ConsensusError) |
Internal error occurred |
For detailed information about events, refer to the API documentation.
Configuration
let config = Config::new(my_address)
.with_wal_dir("/path/to/wal")
.with_history_depth(10)
.with_timeouts(TimeoutValues {
propose_timeout: Duration::from_secs(3),
prevote_timeout: Duration::from_secs(1),
precommit_timeout: Duration::from_secs(1),
commit_timeout: Duration::from_secs(1),
});
Custom Proposer Selection
Implement ProposerSelector<A> to override round-robin selection:
#[derive(Clone)]
struct WeightedSelector;
impl<A: ValidatorAddress> ProposerSelector<A> for WeightedSelector {
fn select_proposer<'a>(
&self,
validator_set: &'a ValidatorSet<A>,
height: u64,
round: u32,
) -> &'a Validator<A> {
// Custom selection logic
&validator_set.validators[round as usize % validator_set.count()]
}
}
let consensus = Consensus::with_proposer_selector(config, WeightedSelector);
Crash Recovery
Recover state from write-ahead log after restart:
let validator_sets = Arc::new(StaticValidatorSetProvider::new(validator_set));
let highest_committed = storage.get_highest_block()?; // Your storage layer
let consensus = Consensus::recover(config, validator_sets, highest_committed)?;
Type Requirements
Your address type must implement ValidatorAddress (auto-implemented for types with Sync + Send + Ord + Display + Debug + Default + Clone + Into<Vec<u8>> + Serialize + DeserializeOwned).
Your value type must implement ValuePayload (auto-implemented for types with Sync + Send + Ord + Display + Debug + Default + Clone + Serialize + DeserializeOwned).
Dependencies
~33MB
~497K SLoC