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);
}
pub fn add_prepare_vote(&mut self, voter: NodeId) -> bool {
self.prepare_voters.insert(voter)
}
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);
assert!(round.advance(Stage::Done).is_err());
}
}