1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! The paxakos consensus protocol has nodes elect leaders and vote on
//! proposals. Nodes will always vote 'no', if voting 'yes' would compromise
//! correctness. In all other cases, nodes will vote using their [`Voter`]
//! strategy. The default strategy, [`IndiscriminateVoter`] always votes 'yes'.
use std::convert::Infallible;

use crate::state::LogEntryOf;
use crate::state::NodeOf;
use crate::state::State;
use crate::CoordNum;
use crate::RoundNum;

/// Shorthand to extract `Abstain` type out of `V`.
pub type AbstainOf<V> = <V as Voter>::Abstain;
/// Shorthand to extract `CoordNum` type out of `V`.
pub type CoordNumOf<V> = <V as Voter>::CoordNum;
/// Shorthand to extract `Nay` type out of `V`.
pub type NayOf<V> = <V as Voter>::Nay;
/// Shorthand to extract `RoundNum` type out of `V`.
pub type RoundNumOf<V> = <V as Voter>::RoundNum;
/// Shorthand to extract `State` type out of `V`.
pub type StateOf<V> = <V as Voter>::State;
/// Shorthand to extract `Yea` type out of `V`.
pub type YeaOf<V> = <V as Voter>::Yea;

/// Strategy to vote on candidates and proposals.
pub trait Voter: Send + 'static {
    /// Type of shared state.
    type State: State;
    /// Round number type.
    type RoundNum: RoundNum;
    /// Coordination number type.
    type CoordNum: CoordNum;

    /// Data that will be added to any `Yea` vote on proposals.
    type Yea: std::fmt::Debug + Send + Sync;
    /// Data that will be added to any `Nay` vote on proposals.
    type Nay: std::fmt::Debug + Send + Sync;
    /// Data that will be added to any `Abstain` from elections.
    type Abstain: std::fmt::Debug + Send + Sync;

    /// Contemplate a bid for leadership.
    ///
    /// `candidate` will be `None` if the node cannot be inferred due to missing
    /// or outdated state.
    ///
    /// *Careful*: `state` is the current applied state and independent of
    /// `round_num`.
    fn contemplate_candidate(
        &mut self,
        round_num: Self::RoundNum,
        coord_num: Self::CoordNum,
        candidate: Option<&NodeOf<Self::State>>,
        state: Option<&Self::State>,
    ) -> Decision<(), Infallible, Self::Abstain>;

    /// Contemplate a proposed log entry.
    ///
    /// `leader` will be `None` if the node cannot be inferred due to missing
    /// or outdated state.
    ///
    /// *Careful*: `state` is the current applied state and independent of
    /// `round_num`.
    fn contemplate_proposal(
        &mut self,
        round_num: Self::RoundNum,
        coord_num: Self::CoordNum,
        log_entry: &LogEntryOf<Self::State>,
        leader: Option<&NodeOf<Self::State>>,
        state: Option<&Self::State>,
    ) -> Decision<Self::Yea, Self::Nay, Infallible>;

    /// Called for every commit.
    ///
    /// `leader` will be `None` if the node cannot be inferred due to missing
    /// or outdated state.
    #[allow(unused_variables)]
    fn observe_commit(
        &mut self,
        round_num: Self::RoundNum,
        coord_num: Self::CoordNum,
        log_entry: &LogEntryOf<Self::State>,
        leader: Option<&NodeOf<Self::State>>,
    ) {
    }
}

/// Voting decision, either `Yea`, `Nay` or `Abstain`.
pub enum Decision<Y, N, A> {
    /// Abstain, i.e. do not vote at all.
    Abstain(A),
    /// Nay, i.e. vote 'no'.
    Nay(N),
    /// Yea, i.e. vote 'yes'.
    Yea(Y),
}

/// A voter that always votes `Yea`.
#[derive(Default)]
pub struct IndiscriminateVoter<S, R, C, A, Y, N>(std::marker::PhantomData<(S, R, C, A, Y, N)>);

impl<S, R, C, A, Y, N> IndiscriminateVoter<S, R, C, A, Y, N> {
    /// Constructs a new `IndiscriminateVoter`.
    pub fn new() -> Self {
        Self(std::marker::PhantomData)
    }
}

impl<S, R, C, A, Y, N> Voter for IndiscriminateVoter<S, R, C, A, Y, N>
where
    S: State,
    R: RoundNum,
    C: CoordNum,
    A: std::fmt::Debug + Send + Sync + 'static,
    Y: std::fmt::Debug + Default + Send + Sync + 'static,
    N: std::fmt::Debug + Send + Sync + 'static,
{
    type State = S;
    type RoundNum = R;
    type CoordNum = C;

    type Yea = Y;
    type Nay = N;
    type Abstain = A;

    fn contemplate_candidate(
        &mut self,
        _round_num: Self::RoundNum,
        _coord_num: Self::CoordNum,
        _candidate: Option<&NodeOf<Self::State>>,
        _state: Option<&Self::State>,
    ) -> Decision<(), Infallible, Self::Abstain> {
        Decision::Yea(())
    }

    fn contemplate_proposal(
        &mut self,
        _round_num: Self::RoundNum,
        _coord_num: Self::CoordNum,
        _log_entry: &LogEntryOf<Self::State>,
        _leader: Option<&NodeOf<Self::State>>,
        _state: Option<&Self::State>,
    ) -> Decision<Self::Yea, Self::Nay, Infallible> {
        Decision::Yea(Default::default())
    }
}