simplification of peerset api (#2123)

* Introduction of PeersetHandle

* integrate PeersetHandle with the rest of the codebase

* fix compilation errors

* more tests for peerset, fixed overwriting bug in add_reserved_peer

* Slots data structure and bugfixes for peerset

* bend to pressure

* updated lru-cache to 0.1.2 and updated linked-hash-map to 0.5.2

* peerset discovered list is now a LinkedHashMap

* fix review suggestions

* split back Peerset and PeersetHandle

* test for Peerset::discovered

* applied review suggestions

* fixes to peerset::incoming

* peerset disconnects are all instantaneous

* instantaneous drop in peerset finished

* Peerset::set_reserved_only can also reconnect nodes

* Peerset scores cache uses lru-cache

* remove redundant function call and comment from Peerset::on_set_reserved_only

* add_peer returns SlotState enum

* apply review suggestions

* is_reserved -> is_connected_and_reserved
This commit is contained in:
Marek Kotewicz
2019-04-04 19:40:40 +02:00
committed by GitHub
parent 301844dd56
commit 1142bcde97
8 changed files with 741 additions and 193 deletions
+563 -149
View File
@@ -17,39 +17,138 @@
//! Peer Set Manager (PSM). Contains the strategy for choosing which nodes the network should be
//! connected to.
use std::collections::HashSet;
use futures::{prelude::*, sync::mpsc};
use libp2p::PeerId;
use parking_lot::Mutex;
use std::sync::Arc;
use log::trace;
mod slots;
use std::collections::VecDeque;
use futures::{prelude::*, sync::mpsc, try_ready};
use libp2p::PeerId;
use linked_hash_map::LinkedHashMap;
use log::trace;
use lru_cache::LruCache;
use slots::{SlotType, SlotState, Slots};
pub use serde_json::Value;
/// Shared part of the peer set manager (PSM). Distributed around the code.
pub struct Peerset {
tx: mpsc::UnboundedSender<Message>,
inner: Mutex<Inner>,
const PEERSET_SCORES_CACHE_SIZE: usize = 1000;
/// FIFO-ordered list of nodes that we know exist, but we are not connected to.
#[derive(Debug, Default)]
struct Discovered {
/// Nodes we should connect to first.
reserved: LinkedHashMap<PeerId, ()>,
/// All remaining nodes.
common: LinkedHashMap<PeerId, ()>,
}
struct Inner {
/// List of nodes that we know exist but we are not connected to.
impl Discovered {
/// Returns true if we already know given node.
fn contains(&self, peer_id: &PeerId) -> bool {
self.reserved.contains_key(peer_id) || self.common.contains_key(peer_id)
}
/// Returns true if given node is reserved.
fn is_reserved(&self, peer_id: &PeerId) -> bool {
self.reserved.contains_key(peer_id)
}
/// Adds new peer of a given type.
fn add_peer(&mut self, peer_id: PeerId, slot_type: SlotType) {
if !self.contains(&peer_id) {
match slot_type {
SlotType::Common => self.common.insert(peer_id, ()),
SlotType::Reserved => self.reserved.insert(peer_id, ()),
};
}
}
/// Pops the oldest peer from the list.
fn pop_peer(&mut self, reserved_only: bool) -> Option<(PeerId, SlotType)> {
if let Some((peer_id, _)) = self.reserved.pop_front() {
return Some((peer_id, SlotType::Reserved));
}
if reserved_only {
return None;
}
self.common.pop_front()
.map(|(peer_id, _)| (peer_id, SlotType::Common))
}
/// Marks the node as not reserved.
fn mark_not_reserved(&mut self, peer_id: &PeerId) {
if let Some(_) = self.reserved.remove(peer_id) {
self.common.insert(peer_id.clone(), ());
}
}
/// Removes the node from the list.
fn remove_peer(&mut self, peer_id: &PeerId) {
self.reserved.remove(peer_id);
self.common.remove(peer_id);
}
}
#[derive(Debug)]
struct PeersetData {
/// List of nodes that we know exist, but we are not connected to.
/// Elements in this list must never be in `out_slots` or `in_slots`.
discovered: Vec<PeerId>,
/// List of reserved nodes.
reserved: HashSet<PeerId>,
discovered: Discovered,
/// If true, we only accept reserved nodes.
reserved_only: bool,
/// Node slots for outgoing connections. Each slot contains either `None` if the node is free,
/// or `Some` if it is assigned to a peer.
out_slots: Vec<Option<PeerId>>,
/// Node slots for incoming connections. Each slot contains either `None` if the node is free,
/// or `Some` if it is assigned to a peer.
in_slots: Vec<Option<PeerId>>,
/// Node slots for outgoing connections.
out_slots: Slots,
/// Node slots for incoming connections.
in_slots: Slots,
/// List of node scores.
scores: LruCache<PeerId, i32>,
}
#[derive(Debug)]
enum Action {
AddReservedPeer(PeerId),
RemoveReservedPeer(PeerId),
SetReservedOnly(bool),
ReportPeer(PeerId, i32),
}
/// Shared handle to the peer set manager (PSM). Distributed around the code.
#[derive(Debug, Clone)]
pub struct PeersetHandle {
tx: mpsc::UnboundedSender<Action>,
}
impl PeersetHandle {
/// Adds a new reserved peer. The peerset will make an effort to always remain connected to
/// this peer.
///
/// Has no effect if the node was already a reserved peer.
///
/// > **Note**: Keep in mind that the networking has to know an address for this node,
/// > otherwise it will not be able to connect to it.
pub fn add_reserved_peer(&self, peer_id: PeerId) {
let _ = self.tx.unbounded_send(Action::AddReservedPeer(peer_id));
}
/// Remove a previously-added reserved peer.
///
/// Has no effect if the node was not a reserved peer.
pub fn remove_reserved_peer(&self, peer_id: PeerId) {
let _ = self.tx.unbounded_send(Action::RemoveReservedPeer(peer_id));
}
/// Sets whether or not the peerset only has connections .
pub fn set_reserved_only(&self, reserved: bool) {
let _ = self.tx.unbounded_send(Action::SetReservedOnly(reserved));
}
/// Reports an adjustment to the reputation of the given peer.
pub fn report_peer(&self, peer_id: PeerId, score_diff: i32) {
let _ = self.tx.unbounded_send(Action::ReportPeer(peer_id, score_diff));
}
}
/// Message that can be sent by the peer set manager (PSM).
#[derive(Debug)]
#[derive(Debug, PartialEq)]
pub enum Message {
/// Request to open a connection to the given peer. From the point of view of the PSM, we are
/// immediately connected.
@@ -104,130 +203,158 @@ pub struct PeersetConfig {
///
/// Implements the `Stream` trait and can be polled for messages. The `Stream` never ends and never
/// errors.
pub struct PeersetMut {
parent: Arc<Peerset>,
rx: mpsc::UnboundedReceiver<Message>,
#[derive(Debug)]
pub struct Peerset {
data: PeersetData,
rx: mpsc::UnboundedReceiver<Action>,
message_queue: VecDeque<Message>,
}
impl Peerset {
/// Builds a new peerset from the given configuration.
pub fn from_config(config: PeersetConfig) -> (Arc<Peerset>, PeersetMut) {
pub fn from_config(config: PeersetConfig) -> (Peerset, PeersetHandle) {
let (tx, rx) = mpsc::unbounded();
let mut inner = Inner {
discovered: config.bootnodes.into_iter().collect(),
reserved: Default::default(),
let data = PeersetData {
discovered: Default::default(),
reserved_only: config.reserved_only,
out_slots: (0 .. config.out_peers).map(|_| None).collect(),
in_slots: (0 .. config.in_peers).map(|_| None).collect(),
out_slots: Slots::new(config.out_peers),
in_slots: Slots::new(config.in_peers),
scores: LruCache::new(PEERSET_SCORES_CACHE_SIZE),
};
alloc_slots(&mut inner, &tx);
let peerset = Arc::new(Peerset {
let handle = PeersetHandle {
tx,
inner: Mutex::new(inner),
});
let rx = PeersetMut {
parent: peerset.clone(),
rx,
};
for reserved in config.reserved_nodes {
peerset.add_reserved_peer(reserved);
let mut peerset = Peerset {
data,
rx,
message_queue: VecDeque::new(),
};
for peer_id in config.reserved_nodes {
peerset.data.discovered.add_peer(peer_id, SlotType::Reserved);
}
(peerset, rx)
for peer_id in config.bootnodes {
peerset.data.discovered.add_peer(peer_id, SlotType::Common);
}
peerset.alloc_slots();
(peerset, handle)
}
/// Adds a new reserved peer. The peerset will make an effort to always remain connected to
/// this peer.
///
/// Has no effect if the node was already a reserved peer.
///
/// > **Note**: Keep in mind that the networking has to know an address for this node,
/// > otherwise it will not be able to connect to it.
pub fn add_reserved_peer(&self, peer_id: PeerId) {
let mut inner = self.inner.lock();
if !inner.reserved.insert(peer_id.clone()) {
// Immediately return if this peer was already in the list.
return;
}
fn on_add_reserved_peer(&mut self, peer_id: PeerId) {
// Nothing more to do if we're already connected.
if inner.out_slots.iter().chain(inner.in_slots.iter()).any(|s| s.as_ref() == Some(&peer_id)) {
if self.data.in_slots.contains(&peer_id) {
self.data.in_slots.mark_reserved(&peer_id);
return;
}
// Assign a slot for this reserved peer.
if let Some(pos) = inner.out_slots.iter().position(|s| s.as_ref().map(|n| !inner.reserved.contains(n)).unwrap_or(true)) {
let _ = self.tx.unbounded_send(Message::Connect(peer_id.clone()));
inner.out_slots[pos] = Some(peer_id);
match self.data.out_slots.add_peer(peer_id, SlotType::Reserved) {
SlotState::Added(peer_id) => {
// reserved node may have been previously stored as normal node in discovered list
self.data.discovered.remove_peer(&peer_id);
} else {
// All slots are filled with reserved peers.
if inner.discovered.iter().all(|p| *p != peer_id) {
inner.discovered.push(peer_id);
// notify that connection has been made
self.message_queue.push_back(Message::Connect(peer_id));
return;
},
SlotState::Swaped { removed, added } => {
// reserved node may have been previously stored as normal node in discovered list
self.data.discovered.remove_peer(&added);
// let's add the peer we disconnected from to the discovered list again
self.data.discovered.add_peer(removed.clone(), SlotType::Common);
// swap connections
self.message_queue.push_back(Message::Drop(removed));
self.message_queue.push_back(Message::Connect(added));
}
SlotState::AlreadyConnected(_) | SlotState::Upgraded(_) => {
return;
}
SlotState::MaxConnections(peer_id) => {
self.data.discovered.add_peer(peer_id, SlotType::Reserved);
return;
}
}
}
/// Remove a previously-added reserved peer.
///
/// Has no effect if the node was not a reserved peer.
pub fn remove_reserved_peer(&self, peer_id: &PeerId) {
let mut inner = self.inner.lock();
inner.reserved.remove(peer_id);
fn on_remove_reserved_peer(&mut self, peer_id: PeerId) {
self.data.in_slots.mark_not_reserved(&peer_id);
self.data.out_slots.mark_not_reserved(&peer_id);
self.data.discovered.mark_not_reserved(&peer_id);
if self.data.reserved_only {
if self.data.in_slots.clear_slot(&peer_id) || self.data.out_slots.clear_slot(&peer_id) {
// insert peer back into discovered list
self.data.discovered.add_peer(peer_id.clone(), SlotType::Common);
self.message_queue.push_back(Message::Drop(peer_id));
// call alloc_slots again, cause we may have some reserved peers in discovered list
// waiting for the slot that was just cleared
self.alloc_slots();
}
}
}
/// Sets whether or not the peerset only has connections .
pub fn set_reserved_only(&self, reserved_only: bool) {
let mut inner = self.inner.lock();
let inner = &mut *inner; // Fixes a borrowing issue.
inner.reserved_only = reserved_only;
fn on_set_reserved_only(&mut self, reserved_only: bool) {
// Disconnect non-reserved nodes.
if reserved_only {
for slot in inner.out_slots.iter_mut().chain(inner.in_slots.iter_mut()) {
if let Some(peer) = slot.as_ref() {
if inner.reserved.contains(peer) {
continue;
}
self.data.reserved_only = reserved_only;
if self.data.reserved_only {
for peer_id in self.data.in_slots.clear_common_slots().into_iter().chain(self.data.out_slots.clear_common_slots().into_iter()) {
// insert peer back into discovered list
self.data.discovered.add_peer(peer_id.clone(), SlotType::Common);
self.message_queue.push_back(Message::Drop(peer_id));
}
} else {
self.alloc_slots();
}
}
let _ = self.tx.unbounded_send(Message::Drop(peer.clone()));
}
fn on_report_peer(&mut self, peer_id: PeerId, score_diff: i32) {
let score = match self.data.scores.get_mut(&peer_id) {
Some(score) => {
*score = score.saturating_add(score_diff);
*score
},
None => {
self.data.scores.insert(peer_id.clone(), score_diff);
score_diff
}
};
*slot = None;
if score < 0 {
// peer will be removed from `in_slots` or `out_slots` in `on_dropped` method
if self.data.in_slots.contains(&peer_id) || self.data.out_slots.contains(&peer_id) {
self.data.in_slots.clear_slot(&peer_id);
self.data.out_slots.clear_slot(&peer_id);
self.message_queue.push_back(Message::Drop(peer_id));
}
}
}
/// Reports an adjustement to the reputation of the given peer.
pub fn report_peer(&self, _peer_id: &PeerId, _score_diff: i32) {
// This is not implemented in this dummy implementation.
}
}
fn alloc_slots(inner: &mut Inner, tx: &mpsc::UnboundedSender<Message>) {
if inner.reserved_only {
return;
}
for slot in inner.out_slots.iter_mut() {
if slot.is_some() {
continue;
}
if !inner.discovered.is_empty() {
let elem = inner.discovered.remove(0);
*slot = Some(elem.clone());
let _ = tx.unbounded_send(Message::Connect(elem));
fn alloc_slots(&mut self) {
while let Some((peer_id, slot_type)) = self.data.discovered.pop_peer(self.data.reserved_only) {
match self.data.out_slots.add_peer(peer_id, slot_type) {
SlotState::Added(peer_id) => {
self.message_queue.push_back(Message::Connect(peer_id));
},
SlotState::Swaped { removed, added } => {
// insert peer back into discovered list
self.data.discovered.add_peer(removed.clone(), SlotType::Common);
self.message_queue.push_back(Message::Drop(removed));
self.message_queue.push_back(Message::Connect(added));
}
SlotState::Upgraded(_) | SlotState::AlreadyConnected(_) => {
// TODO: we should never reach this point
},
SlotState::MaxConnections(peer_id) => {
self.data.discovered.add_peer(peer_id, slot_type);
break;
},
}
}
}
}
impl PeersetMut {
/// Indicate that we received an incoming connection. Must be answered either with
/// a corresponding `Accept` or `Reject`, except if we were already connected to this peer.
///
@@ -237,21 +364,51 @@ impl PeersetMut {
///
/// Because of concurrency issues, it is acceptable to call `incoming` with a `PeerId` the
/// peerset is already connected to, in which case it must not answer.
pub fn incoming(&self, peer_id: PeerId, index: IncomingIndex) {
let mut inner = self.parent.inner.lock();
trace!("Incoming {}\nin_slots={:?}\nout_slots={:?}", peer_id, inner.in_slots, inner.out_slots);
if inner.out_slots.iter().chain(inner.in_slots.iter()).any(|s| s.as_ref() == Some(&peer_id)) {
return
pub fn incoming(&mut self, peer_id: PeerId, index: IncomingIndex) {
trace!("Incoming {}\nin_slots={:?}\nout_slots={:?}", peer_id, self.data.in_slots, self.data.out_slots);
// if `reserved_only` is set, but this peer is not a part of our discovered list,
// a) it is not reserved, so we reject the connection
// b) we are already connected to it, so we reject the connection
if self.data.reserved_only && !self.data.discovered.is_reserved(&peer_id) {
self.message_queue.push_back(Message::Reject(index));
return;
}
if let Some(pos) = inner.in_slots.iter().position(|s| s.is_none()) {
inner.in_slots[pos] = Some(peer_id);
let _ = self.parent.tx.unbounded_send(Message::Accept(index));
// check if we are already connected to this peer
if self.data.out_slots.contains(&peer_id) {
// we are already connected. in this case we do not answer
return;
}
let slot_type = if self.data.reserved_only {
SlotType::Reserved
} else {
if inner.discovered.iter().all(|p| *p != peer_id) {
inner.discovered.push(peer_id);
}
let _ = self.parent.tx.unbounded_send(Message::Reject(index));
SlotType::Common
};
match self.data.in_slots.add_peer(peer_id, slot_type) {
SlotState::Added(peer_id) => {
// reserved node may have been previously stored as normal node in discovered list
self.data.discovered.remove_peer(&peer_id);
self.message_queue.push_back(Message::Accept(index));
return;
},
SlotState::Swaped { removed, added } => {
// reserved node may have been previously stored as normal node in discovered list
self.data.discovered.remove_peer(&added);
// swap connections.
self.message_queue.push_back(Message::Drop(removed));
self.message_queue.push_back(Message::Accept(index));
},
SlotState::AlreadyConnected(_) | SlotState::Upgraded(_) => {
// we are already connected. in this case we do not answer
return;
},
SlotState::MaxConnections(peer_id) => {
self.data.discovered.add_peer(peer_id, slot_type);
self.message_queue.push_back(Message::Reject(index));
return;
},
}
}
@@ -259,48 +416,35 @@ impl PeersetMut {
///
/// Must only be called after the PSM has either generated a `Connect` message with this
/// `PeerId`, or accepted an incoming connection with this `PeerId`.
pub fn dropped(&self, peer_id: &PeerId) {
let mut inner = self.parent.inner.lock();
let inner = &mut *inner; // Fixes a borrowing issue.
trace!("Dropping {}\nin_slots={:?}\nout_slots={:?}", peer_id, inner.in_slots, inner.out_slots);
pub fn dropped(&mut self, peer_id: PeerId) {
trace!("Dropping {}\nin_slots={:?}\nout_slots={:?}", peer_id, self.data.in_slots, self.data.out_slots);
// Automatically connect back if reserved.
if inner.reserved.contains(peer_id) {
let _ = self.parent.tx.unbounded_send(Message::Connect(peer_id.clone()));
return
if self.data.in_slots.is_connected_and_reserved(&peer_id) || self.data.out_slots.is_connected_and_reserved(&peer_id) {
self.message_queue.push_back(Message::Connect(peer_id));
return;
}
// Otherwise, free the slot.
for slot in inner.out_slots.iter_mut().chain(inner.in_slots.iter_mut()) {
if slot.as_ref() == Some(peer_id) {
*slot = None;
break;
}
}
self.data.in_slots.clear_slot(&peer_id);
self.data.out_slots.clear_slot(&peer_id);
// Note: in this dummy implementation we consider that peers never expire. As soon as we
// are disconnected from a peer, we try again.
if inner.discovered.iter().all(|p| p != peer_id) {
inner.discovered.push(peer_id.clone());
}
alloc_slots(inner, &self.parent.tx);
self.data.discovered.add_peer(peer_id, SlotType::Common);
self.alloc_slots();
}
/// Adds a discovered peer id to the PSM.
///
/// > **Note**: There is no equivalent "expired" message, meaning that it is the responsibility
/// > of the PSM to remove `PeerId`s that fail to dial too often.
pub fn discovered(&self, peer_id: PeerId) {
let mut inner = self.parent.inner.lock();
if inner.out_slots.iter().chain(inner.in_slots.iter()).any(|p| p.as_ref() == Some(&peer_id)) {
pub fn discovered(&mut self, peer_id: PeerId) {
if self.data.in_slots.contains(&peer_id) || self.data.out_slots.contains(&peer_id) {
return;
}
if inner.discovered.iter().all(|p| *p != peer_id) {
inner.discovered.push(peer_id);
}
alloc_slots(&mut inner, &self.parent.tx);
self.data.discovered.add_peer(peer_id, SlotType::Common);
self.alloc_slots();
}
/// Produces a JSON object containing the state of the peerset manager, for debugging purposes.
@@ -309,11 +453,281 @@ impl PeersetMut {
}
}
impl Stream for PeersetMut {
impl Stream for Peerset {
type Item = Message;
type Error = ();
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
self.rx.poll()
loop {
if let Some(message) = self.message_queue.pop_front() {
return Ok(Async::Ready(Some(message)));
}
match try_ready!(self.rx.poll()) {
None => return Ok(Async::Ready(None)),
Some(action) => match action {
Action::AddReservedPeer(peer_id) => self.on_add_reserved_peer(peer_id),
Action::RemoveReservedPeer(peer_id) => self.on_remove_reserved_peer(peer_id),
Action::SetReservedOnly(reserved) => self.on_set_reserved_only(reserved),
Action::ReportPeer(peer_id, score_diff) => self.on_report_peer(peer_id, score_diff),
}
}
}
}
}
#[cfg(test)]
mod tests {
use libp2p::PeerId;
use futures::prelude::*;
use super::{PeersetConfig, Peerset, Message, IncomingIndex};
fn assert_messages(mut peerset: Peerset, messages: Vec<Message>) -> Peerset {
for expected_message in messages {
let (message, p) = next_message(peerset).expect("expected message");
assert_eq!(message, expected_message);
peerset = p;
}
assert!(peerset.message_queue.is_empty());
peerset
}
fn next_message(peerset: Peerset) -> Result<(Message, Peerset), ()> {
let (next, peerset) = peerset.into_future()
.wait()
.map_err(|_| ())?;
let message = next.ok_or_else(|| ())?;
Ok((message, peerset))
}
#[test]
fn test_peerset_from_config_with_bootnodes() {
let bootnode = PeerId::random();
let bootnode2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 2,
bootnodes: vec![bootnode.clone(), bootnode2.clone()],
reserved_only: false,
reserved_nodes: Vec::new(),
};
let (peerset, _handle) = Peerset::from_config(config);
assert_messages(peerset, vec![
Message::Connect(bootnode),
Message::Connect(bootnode2),
]);
}
#[test]
fn test_peerset_from_config_with_reserved_nodes() {
let bootnode = PeerId::random();
let bootnode2 = PeerId::random();
let reserved_peer = PeerId::random();
let reserved_peer2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 3,
bootnodes: vec![bootnode.clone(), bootnode2.clone()],
reserved_only: false,
reserved_nodes: vec![reserved_peer.clone(), reserved_peer2.clone()],
};
let (peerset, _handle) = Peerset::from_config(config);
assert_messages(peerset, vec![
Message::Connect(reserved_peer),
Message::Connect(reserved_peer2),
Message::Connect(bootnode)
]);
}
#[test]
fn test_peerset_add_reserved_peer() {
let bootnode = PeerId::random();
let reserved_peer = PeerId::random();
let reserved_peer2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 2,
bootnodes: vec![bootnode],
reserved_only: true,
reserved_nodes: Vec::new(),
};
let (peerset, handle) = Peerset::from_config(config);
handle.add_reserved_peer(reserved_peer.clone());
handle.add_reserved_peer(reserved_peer2.clone());
assert_messages(peerset, vec![
Message::Connect(reserved_peer),
Message::Connect(reserved_peer2)
]);
}
#[test]
fn test_peerset_remove_reserved_peer() {
let reserved_peer = PeerId::random();
let reserved_peer2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 2,
bootnodes: vec![],
reserved_only: false,
reserved_nodes: vec![reserved_peer.clone(), reserved_peer2.clone()],
};
let (peerset, handle) = Peerset::from_config(config);
handle.remove_reserved_peer(reserved_peer.clone());
let peerset = assert_messages(peerset, vec![
Message::Connect(reserved_peer.clone()),
Message::Connect(reserved_peer2.clone()),
]);
handle.set_reserved_only(true);
handle.remove_reserved_peer(reserved_peer2.clone());
assert_messages(peerset, vec![
Message::Drop(reserved_peer),
Message::Drop(reserved_peer2),
]);
}
#[test]
fn test_peerset_set_reserved_only() {
let bootnode = PeerId::random();
let bootnode2 = PeerId::random();
let reserved_peer = PeerId::random();
let reserved_peer2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 4,
bootnodes: vec![bootnode.clone(), bootnode2.clone()],
reserved_only: false,
reserved_nodes: vec![reserved_peer.clone(), reserved_peer2.clone()],
};
let (peerset, handle) = Peerset::from_config(config);
handle.set_reserved_only(true);
handle.set_reserved_only(false);
assert_messages(peerset, vec![
Message::Connect(reserved_peer),
Message::Connect(reserved_peer2),
Message::Connect(bootnode.clone()),
Message::Connect(bootnode2.clone()),
Message::Drop(bootnode.clone()),
Message::Drop(bootnode2.clone()),
Message::Connect(bootnode),
Message::Connect(bootnode2),
]);
}
#[test]
fn test_peerset_report_peer() {
let bootnode = PeerId::random();
let bootnode2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 1,
bootnodes: vec![bootnode.clone(), bootnode2.clone()],
reserved_only: false,
reserved_nodes: Vec::new(),
};
let (peerset, handle) = Peerset::from_config(config);
handle.report_peer(bootnode2, -1);
handle.report_peer(bootnode.clone(), -1);
assert_messages(peerset, vec![
Message::Connect(bootnode.clone()),
Message::Drop(bootnode)
]);
}
#[test]
fn test_peerset_incoming() {
let bootnode = PeerId::random();
let incoming = PeerId::random();
let incoming2 = PeerId::random();
let incoming3 = PeerId::random();
let ii = IncomingIndex(1);
let ii2 = IncomingIndex(2);
let ii3 = IncomingIndex(3);
let ii4 = IncomingIndex(3);
let config = PeersetConfig {
in_peers: 2,
out_peers: 1,
bootnodes: vec![bootnode.clone()],
reserved_only: false,
reserved_nodes: Vec::new(),
};
let (mut peerset, _handle) = Peerset::from_config(config);
peerset.incoming(incoming.clone(), ii);
peerset.incoming(incoming.clone(), ii4);
peerset.incoming(incoming2.clone(), ii2);
peerset.incoming(incoming3.clone(), ii3);
assert_messages(peerset, vec![
Message::Connect(bootnode.clone()),
Message::Accept(ii),
Message::Accept(ii2),
Message::Reject(ii3),
]);
}
#[test]
fn test_peerset_dropped() {
let bootnode = PeerId::random();
let bootnode2 = PeerId::random();
let reserved_peer = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 2,
bootnodes: vec![bootnode.clone(), bootnode2.clone()],
reserved_only: false,
reserved_nodes: vec![reserved_peer.clone()],
};
let (peerset, _handle) = Peerset::from_config(config);
let mut peerset = assert_messages(peerset, vec![
Message::Connect(reserved_peer.clone()),
Message::Connect(bootnode.clone()),
]);
peerset.dropped(reserved_peer.clone());
peerset.dropped(bootnode);
let _peerset = assert_messages(peerset, vec![
Message::Connect(reserved_peer),
Message::Connect(bootnode2),
]);
}
#[test]
fn test_peerset_discovered() {
let bootnode = PeerId::random();
let discovered = PeerId::random();
let discovered2 = PeerId::random();
let config = PeersetConfig {
in_peers: 0,
out_peers: 2,
bootnodes: vec![bootnode.clone()],
reserved_only: false,
reserved_nodes: vec![],
};
let (mut peerset, _handle) = Peerset::from_config(config);
peerset.discovered(discovered.clone());
peerset.discovered(discovered.clone());
peerset.discovered(discovered2);
assert_messages(peerset, vec![
Message::Connect(bootnode),
Message::Connect(discovered),
]);
}
}