mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 18:21:45 +00:00
Upgrade to libp2p v0.50.0 (#3764)
I've needed to do this work in order to do some episub testing. This version of libp2p has not yet been released, so this is left as a draft for when we wish to update. Co-authored-by: Diva M <divma@protonmail.com>
This commit is contained in:
@@ -15,13 +15,6 @@ use types::{
|
||||
};
|
||||
use unused_port::unused_tcp_port;
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(unused)]
|
||||
pub mod behaviour;
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[allow(unused)]
|
||||
pub mod swarm;
|
||||
|
||||
type E = MinimalEthSpec;
|
||||
type ReqId = usize;
|
||||
|
||||
@@ -1,395 +0,0 @@
|
||||
// NOTE: Taken from libp2p's swarm's testing utils.
|
||||
//
|
||||
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a
|
||||
// copy of this software and associated documentation files (the "Software"),
|
||||
// to deal in the Software without restriction, including without limitation
|
||||
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
// and/or sell copies of the Software, and to permit persons to whom the
|
||||
// Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::task::{Context, Poll};
|
||||
|
||||
use libp2p::core::connection::{ConnectedPoint, ConnectionId};
|
||||
use libp2p::core::transport::ListenerId;
|
||||
use libp2p::swarm::handler::{ConnectionHandler, DummyConnectionHandler, IntoConnectionHandler};
|
||||
use libp2p::swarm::{DialError, NetworkBehaviour, NetworkBehaviourAction, PollParameters};
|
||||
use libp2p::{Multiaddr, PeerId};
|
||||
|
||||
/// A `MockBehaviour` is a `NetworkBehaviour` that allows for
|
||||
/// the instrumentation of return values, without keeping
|
||||
/// any further state.
|
||||
pub struct MockBehaviour<
|
||||
THandler = DummyConnectionHandler,
|
||||
TOutEvent = <DummyConnectionHandler as ConnectionHandler>::OutEvent,
|
||||
> where
|
||||
THandler: ConnectionHandler,
|
||||
{
|
||||
/// The prototype protocols handler that is cloned for every
|
||||
/// invocation of `new_handler`.
|
||||
pub handler_proto: THandler,
|
||||
/// The addresses to return from `addresses_of_peer`.
|
||||
pub addresses: HashMap<PeerId, Vec<Multiaddr>>,
|
||||
/// The next action to return from `poll`.
|
||||
///
|
||||
/// An action is only returned once.
|
||||
pub next_action: Option<NetworkBehaviourAction<TOutEvent, THandler>>,
|
||||
}
|
||||
|
||||
impl<THandler, TOutEvent> MockBehaviour<THandler, TOutEvent>
|
||||
where
|
||||
THandler: ConnectionHandler,
|
||||
{
|
||||
pub fn new(handler_proto: THandler) -> Self {
|
||||
MockBehaviour {
|
||||
handler_proto,
|
||||
addresses: HashMap::new(),
|
||||
next_action: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<THandler, TOutEvent> NetworkBehaviour for MockBehaviour<THandler, TOutEvent>
|
||||
where
|
||||
THandler: ConnectionHandler + Clone,
|
||||
THandler::OutEvent: Clone,
|
||||
TOutEvent: Send + 'static,
|
||||
{
|
||||
type ConnectionHandler = THandler;
|
||||
type OutEvent = TOutEvent;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||
self.handler_proto.clone()
|
||||
}
|
||||
|
||||
fn addresses_of_peer(&mut self, p: &PeerId) -> Vec<Multiaddr> {
|
||||
self.addresses.get(p).map_or(Vec::new(), |v| v.clone())
|
||||
}
|
||||
|
||||
fn inject_event(&mut self, _: PeerId, _: ConnectionId, _: THandler::OutEvent) {}
|
||||
|
||||
fn poll(
|
||||
&mut self,
|
||||
_: &mut Context,
|
||||
_: &mut impl PollParameters,
|
||||
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
|
||||
Option::take(&mut self.next_action).map_or(Poll::Pending, Poll::Ready)
|
||||
}
|
||||
}
|
||||
|
||||
/// A `CallTraceBehaviour` is a `NetworkBehaviour` that tracks invocations of callback methods and
|
||||
/// their arguments, wrapping around an inner behaviour. It ensures certain invariants are met.
|
||||
pub struct CallTraceBehaviour<TInner>
|
||||
where
|
||||
TInner: NetworkBehaviour,
|
||||
{
|
||||
inner: TInner,
|
||||
|
||||
pub addresses_of_peer: Vec<PeerId>,
|
||||
pub inject_connection_established: Vec<(PeerId, ConnectionId, ConnectedPoint, usize)>,
|
||||
pub inject_connection_closed: Vec<(PeerId, ConnectionId, ConnectedPoint, usize)>,
|
||||
pub inject_event: Vec<(
|
||||
PeerId,
|
||||
ConnectionId,
|
||||
<<TInner::ConnectionHandler as IntoConnectionHandler>::Handler as ConnectionHandler>::OutEvent,
|
||||
)>,
|
||||
pub inject_dial_failure: Vec<Option<PeerId>>,
|
||||
pub inject_new_listener: Vec<ListenerId>,
|
||||
pub inject_new_listen_addr: Vec<(ListenerId, Multiaddr)>,
|
||||
pub inject_new_external_addr: Vec<Multiaddr>,
|
||||
pub inject_expired_listen_addr: Vec<(ListenerId, Multiaddr)>,
|
||||
pub inject_expired_external_addr: Vec<Multiaddr>,
|
||||
pub inject_listener_error: Vec<ListenerId>,
|
||||
pub inject_listener_closed: Vec<(ListenerId, bool)>,
|
||||
pub poll: usize,
|
||||
}
|
||||
|
||||
impl<TInner> CallTraceBehaviour<TInner>
|
||||
where
|
||||
TInner: NetworkBehaviour,
|
||||
{
|
||||
pub fn new(inner: TInner) -> Self {
|
||||
Self {
|
||||
inner,
|
||||
addresses_of_peer: Vec::new(),
|
||||
inject_connection_established: Vec::new(),
|
||||
inject_connection_closed: Vec::new(),
|
||||
inject_event: Vec::new(),
|
||||
inject_dial_failure: Vec::new(),
|
||||
inject_new_listener: Vec::new(),
|
||||
inject_new_listen_addr: Vec::new(),
|
||||
inject_new_external_addr: Vec::new(),
|
||||
inject_expired_listen_addr: Vec::new(),
|
||||
inject_expired_external_addr: Vec::new(),
|
||||
inject_listener_error: Vec::new(),
|
||||
inject_listener_closed: Vec::new(),
|
||||
poll: 0,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn reset(&mut self) {
|
||||
self.addresses_of_peer = Vec::new();
|
||||
self.inject_connection_established = Vec::new();
|
||||
self.inject_connection_closed = Vec::new();
|
||||
self.inject_event = Vec::new();
|
||||
self.inject_dial_failure = Vec::new();
|
||||
self.inject_new_listen_addr = Vec::new();
|
||||
self.inject_new_external_addr = Vec::new();
|
||||
self.inject_expired_listen_addr = Vec::new();
|
||||
self.inject_listener_error = Vec::new();
|
||||
self.inject_listener_closed = Vec::new();
|
||||
self.poll = 0;
|
||||
}
|
||||
|
||||
pub fn inner(&mut self) -> &mut TInner {
|
||||
&mut self.inner
|
||||
}
|
||||
|
||||
/// Checks that when the expected number of closed connection notifications are received, a
|
||||
/// given number of expected disconnections have been received as well.
|
||||
///
|
||||
/// Returns if the first condition is met.
|
||||
pub fn assert_disconnected(
|
||||
&self,
|
||||
expected_closed_connections: usize,
|
||||
expected_disconnections: usize,
|
||||
) -> bool {
|
||||
if self.inject_connection_closed.len() == expected_closed_connections {
|
||||
assert_eq!(
|
||||
self.inject_connection_closed
|
||||
.iter()
|
||||
.filter(|(.., remaining_established)| { *remaining_established == 0 })
|
||||
.count(),
|
||||
expected_disconnections
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
/// Checks that when the expected number of established connection notifications are received,
|
||||
/// a given number of expected connections have been received as well.
|
||||
///
|
||||
/// Returns if the first condition is met.
|
||||
pub fn assert_connected(
|
||||
&self,
|
||||
expected_established_connections: usize,
|
||||
expected_connections: usize,
|
||||
) -> bool {
|
||||
if self.inject_connection_established.len() == expected_established_connections {
|
||||
assert_eq!(
|
||||
self.inject_connection_established
|
||||
.iter()
|
||||
.filter(|(.., reported_aditional_connections)| {
|
||||
*reported_aditional_connections == 0
|
||||
})
|
||||
.count(),
|
||||
expected_connections
|
||||
);
|
||||
return true;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl<TInner> NetworkBehaviour for CallTraceBehaviour<TInner>
|
||||
where
|
||||
TInner: NetworkBehaviour,
|
||||
<<TInner::ConnectionHandler as IntoConnectionHandler>::Handler as ConnectionHandler>::OutEvent:
|
||||
Clone,
|
||||
{
|
||||
type ConnectionHandler = TInner::ConnectionHandler;
|
||||
type OutEvent = TInner::OutEvent;
|
||||
|
||||
fn new_handler(&mut self) -> Self::ConnectionHandler {
|
||||
self.inner.new_handler()
|
||||
}
|
||||
|
||||
fn addresses_of_peer(&mut self, p: &PeerId) -> Vec<Multiaddr> {
|
||||
self.addresses_of_peer.push(*p);
|
||||
self.inner.addresses_of_peer(p)
|
||||
}
|
||||
|
||||
fn inject_connection_established(
|
||||
&mut self,
|
||||
p: &PeerId,
|
||||
c: &ConnectionId,
|
||||
e: &ConnectedPoint,
|
||||
errors: Option<&Vec<Multiaddr>>,
|
||||
other_established: usize,
|
||||
) {
|
||||
let mut other_peer_connections = self
|
||||
.inject_connection_established
|
||||
.iter()
|
||||
.rev() // take last to first
|
||||
.filter_map(|(peer, .., other_established)| {
|
||||
if p == peer {
|
||||
Some(other_established)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.take(other_established);
|
||||
|
||||
// We are informed that there are `other_established` additional connections. Ensure that the
|
||||
// number of previous connections is consistent with this
|
||||
if let Some(&prev) = other_peer_connections.next() {
|
||||
if prev < other_established {
|
||||
assert_eq!(
|
||||
prev,
|
||||
other_established - 1,
|
||||
"Inconsistent connection reporting"
|
||||
)
|
||||
}
|
||||
assert_eq!(other_peer_connections.count(), other_established - 1);
|
||||
} else {
|
||||
assert_eq!(other_established, 0)
|
||||
}
|
||||
self.inject_connection_established
|
||||
.push((*p, *c, e.clone(), other_established));
|
||||
self.inner
|
||||
.inject_connection_established(p, c, e, errors, other_established);
|
||||
}
|
||||
|
||||
fn inject_connection_closed(
|
||||
&mut self,
|
||||
p: &PeerId,
|
||||
c: &ConnectionId,
|
||||
e: &ConnectedPoint,
|
||||
handler: <Self::ConnectionHandler as IntoConnectionHandler>::Handler,
|
||||
remaining_established: usize,
|
||||
) {
|
||||
let mut other_closed_connections = self
|
||||
.inject_connection_established
|
||||
.iter()
|
||||
.rev() // take last to first
|
||||
.filter_map(|(peer, .., remaining_established)| {
|
||||
if p == peer {
|
||||
Some(remaining_established)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.take(remaining_established);
|
||||
|
||||
// We are informed that there are `other_established` additional connections. Ensure that the
|
||||
// number of previous connections is consistent with this
|
||||
if let Some(&prev) = other_closed_connections.next() {
|
||||
if prev < remaining_established {
|
||||
assert_eq!(
|
||||
prev,
|
||||
remaining_established - 1,
|
||||
"Inconsistent closed connection reporting"
|
||||
)
|
||||
}
|
||||
assert_eq!(other_closed_connections.count(), remaining_established - 1);
|
||||
} else {
|
||||
assert_eq!(remaining_established, 0)
|
||||
}
|
||||
assert!(
|
||||
self.inject_connection_established
|
||||
.iter()
|
||||
.any(|(peer, conn_id, endpoint, _)| (peer, conn_id, endpoint) == (p, c, e)),
|
||||
"`inject_connection_closed` is called only for connections for \
|
||||
which `inject_connection_established` was called first."
|
||||
);
|
||||
self.inject_connection_closed
|
||||
.push((*p, *c, e.clone(), remaining_established));
|
||||
self.inner
|
||||
.inject_connection_closed(p, c, e, handler, remaining_established);
|
||||
}
|
||||
|
||||
fn inject_event(
|
||||
&mut self,
|
||||
p: PeerId,
|
||||
c: ConnectionId,
|
||||
e: <<Self::ConnectionHandler as IntoConnectionHandler>::Handler as ConnectionHandler>::OutEvent,
|
||||
) {
|
||||
assert!(
|
||||
self.inject_connection_established
|
||||
.iter()
|
||||
.any(|(peer_id, conn_id, ..)| *peer_id == p && c == *conn_id),
|
||||
"`inject_event` is called for reported connections."
|
||||
);
|
||||
assert!(
|
||||
!self
|
||||
.inject_connection_closed
|
||||
.iter()
|
||||
.any(|(peer_id, conn_id, ..)| *peer_id == p && c == *conn_id),
|
||||
"`inject_event` is never called for closed connections."
|
||||
);
|
||||
|
||||
self.inject_event.push((p, c, e.clone()));
|
||||
self.inner.inject_event(p, c, e);
|
||||
}
|
||||
|
||||
fn inject_dial_failure(
|
||||
&mut self,
|
||||
p: Option<PeerId>,
|
||||
handler: Self::ConnectionHandler,
|
||||
error: &DialError,
|
||||
) {
|
||||
self.inject_dial_failure.push(p);
|
||||
self.inner.inject_dial_failure(p, handler, error);
|
||||
}
|
||||
|
||||
fn inject_new_listener(&mut self, id: ListenerId) {
|
||||
self.inject_new_listener.push(id);
|
||||
self.inner.inject_new_listener(id);
|
||||
}
|
||||
|
||||
fn inject_new_listen_addr(&mut self, id: ListenerId, a: &Multiaddr) {
|
||||
self.inject_new_listen_addr.push((id, a.clone()));
|
||||
self.inner.inject_new_listen_addr(id, a);
|
||||
}
|
||||
|
||||
fn inject_expired_listen_addr(&mut self, id: ListenerId, a: &Multiaddr) {
|
||||
self.inject_expired_listen_addr.push((id, a.clone()));
|
||||
self.inner.inject_expired_listen_addr(id, a);
|
||||
}
|
||||
|
||||
fn inject_new_external_addr(&mut self, a: &Multiaddr) {
|
||||
self.inject_new_external_addr.push(a.clone());
|
||||
self.inner.inject_new_external_addr(a);
|
||||
}
|
||||
|
||||
fn inject_expired_external_addr(&mut self, a: &Multiaddr) {
|
||||
self.inject_expired_external_addr.push(a.clone());
|
||||
self.inner.inject_expired_external_addr(a);
|
||||
}
|
||||
|
||||
fn inject_listener_error(&mut self, l: ListenerId, e: &(dyn std::error::Error + 'static)) {
|
||||
self.inject_listener_error.push(l);
|
||||
self.inner.inject_listener_error(l, e);
|
||||
}
|
||||
|
||||
fn inject_listener_closed(&mut self, l: ListenerId, r: Result<(), &std::io::Error>) {
|
||||
self.inject_listener_closed.push((l, r.is_ok()));
|
||||
self.inner.inject_listener_closed(l, r);
|
||||
}
|
||||
|
||||
fn poll(
|
||||
&mut self,
|
||||
cx: &mut Context,
|
||||
args: &mut impl PollParameters,
|
||||
) -> Poll<NetworkBehaviourAction<Self::OutEvent, Self::ConnectionHandler>> {
|
||||
self.poll += 1;
|
||||
self.inner.poll(cx, args)
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
use std::collections::HashMap;
|
||||
use std::pin::Pin;
|
||||
|
||||
use super::behaviour::{CallTraceBehaviour, MockBehaviour};
|
||||
|
||||
use futures::stream::Stream;
|
||||
use futures::task::{Context, Poll};
|
||||
use libp2p::swarm::handler::ConnectionHandler;
|
||||
use libp2p::swarm::{IntoConnectionHandler, NetworkBehaviour, Swarm, SwarmBuilder, SwarmEvent};
|
||||
use libp2p::{PeerId, Transport};
|
||||
|
||||
use futures::StreamExt;
|
||||
|
||||
pub fn new_test_swarm<B>(behaviour: B) -> Swarm<B>
|
||||
where
|
||||
B: NetworkBehaviour,
|
||||
{
|
||||
let id_keys = libp2p::identity::Keypair::generate_ed25519();
|
||||
let local_public_key = id_keys.public();
|
||||
let transport = libp2p::core::transport::MemoryTransport::default()
|
||||
.upgrade(libp2p::core::upgrade::Version::V1)
|
||||
.authenticate(libp2p::plaintext::PlainText2Config {
|
||||
local_public_key: local_public_key.clone(),
|
||||
})
|
||||
.multiplex(libp2p::yamux::YamuxConfig::default())
|
||||
.boxed();
|
||||
SwarmBuilder::new(transport, behaviour, local_public_key.into()).build()
|
||||
}
|
||||
|
||||
pub fn random_multiaddr() -> libp2p::multiaddr::Multiaddr {
|
||||
libp2p::multiaddr::Protocol::Memory(rand::random::<u64>()).into()
|
||||
}
|
||||
|
||||
/// Bind a memory multiaddr to a compatible swarm.
|
||||
pub async fn bind_listener<B: NetworkBehaviour>(
|
||||
swarm: &mut Swarm<B>,
|
||||
) -> libp2p::multiaddr::Multiaddr {
|
||||
swarm.listen_on(random_multiaddr()).unwrap();
|
||||
match swarm.select_next_some().await {
|
||||
SwarmEvent::NewListenAddr {
|
||||
listener_id: _,
|
||||
address,
|
||||
} => address,
|
||||
_ => panic!("Testing swarm's first event should be a new listener"),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SwarmPool<B: NetworkBehaviour> {
|
||||
swarms: HashMap<PeerId, Swarm<B>>,
|
||||
}
|
||||
|
||||
impl<B: NetworkBehaviour> SwarmPool<B> {
|
||||
pub fn with_capacity(capacity: usize) -> Self {
|
||||
Self {
|
||||
swarms: HashMap::with_capacity(capacity),
|
||||
}
|
||||
}
|
||||
pub fn insert(&mut self, swarm: Swarm<B>) -> PeerId {
|
||||
let peer_id = *swarm.local_peer_id();
|
||||
self.swarms.insert(peer_id, swarm);
|
||||
peer_id
|
||||
}
|
||||
|
||||
pub fn remove(&mut self, peer_id: &PeerId) {
|
||||
self.swarms.remove(peer_id);
|
||||
}
|
||||
|
||||
pub fn get_mut(&mut self, peer_id: &PeerId) -> Option<&mut Swarm<B>> {
|
||||
self.swarms.get_mut(peer_id)
|
||||
}
|
||||
|
||||
pub fn swarms(&self) -> &HashMap<PeerId, Swarm<B>> {
|
||||
&self.swarms
|
||||
}
|
||||
|
||||
pub fn swarms_mut(&mut self) -> &mut HashMap<PeerId, Swarm<B>> {
|
||||
&mut self.swarms
|
||||
}
|
||||
}
|
||||
|
||||
impl<B> Stream for SwarmPool<B>
|
||||
where
|
||||
B: NetworkBehaviour,
|
||||
<B as NetworkBehaviour>::ConnectionHandler: ConnectionHandler,
|
||||
{
|
||||
type Item = (PeerId,
|
||||
SwarmEvent<<B as NetworkBehaviour>::OutEvent, <<<B as NetworkBehaviour>::ConnectionHandler as IntoConnectionHandler>::Handler as ConnectionHandler>::Error>);
|
||||
|
||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||
let mut polls = self
|
||||
.get_mut()
|
||||
.swarms
|
||||
.iter_mut()
|
||||
.map(|(&peer_id, swarm)| swarm.map(move |ev| (peer_id, ev)))
|
||||
.collect::<futures::stream::SelectAll<_>>();
|
||||
polls.poll_next_unpin(cx)
|
||||
}
|
||||
}
|
||||
@@ -1,203 +0,0 @@
|
||||
#![cfg(not(debug_assertions))]
|
||||
|
||||
mod common;
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use common::{
|
||||
behaviour::{CallTraceBehaviour, MockBehaviour},
|
||||
swarm,
|
||||
};
|
||||
use lighthouse_network::{
|
||||
peer_manager::{config::Config, PeerManagerEvent},
|
||||
NetworkGlobals, PeerAction, PeerInfo, PeerManager, ReportSource,
|
||||
};
|
||||
use types::MinimalEthSpec as E;
|
||||
|
||||
use futures::StreamExt;
|
||||
use libp2p::{
|
||||
core::either::EitherError,
|
||||
swarm::SwarmEvent,
|
||||
swarm::{handler::DummyConnectionHandler, DummyBehaviour, KeepAlive, Swarm},
|
||||
NetworkBehaviour,
|
||||
};
|
||||
|
||||
use slog::debug;
|
||||
|
||||
/// Struct that mimics the lighthouse_network::Service with respect to handling peer manager
|
||||
/// events.
|
||||
// TODO: make this a real struct for more accurate testing.
|
||||
struct Service {
|
||||
swarm: Swarm<Behaviour>,
|
||||
}
|
||||
|
||||
impl Service {
|
||||
async fn select_next_some(&mut self) -> SwarmEvent<Ev, EitherError<void::Void, void::Void>> {
|
||||
let ev = self.swarm.select_next_some().await;
|
||||
match &ev {
|
||||
SwarmEvent::Behaviour(Ev(PeerManagerEvent::Banned(peer_id, _addr_vec))) => {
|
||||
self.swarm.ban_peer_id(*peer_id);
|
||||
}
|
||||
SwarmEvent::Behaviour(Ev(PeerManagerEvent::UnBanned(peer_id, _addr_vec))) => {
|
||||
self.swarm.unban_peer_id(*peer_id);
|
||||
}
|
||||
SwarmEvent::Behaviour(Ev(PeerManagerEvent::DisconnectPeer(peer_id, _reason))) => {
|
||||
// directly disconnect here.
|
||||
let _ = self.swarm.disconnect_peer_id(*peer_id);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
ev
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Ev(PeerManagerEvent);
|
||||
impl From<void::Void> for Ev {
|
||||
fn from(_: void::Void) -> Self {
|
||||
unreachable!("No events are emmited")
|
||||
}
|
||||
}
|
||||
impl From<PeerManagerEvent> for Ev {
|
||||
fn from(ev: PeerManagerEvent) -> Self {
|
||||
Ev(ev)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(NetworkBehaviour)]
|
||||
#[behaviour(out_event = "Ev")]
|
||||
struct Behaviour {
|
||||
pm_call_trace: CallTraceBehaviour<PeerManager<E>>,
|
||||
sibling: MockBehaviour,
|
||||
}
|
||||
|
||||
impl Behaviour {
|
||||
fn new(pm: PeerManager<E>) -> Self {
|
||||
Behaviour {
|
||||
pm_call_trace: CallTraceBehaviour::new(pm),
|
||||
sibling: MockBehaviour::new(DummyConnectionHandler {
|
||||
// The peer manager votes No, so we make sure the combined handler stays alive this
|
||||
// way.
|
||||
keep_alive: KeepAlive::Yes,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn banned_peers_consistency() {
|
||||
let log = common::build_log(slog::Level::Debug, false);
|
||||
let pm_log = log.new(slog::o!("who" => "[PM]"));
|
||||
let globals: Arc<NetworkGlobals<E>> = Arc::new(NetworkGlobals::new_test_globals(&log));
|
||||
|
||||
// Build the peer manager.
|
||||
let (mut pm_service, pm_addr) = {
|
||||
let pm_config = Config {
|
||||
discovery_enabled: false,
|
||||
..Default::default()
|
||||
};
|
||||
let pm = PeerManager::new(pm_config, globals.clone(), &pm_log).unwrap();
|
||||
let mut pm_swarm = swarm::new_test_swarm(Behaviour::new(pm));
|
||||
let pm_addr = swarm::bind_listener(&mut pm_swarm).await;
|
||||
let service = Service { swarm: pm_swarm };
|
||||
(service, pm_addr)
|
||||
};
|
||||
|
||||
let excess_banned_peers = 15;
|
||||
let peers_to_ban =
|
||||
lighthouse_network::peer_manager::peerdb::MAX_BANNED_PEERS + excess_banned_peers;
|
||||
|
||||
// Build all the dummy peers needed.
|
||||
let (mut swarm_pool, peers) = {
|
||||
let mut pool = swarm::SwarmPool::with_capacity(peers_to_ban);
|
||||
let mut peers = HashSet::with_capacity(peers_to_ban);
|
||||
for _ in 0..peers_to_ban {
|
||||
let mut peer_swarm =
|
||||
swarm::new_test_swarm(DummyBehaviour::with_keep_alive(KeepAlive::Yes));
|
||||
let _peer_addr = swarm::bind_listener(&mut peer_swarm).await;
|
||||
// It is ok to dial all at the same time since the swarm handles an event at a time.
|
||||
peer_swarm.dial(pm_addr.clone()).unwrap();
|
||||
let peer_id = pool.insert(peer_swarm);
|
||||
peers.insert(peer_id);
|
||||
}
|
||||
(pool, peers)
|
||||
};
|
||||
|
||||
// we track banned peers at the swarm level here since there is no access to that info.
|
||||
let mut swarm_banned_peers = HashMap::with_capacity(peers_to_ban);
|
||||
let mut peers_unbanned = 0;
|
||||
let timeout = tokio::time::sleep(tokio::time::Duration::from_secs(30));
|
||||
futures::pin_mut!(timeout);
|
||||
|
||||
loop {
|
||||
// poll the pm and dummy swarms.
|
||||
tokio::select! {
|
||||
pm_event = pm_service.select_next_some() => {
|
||||
debug!(log, "[PM] {:?}", pm_event);
|
||||
match pm_event {
|
||||
SwarmEvent::Behaviour(Ev(ev)) => match ev {
|
||||
PeerManagerEvent::Banned(peer_id, _) => {
|
||||
let has_been_unbanned = false;
|
||||
swarm_banned_peers.insert(peer_id, has_been_unbanned);
|
||||
}
|
||||
PeerManagerEvent::UnBanned(peer_id, _) => {
|
||||
*swarm_banned_peers.get_mut(&peer_id).expect("Unbanned peer must be banned first") = true;
|
||||
peers_unbanned += 1;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
SwarmEvent::ConnectionEstablished {
|
||||
peer_id,
|
||||
endpoint: _,
|
||||
num_established: _,
|
||||
concurrent_dial_errors: _,
|
||||
} => {
|
||||
assert!(peers.contains(&peer_id));
|
||||
// now we report the peer as banned.
|
||||
pm_service
|
||||
.swarm
|
||||
.behaviour_mut()
|
||||
.pm_call_trace
|
||||
.inner()
|
||||
.report_peer(
|
||||
&peer_id,
|
||||
PeerAction::Fatal,
|
||||
ReportSource::Processor,
|
||||
None,
|
||||
""
|
||||
);
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
Some((_peer_id, _peer_ev)) = swarm_pool.next() => {
|
||||
// we need to poll the swarms to keep the peers going
|
||||
}
|
||||
_ = timeout.as_mut() => {
|
||||
panic!("Test timeout.")
|
||||
}
|
||||
}
|
||||
|
||||
if peers_unbanned == excess_banned_peers {
|
||||
let pdb = globals.peers.read();
|
||||
let inconsistencies = swarm_banned_peers
|
||||
.into_iter()
|
||||
.map(|(peer_id, was_unbanned)| {
|
||||
was_unbanned
|
||||
!= pdb.peer_info(&peer_id).map_or(
|
||||
false, /* We forgot about a banned peer */
|
||||
PeerInfo::is_banned,
|
||||
)
|
||||
});
|
||||
assert_eq!(
|
||||
inconsistencies
|
||||
.filter(|is_consistent| *is_consistent)
|
||||
.count(),
|
||||
peers_to_ban
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user