vrf-pbft 0.1.0

A Rust implementation of VRF-enhanced PBFT consensus protocol
Documentation
use std::collections::HashSet;

use crate::error::Result;
use crate::pbft::stage::validate_transition;
use crate::types::{Block, NodeId, Role, Stage};

#[derive(Debug)]
pub struct Round {
    pub num: u64,
    stage: Stage,
    role: Role,
    threshold: u64,
    prepare_voters: HashSet<NodeId>,
    commit_voters: HashSet<NodeId>,
    block: Option<Block>,
}

impl Round {
    pub fn new(num: u64, role: Role, threshold: u64) -> Self {
        let stage = match role {
            Role::Proposer => Stage::PrePrepare,
            _ => Stage::Idle,
        };
        Self {
            num,
            stage,
            role,
            threshold,
            prepare_voters: HashSet::new(),
            commit_voters: HashSet::new(),
            block: None,
        }
    }

    pub fn stage(&self) -> Stage {
        self.stage
    }

    pub fn role(&self) -> Role {
        self.role
    }

    pub fn block(&self) -> Option<&Block> {
        self.block.as_ref()
    }

    pub fn set_block(&mut self, block: Block) {
        self.block = Some(block);
    }

    /// Record a prepare vote. Returns false if this voter already voted.
    pub fn add_prepare_vote(&mut self, voter: NodeId) -> bool {
        self.prepare_voters.insert(voter)
    }

    /// Record a commit vote. Returns false if this voter already voted.
    pub fn add_commit_vote(&mut self, voter: NodeId) -> bool {
        self.commit_voters.insert(voter)
    }

    pub fn prepare_count(&self) -> u64 {
        self.prepare_voters.len() as u64
    }

    pub fn commit_count(&self) -> u64 {
        self.commit_voters.len() as u64
    }

    pub fn has_prepare_quorum(&self) -> bool {
        self.prepare_count() >= self.threshold
    }

    pub fn has_commit_quorum(&self) -> bool {
        self.commit_count() >= self.threshold
    }

    pub fn advance(&mut self, to: Stage) -> Result<()> {
        validate_transition(self.stage, to)?;
        self.stage = to;
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn vote_counting() {
        let mut round = Round::new(1, Role::Proposer, 3);
        assert!(!round.has_prepare_quorum());

        round.add_prepare_vote(1);
        round.add_prepare_vote(2);
        assert!(!round.has_prepare_quorum());

        round.add_prepare_vote(3);
        assert!(round.has_prepare_quorum());
    }

    #[test]
    fn stage_advances() {
        let mut round = Round::new(1, Role::Proposer, 3);
        assert_eq!(round.stage(), Stage::PrePrepare);

        assert!(round.advance(Stage::Prepare).is_ok());
        assert_eq!(round.stage(), Stage::Prepare);

        // Can't skip to Done
        assert!(round.advance(Stage::Done).is_err());
    }
}