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
147
// Copyright 2019 Stichting Organism
// Copyright (c) 2018-2019 Prime Type Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! A Known Peer in the p3p system

use async_std::net::SocketAddr;
use std::{collections::BTreeSet, time::SystemTime};
use serde::{Deserialize, Serialize};
use crate::topic::{InterestLevel, Proximity, Subscription, Subscriptions, Topic};
use crate::id::NetworkID;


/// The data associated to a Node.
///
/// This can be gossiped through the topology in order to update
/// the topology of new nodes or _better_ neighbors.
///
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct KnownPeer {
    /// a unique identifier associated to the node
    pub(crate) id: NetworkID,

    /// the address to contact the node
    pub(crate) address: Option<SocketAddr>,

    /// all the subscription this node is interested about
    /// (with associated priority of interest)
    pub(crate) subscriptions: Subscriptions,

    /// the `Id` of the other `Node` this `Node` is aware of
    pub(crate) subscribers: BTreeSet<NetworkID>,

    /// this value denotes when this node exchange gossips
    /// with us for the last time.
    pub(crate) last_gossip: SystemTime,
}

impl KnownPeer {

    /// create a new unreachable Node with the given [`Id`].
    fn new(id: NetworkID) -> Self {
        KnownPeer {
            id,
            address: None,
            subscriptions: Subscriptions::default(),
            subscribers: BTreeSet::new(),
            last_gossip: SystemTime::now(),
        }
    }
    
    /// create a new Node with the given [`SocketAddr`].
    pub fn new_with(id: NetworkID, address: SocketAddr) -> Self {
        KnownPeer {
            id,
            address: Some(address),
            subscriptions: Subscriptions::default(),
            subscribers: BTreeSet::new(),
            last_gossip: SystemTime::now(),
        }
    }

    /// access the unique identifier of the `Node`.
    pub fn id(&self) -> &NetworkID {
        &self.id
    }

    /// get the Node's address (mean to contact it)
    pub fn address(&self) -> &Option<SocketAddr> {
        &self.address
    }

    /// these are the [`Topic`] and the [`InterestLevel`] associated.
    ///
    pub fn subscriptions(&self) -> impl Iterator<Item = (&Topic, &InterestLevel)> {
        self.subscriptions.iter()
    }

    /// the nodes that are related to this Node
    pub fn subscribers(&self) -> impl Iterator<Item = &NetworkID> {
        self.subscribers.iter()
    }

    /// add a subscription
    pub fn add_subscription(&mut self, subscription: Subscription) -> Option<InterestLevel> {
        self.subscriptions.add(subscription)
    }

    /// remove a subscriptions
    pub fn remove_subscription(&mut self, topic: Topic) -> Option<InterestLevel> {
        self.subscriptions.remove(topic)
    }

    /// list all common subscriptions between the two nodes
    pub fn common_subscriptions<'a>(&'a self, other: &'a Self) -> impl Iterator<Item = &'a Topic> {
        self.subscriptions
            .common_subscriptions(&other.subscriptions)
    }

    /// list all common subscribers between the two nodes
    pub fn common_subscribers<'a>(&'a self, other: &'a Self) -> impl Iterator<Item = &'a NetworkID> {
        self.subscribers.intersection(&other.subscribers)
    }

    /// compute the relative proximity between these 2 nodes.
    ///
    /// This is based on the subscription. The more 2 nodes have subscription
    /// in common the _closer_ they are.
    pub fn proximity(&self, other: &Self) -> Proximity {
        self.subscriptions.proximity_to(&other.subscriptions)
    }
}


#[cfg(test)]
mod test {
    use super::*;
    use quickcheck::{Arbitrary, Gen};

    impl Arbitrary for KnownPeer {
        fn arbitrary<G: Gen>(g: &mut G) -> Self {
            use std::ops::Sub;
            let address: Option<SocketAddr> = Arbitrary::arbitrary(g);
            let id = NetworkID::arbitrary(g);

            KnownPeer {
                id,
                address,
                subscriptions: Subscriptions::arbitrary(g),
                subscribers: Arbitrary::arbitrary(g),
                last_gossip: SystemTime::now()
                    .sub(std::time::Duration::new(u32::arbitrary(g) as u64, 0)),
            }
        }
    }
}