Expand description
§Pathfinder Consensus
A Byzantine Fault Tolerant (BFT) consensus engine for Starknet nodes.
§Overview
This crate provides a 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.
§Core Concepts
§ValidatorAddress Trait
Your validator address type must implement the ValidatorAddress trait,
which requires:
Sync + Send: Thread-safe and sendable across threadsOrd + Display + Debug + Default + Clone: Standard Rust traits for ordering, display, debugging, default values, and cloningInto<Vec<u8>>: Convertible to bytes for serializationSerialize + DeserializeOwned: Serde serialization support
§ValuePayload Trait
Your consensus value type must implement the ValuePayload trait, which
requires:
Sync + Send: Thread-safe and sendable across threadsOrd + Display + Debug + Default + Clone: Standard Rust traitsSerialize + DeserializeOwned: Serde serialization support
§Consensus Engine
The main Consensus<V, A> struct is generic over:
V: Your consensus value type (must implementValuePayload)A: Your validator address type (must implementValidatorAddress)
§Usage Example
use pathfinder_consensus::*;
use serde::{Deserialize, Serialize};
// Define your validator address type
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct MyAddress(String);
impl std::fmt::Display for MyAddress {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<MyAddress> for Vec<u8> {
fn from(addr: MyAddress) -> Self {
addr.0.into_bytes()
}
}
// Define your consensus value type
#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
struct BlockData(String);
impl std::fmt::Display for BlockData {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
// Custom proposer selector that uses weighted selection
#[derive(Clone)]
struct WeightedProposerSelector;
impl ProposerSelector<MyAddress> for WeightedProposerSelector {
fn select_proposer<'a>(
&self,
validator_set: &'a ValidatorSet<MyAddress>,
_height: u64,
round: u32,
) -> &'a Validator<MyAddress> {
// Simple weighted selection based on voting power
let total_power: u64 = validator_set
.validators
.iter()
.map(|v| v.voting_power)
.sum();
let selection = (round as u64) % total_power;
let mut cumulative = 0;
for validator in &validator_set.validators {
cumulative += validator.voting_power;
if selection < cumulative {
return validator;
}
}
// Fallback to first validator
&validator_set.validators[0]
}
}
#[tokio::main]
async fn main() {
// Create configuration
let my_address = MyAddress("validator_1".to_string());
let config = Config::new(my_address.clone());
// Create consensus engine with custom proposer selector
let proposer_selector = WeightedProposerSelector;
let mut consensus = Consensus::new(config).with_proposer_selector(proposer_selector);
// Or use the default round-robin selector (no additional configuration needed)
// let mut consensus = Consensus::new(config);
// Start consensus at height 1
let validator_set = ValidatorSet::new(vec![Validator::new(
my_address.clone(),
PublicKey::from_bytes([0; 32]),
)
.with_voting_power(10)]);
consensus.handle_command(ConsensusCommand::StartHeight(1, validator_set));
// Poll for events
while let Some(event) = consensus.next_event().await {
match event {
ConsensusEvent::RequestProposal { height, round } => {
println!("Need to propose at height {}, round {}", height, round);
}
ConsensusEvent::Decision { height, value } => {
println!("Consensus reached at height {}: {:?}", height, value);
}
ConsensusEvent::Gossip(message) => {
println!("Need to gossip: {:?}", message);
}
ConsensusEvent::Error(error) => {
eprintln!("Consensus error: {}", error);
}
}
}
}§Commands and Events
The consensus engine operates on a command/event model:
- Commands: Send commands to the consensus engine via
handle_command() - Events: Poll for events from the consensus engine via
next_event().await
§Crash Recovery
The consensus engine supports crash recovery through write-ahead logging:
// Recover from a previous crash
let validator_sets = Arc::new(StaticValidatorSetProvider::new(validator_set));
let mut consensus = Consensus::recover(config, validator_sets);Structs§
- Config
- The configuration for the consensus engine.
- Consensus
- Pathfinder consensus engine
- Proposal
- A proposal for a block value in a consensus round.
- Round
- A round number (or
Noneif the round is nil). - Round
Robin Proposer Selector - A default proposer selector that uses round-robin selection.
- Signed
Proposal - A fully validated, signed proposal ready to enter consensus.
- Signed
Vote - A signed vote.
- Static
Validator SetProvider - A validator set provider that always returns the same validator set.
- Timeout
Values - The timeout values for the consensus engine.
- Validator
- A validator in the consensus protocol.
- Validator
Set - A validator set represents a group of consensus participants.
- Vote
- A vote for a value in a consensus round.
Enums§
- Consensus
Command - Commands that the application can send into the consensus engine.
- Consensus
Event - Events that the consensus engine emits for the application to handle.
- Network
Message - A message to be gossiped to peers.
- Vote
Type - The type of vote.
Traits§
- Proposer
Selector - A trait for selecting the proposer for a given height and round.
- Validator
Address - A trait for consensus validator addresses.
- Validator
SetProvider - A trait for retrieving the validator set at a specific blockchain height.
- Value
Payload - A trait for consensus value payloads.
Type Aliases§
- Default
Consensus - A type alias for consensus with the default round-robin proposer selector.
- Public
Key - A public key for the consensus protocol.
- Signature
- A cryptographic signature for consensus messages.
- Signing
Key - An Ed25519 signing key.
- Voting
Power - A validator’s voting power.