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
use crate::Node;
use serde::{Deserialize, Serialize};
use std::{
    collections::VecDeque,
    time::{Duration, SystemTime},
};

/// default quarantine duration is 30min
const DEFAULT_QUARANTINE_DURATION: Duration = Duration::from_secs(1800);

pub struct DefaultPolicy;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Strike {
    when: SystemTime,

    reason: StrikeReason,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum StrikeReason {
    CannotConnect,
    InvalidPublicId,
    InvalidData,
}

#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize, Deserialize)]
pub struct Record {
    strikes: VecDeque<Strike>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum PolicyReport {
    None,
    Quarantine,
    LiftQuarantine,
    Forget,
}

pub trait Policy {
    fn check(&mut self, node: &mut Node) -> PolicyReport;
}

impl Default for DefaultPolicy {
    fn default() -> Self {
        DefaultPolicy
    }
}

impl<P: Policy + ?Sized> Policy for Box<P> {
    fn check(&mut self, node: &mut Node) -> PolicyReport {
        self.as_mut().check(node)
    }
}

impl Policy for DefaultPolicy {
    fn check(&mut self, node: &mut Node) -> PolicyReport {
        // if the node is already quarantined
        if let Some(since) = node.logs().quarantined() {
            let duration = since.elapsed().unwrap();

            if duration < DEFAULT_QUARANTINE_DURATION {
                // the node still need to do some quarantine time
                PolicyReport::None
            } else if node.logs().last_update().elapsed().unwrap() < DEFAULT_QUARANTINE_DURATION {
                // the node has been quarantined long enough, check if it has been updated
                // while being quarantined (i.e. the node is still up and advertising itself
                // or others are still gossiping about it.)

                // the fact that this `Policy` does clean the records is a policy choice.
                // one could prefer to keep the record longers for future `check`.
                node.record_mut().clean_slate();
                PolicyReport::LiftQuarantine
            } else {
                // it appears the node was quarantine and is no longer active or gossiped
                // about, so we can forget it
                PolicyReport::Forget
            }
        } else if node.record().is_clear() {
            // if the record is clear, do nothing, leave the Node in the available nodes
            PolicyReport::None
        } else {
            // if the record is not `clear` then we quarantine the block for some time
            PolicyReport::Quarantine
        }
    }
}

impl Record {
    /// returns `true` if the record show that the `Node` is clear
    /// of any strike
    pub fn is_clear(&self) -> bool {
        self.strikes.is_empty()
    }

    pub fn clean_slate(&mut self) {
        self.strikes = VecDeque::default()
    }

    pub fn strike(&mut self, reason: StrikeReason) {
        self.strikes.push_back(Strike {
            when: SystemTime::now(),
            reason,
        })
    }
}