viewstamped-replication 0.6.0

A Rust-based implementation of the Viewstamped Replication consensus protocol.
Documentation
use crate::stamps::View;

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ReplicaIdentifier(GroupIdentifier, usize);

impl ReplicaIdentifier {
    pub fn group(&self) -> GroupIdentifier {
        self.0
    }

    pub fn primary(&self, view: View) -> Self {
        self.0.primary(view)
    }

    pub fn sub_majority(&self) -> usize {
        self.0.sub_majority()
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GroupIdentifier(u128, usize);

impl Default for GroupIdentifier {
    fn default() -> Self {
        Self::new(3)
    }
}

impl GroupIdentifier {
    pub fn new(replicas: usize) -> Self {
        Self(uuid::Uuid::now_v7().as_u128(), replicas)
    }

    pub fn size(&self) -> usize {
        self.1
    }

    pub fn primary(&self, view: View) -> ReplicaIdentifier {
        ReplicaIdentifier(*self, (view.as_u128() % (self.1 as u128)) as usize)
    }

    pub fn replicas(&self, view: View) -> impl Iterator<Item = ReplicaIdentifier> + Clone {
        let primary = self.primary(view);

        ReplicaIterator(*self, 0).filter(move |r| r != &primary)
    }

    pub fn sub_majority(&self) -> usize {
        (self.1 - 1) / 2
    }
}

#[derive(Clone)]
pub struct ReplicaIterator(GroupIdentifier, usize);

impl Iterator for ReplicaIterator {
    type Item = ReplicaIdentifier;

    fn next(&mut self) -> Option<Self::Item> {
        if self.1 >= self.0 .1 {
            None
        } else {
            self.1 += 1;
            Some(ReplicaIdentifier(self.0, self.1 - 1))
        }
    }
}

impl IntoIterator for GroupIdentifier {
    type Item = ReplicaIdentifier;
    type IntoIter = ReplicaIterator;

    fn into_iter(self) -> Self::IntoIter {
        ReplicaIterator(self, 0)
    }
}

#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct ClientIdentifier(u128);

impl Default for ClientIdentifier {
    fn default() -> Self {
        Self(uuid::Uuid::now_v7().as_u128())
    }
}

#[derive(Copy, Clone, Debug, Default, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct RequestIdentifier(u128);

impl RequestIdentifier {
    pub fn increment(&mut self) -> Self {
        self.0 += 1;
        *self
    }
}

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

    #[test]
    fn sub_majority() {
        assert_eq!(GroupIdentifier::new(3).sub_majority(), 1);
        assert_eq!(GroupIdentifier::new(4).sub_majority(), 1);
        assert_eq!(GroupIdentifier::new(5).sub_majority(), 2);
    }

    #[test]
    fn into_iter() {
        let group = GroupIdentifier::new(3);
        let replicas: Vec<ReplicaIdentifier> = group.into_iter().collect();

        assert_eq!(
            replicas,
            vec![
                ReplicaIdentifier(group, 0),
                ReplicaIdentifier(group, 1),
                ReplicaIdentifier(group, 2)
            ]
        );
    }

    #[test]
    fn primary() {
        let group = GroupIdentifier::new(3);
        let replicas: Vec<ReplicaIdentifier> = group.into_iter().collect();

        let mut view = View::default();

        for i in 0..10 {
            assert_eq!(group.primary(view), replicas[i % group.size()]);
            view.increment()
        }
    }

    #[test]
    fn replicas() {
        let group = GroupIdentifier::new(3);

        let mut view = View::default();

        for _ in 0..10 {
            let primary = group.primary(view);
            assert!(group.replicas(view).all(|r| r != primary));
            view.increment();
        }
    }
}