Activate peer scoring (#1284)

* Initial score structure

* Peer manager update

* Updates to dialing

* Correct tests

* Correct typos and remove unused function

* Integrate scoring into the network crate

* Clean warnings

* Formatting

* Shift core functionality into the behaviour

* Temp commit

* Shift disconnections into the behaviour

* Temp commit

* Update libp2p and gossipsub

* Remove gossipsub lru cache

* Correct merge conflicts

* Modify handler and correct tests

* Update enr network globals on socket update

* Apply clippy lints

* Add new prysm fingerprint

* More clippy fixes
This commit is contained in:
Age Manning
2020-07-07 10:13:16 +10:00
committed by GitHub
parent 5977c00edb
commit 5bc8fea2e0
26 changed files with 1339 additions and 934 deletions

View File

@@ -80,6 +80,7 @@ pub enum DelegateError<TSpec: EthSpec> {
Gossipsub(<GossipHandler as ProtocolsHandler>::Error),
RPC(<RPCHandler<TSpec> as ProtocolsHandler>::Error),
Identify(<IdentifyHandler as ProtocolsHandler>::Error),
Disconnected,
}
impl<TSpec: EthSpec> std::error::Error for DelegateError<TSpec> {}
@@ -93,6 +94,7 @@ impl<TSpec: EthSpec> std::fmt::Display for DelegateError<TSpec> {
DelegateError::Gossipsub(err) => err.fmt(formater),
DelegateError::RPC(err) => err.fmt(formater),
DelegateError::Identify(err) => err.fmt(formater),
DelegateError::Disconnected => write!(formater, "Disconnected"),
}
}
}
@@ -135,11 +137,10 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
let rpc_proto = self.rpc_handler.listen_protocol();
let identify_proto = self.identify_handler.listen_protocol();
let timeout = gossip_proto
let timeout = *gossip_proto
.timeout()
.max(rpc_proto.timeout())
.max(identify_proto.timeout())
.clone();
.max(identify_proto.timeout());
let select = SelectUpgrade::new(
gossip_proto.into_upgrade().1,
@@ -317,7 +318,7 @@ impl<TSpec: EthSpec> ProtocolsHandler for DelegatingHandler<TSpec> {
}
Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest { protocol, info }) => {
return Poll::Ready(ProtocolsHandlerEvent::OutboundSubstreamRequest {
protocol: protocol.map_upgrade(|u| EitherUpgrade::A(u)),
protocol: protocol.map_upgrade(EitherUpgrade::A),
info: EitherOutput::First(info),
});
}

View File

@@ -95,14 +95,9 @@ impl<TSpec: EthSpec> ProtocolsHandler for BehaviourHandler<TSpec> {
self.delegate.inject_dial_upgrade_error(info, err)
}
// We don't use the keep alive to disconnect. This is handled in the poll
fn connection_keep_alive(&self) -> KeepAlive {
if self.shutting_down {
let rpc_keep_alive = self.delegate.rpc().connection_keep_alive();
let identify_keep_alive = self.delegate.identify().connection_keep_alive();
rpc_keep_alive.max(identify_keep_alive)
} else {
KeepAlive::Yes
}
KeepAlive::Yes
}
fn poll(
@@ -116,6 +111,15 @@ impl<TSpec: EthSpec> ProtocolsHandler for BehaviourHandler<TSpec> {
Self::Error,
>,
> {
// Disconnect if the sub-handlers are ready.
if self.shutting_down {
let rpc_keep_alive = self.delegate.rpc().connection_keep_alive();
let identify_keep_alive = self.delegate.identify().connection_keep_alive();
if KeepAlive::No == rpc_keep_alive.max(identify_keep_alive) {
return Poll::Ready(ProtocolsHandlerEvent::Close(DelegateError::Disconnected));
}
}
match self.delegate.poll(cx) {
Poll::Ready(ProtocolsHandlerEvent::Custom(event)) => {
return Poll::Ready(ProtocolsHandlerEvent::Custom(

View File

@@ -1,4 +1,4 @@
use crate::peer_manager::{PeerManager, PeerManagerEvent};
use crate::peer_manager::{score::PeerAction, PeerManager, PeerManagerEvent};
use crate::rpc::*;
use crate::types::{GossipEncoding, GossipKind, GossipTopic};
use crate::Eth2Enr;
@@ -21,6 +21,7 @@ use libp2p::{
};
use slog::{crit, debug, o};
use std::{
collections::VecDeque,
marker::PhantomData,
sync::Arc,
task::{Context, Poll},
@@ -46,10 +47,12 @@ pub struct Behaviour<TSpec: EthSpec> {
identify: Identify,
/// The peer manager that keeps track of peer's reputation and status.
peer_manager: PeerManager<TSpec>,
/// The events generated by this behaviour to be consumed in the swarm poll.
events: Vec<BehaviourEvent<TSpec>>,
/// The output events generated by this behaviour to be consumed in the swarm poll.
events: VecDeque<BehaviourEvent<TSpec>>,
/// Events generated in the global behaviour to be sent to the behaviour handler.
handler_events: VecDeque<NBAction<BehaviourHandlerIn<TSpec>, BehaviourEvent<TSpec>>>,
/// Queue of peers to disconnect.
peers_to_dc: Vec<PeerId>,
peers_to_dc: VecDeque<PeerId>,
/// The current meta data of the node, so respond to pings and get metadata
meta_data: MetaData<TSpec>,
/// A collections of variables accessible outside the network service.
@@ -58,173 +61,12 @@ pub struct Behaviour<TSpec: EthSpec> {
// NOTE: This can be accessed via the network_globals ENR. However we keep it here for quick
// lookups for every gossipsub message send.
enr_fork_id: EnrForkId,
/// The waker for the current thread.
waker: Option<std::task::Waker>,
/// Logger for behaviour actions.
log: slog::Logger,
}
/// Calls the given function with the given args on all sub behaviours.
macro_rules! delegate_to_behaviours {
($self: ident, $fn: ident, $($arg: ident), *) => {
$self.gossipsub.$fn($($arg),*);
$self.eth2_rpc.$fn($($arg),*);
$self.identify.$fn($($arg),*);
};
}
impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
type ProtocolsHandler = BehaviourHandler<TSpec>;
type OutEvent = BehaviourEvent<TSpec>;
fn new_handler(&mut self) -> Self::ProtocolsHandler {
BehaviourHandler::new(&mut self.gossipsub, &mut self.eth2_rpc, &mut self.identify)
}
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
self.peer_manager.addresses_of_peer(peer_id)
}
fn inject_connected(&mut self, peer_id: &PeerId) {
delegate_to_behaviours!(self, inject_connected, peer_id);
}
fn inject_disconnected(&mut self, peer_id: &PeerId) {
delegate_to_behaviours!(self, inject_disconnected, peer_id);
}
fn inject_connection_established(
&mut self,
peer_id: &PeerId,
conn_id: &ConnectionId,
endpoint: &ConnectedPoint,
) {
delegate_to_behaviours!(
self,
inject_connection_established,
peer_id,
conn_id,
endpoint
);
}
fn inject_connection_closed(
&mut self,
peer_id: &PeerId,
conn_id: &ConnectionId,
endpoint: &ConnectedPoint,
) {
delegate_to_behaviours!(self, inject_connection_closed, peer_id, conn_id, endpoint);
}
fn inject_addr_reach_failure(
&mut self,
peer_id: Option<&PeerId>,
addr: &Multiaddr,
error: &dyn std::error::Error,
) {
delegate_to_behaviours!(self, inject_addr_reach_failure, peer_id, addr, error);
}
fn inject_dial_failure(&mut self, peer_id: &PeerId) {
delegate_to_behaviours!(self, inject_dial_failure, peer_id);
}
fn inject_new_listen_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_new_listen_addr, addr);
}
fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_expired_listen_addr, addr);
}
fn inject_new_external_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_new_external_addr, addr);
}
fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) {
delegate_to_behaviours!(self, inject_listener_error, id, err);
}
fn inject_listener_closed(&mut self, id: ListenerId, reason: Result<(), &std::io::Error>) {
delegate_to_behaviours!(self, inject_listener_closed, id, reason);
}
fn inject_event(
&mut self,
peer_id: PeerId,
conn_id: ConnectionId,
event: <Self::ProtocolsHandler as ProtocolsHandler>::OutEvent,
) {
match event {
// Events comming from the handler, redirected to each behaviour
BehaviourHandlerOut::Delegate(delegate) => match *delegate {
DelegateOut::Gossipsub(ev) => self.gossipsub.inject_event(peer_id, conn_id, ev),
DelegateOut::RPC(ev) => self.eth2_rpc.inject_event(peer_id, conn_id, ev),
DelegateOut::Identify(ev) => self.identify.inject_event(peer_id, conn_id, *ev),
},
/* Custom events sent BY the handler */
BehaviourHandlerOut::Custom => {
// TODO: implement
}
}
}
fn poll(
&mut self,
cx: &mut Context,
poll_params: &mut impl PollParameters,
) -> Poll<NBAction<<Self::ProtocolsHandler as ProtocolsHandler>::InEvent, Self::OutEvent>> {
// TODO: move where it's less distracting
macro_rules! poll_behaviour {
/* $behaviour: The sub-behaviour being polled.
* $on_event_fn: Function to call if we get an event from the sub-behaviour.
* $notify_handler_event_closure: Closure mapping the received event type to
* the one that the handler should get.
*/
($behaviour: ident, $on_event_fn: ident, $notify_handler_event_closure: expr) => {
loop {
// poll the sub-behaviour
match self.$behaviour.poll(cx, poll_params) {
Poll::Ready(action) => match action {
// call the designated function to handle the event from sub-behaviour
NBAction::GenerateEvent(event) => self.$on_event_fn(event),
NBAction::DialAddress { address } => {
return Poll::Ready(NBAction::DialAddress { address })
}
NBAction::DialPeer { peer_id, condition } => {
return Poll::Ready(NBAction::DialPeer { peer_id, condition })
}
NBAction::NotifyHandler {
peer_id,
handler,
event,
} => {
return Poll::Ready(NBAction::NotifyHandler {
peer_id,
handler,
// call the closure mapping the received event to the needed one
// in order to notify the handler
event: BehaviourHandlerIn::Delegate(
$notify_handler_event_closure(event),
),
});
}
NBAction::ReportObservedAddr { address } => {
return Poll::Ready(NBAction::ReportObservedAddr { address })
}
},
Poll::Pending => break,
}
}
};
}
poll_behaviour!(gossipsub, on_gossip_event, DelegateIn::Gossipsub);
poll_behaviour!(eth2_rpc, on_rpc_event, DelegateIn::RPC);
poll_behaviour!(identify, on_identify_event, DelegateIn::Identify);
self.custom_poll(cx)
}
}
/// Implements the combined behaviour for the libp2p service.
impl<TSpec: EthSpec> Behaviour<TSpec> {
pub fn new(
@@ -264,15 +106,27 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
),
identify,
peer_manager: PeerManager::new(local_key, net_conf, network_globals.clone(), log)?,
events: Vec::new(),
peers_to_dc: Vec::new(),
events: VecDeque::new(),
handler_events: VecDeque::new(),
peers_to_dc: VecDeque::new(),
meta_data,
network_globals,
enr_fork_id,
waker: None,
log: behaviour_log,
})
}
/// Attempts to connect to a libp2p peer.
///
/// This MUST be used over Swarm::dial() as this keeps track of the peer in the peer manager.
///
/// All external dials, dial a multiaddr. This is currently unused but kept here in case any
/// part of lighthouse needs to connect to a peer_id in the future.
pub fn _dial(&mut self, peer_id: &PeerId) {
self.peer_manager.dial_peer(peer_id);
}
/// Returns the local ENR of the node.
pub fn local_enr(&self) -> Enr {
self.network_globals.local_enr()
@@ -409,13 +263,18 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
/* Peer management functions */
/// Notify discovery that the peer has been banned.
// TODO: Remove this and integrate all disconnection/banning logic inside the peer manager.
pub fn peer_banned(&mut self, _peer_id: PeerId) {}
/// Report a peer's action.
pub fn report_peer(&mut self, peer_id: &PeerId, action: PeerAction) {
self.peer_manager.report_peer(peer_id, action)
}
/// Notify discovery that the peer has been unbanned.
// TODO: Remove this and integrate all disconnection/banning logic inside the peer manager.
pub fn peer_unbanned(&mut self, _peer_id: &PeerId) {}
/// Disconnects from a peer providing a reason.
///
/// This will send a goodbye, disconnect and then ban the peer.
/// This is fatal for a peer, and should be used in unrecoverable circumstances.
pub fn goodbye_peer(&mut self, peer_id: &PeerId, reason: GoodbyeReason) {
self.peer_manager.goodbye_peer(peer_id, reason);
}
/// Returns an iterator over all enr entries in the DHT.
pub fn enr_entries(&mut self) -> Vec<Enr> {
@@ -531,40 +390,6 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
&mut self.peer_manager
}
/* Address in the new behaviour. Connections are now maintained at the swarm level.
/// Notifies the behaviour that a peer has connected.
pub fn notify_peer_connect(&mut self, peer_id: PeerId, endpoint: ConnectedPoint) {
match endpoint {
ConnectedPoint::Dialer { .. } => self.peer_manager.connect_outgoing(&peer_id),
ConnectedPoint::Listener { .. } => self.peer_manager.connect_ingoing(&peer_id),
};
// Find ENR info about a peer if possible.
if let Some(enr) = self.discovery.enr_of_peer(&peer_id) {
let bitfield = match enr.bitfield::<TSpec>() {
Ok(v) => v,
Err(e) => {
warn!(self.log, "Peer has invalid ENR bitfield";
"peer_id" => format!("{}", peer_id),
"error" => format!("{:?}", e));
return;
}
};
// use this as a baseline, until we get the actual meta-data
let meta_data = MetaData {
seq_number: 0,
attnets: bitfield,
};
// TODO: Shift to the peer manager
self.network_globals
.peers
.write()
.add_metadata(&peer_id, meta_data);
}
}
*/
fn on_gossip_event(&mut self, event: GossipsubEvent) {
match event {
GossipsubEvent::Message(propagation_source, id, gs_msg) => {
@@ -576,7 +401,7 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
}
Ok(msg) => {
// if this message isn't a duplicate, notify the network
self.events.push(BehaviourEvent::PubsubMessage {
self.add_event(BehaviourEvent::PubsubMessage {
id,
source: propagation_source,
topics: gs_msg.topics,
@@ -586,8 +411,7 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
}
}
GossipsubEvent::Subscribed { peer_id, topic } => {
self.events
.push(BehaviourEvent::PeerSubscribed(peer_id, topic));
self.add_event(BehaviourEvent::PeerSubscribed(peer_id, topic));
}
GossipsubEvent::Unsubscribed { .. } => {}
}
@@ -596,7 +420,7 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
/// Queues the response to be sent upwards as long at it was requested outside the Behaviour.
fn propagate_response(&mut self, id: RequestId, peer_id: PeerId, response: Response<TSpec>) {
if !matches!(id, RequestId::Behaviour) {
self.events.push(BehaviourEvent::ResponseReceived {
self.add_event(BehaviourEvent::ResponseReceived {
peer_id,
id,
response,
@@ -606,7 +430,7 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
/// Convenience function to propagate a request.
fn propagate_request(&mut self, id: PeerRequestId, peer_id: PeerId, request: Request) {
self.events.push(BehaviourEvent::RequestReceived {
self.add_event(BehaviourEvent::RequestReceived {
peer_id,
id,
request,
@@ -639,8 +463,7 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
self.peer_manager.handle_rpc_error(&peer_id, proto, &error);
// inform failures of requests comming outside the behaviour
if !matches!(id, RequestId::Behaviour) {
self.events
.push(BehaviourEvent::RPCFailed { peer_id, id, error });
self.add_event(BehaviourEvent::RPCFailed { peer_id, id, error });
}
}
}
@@ -664,11 +487,18 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
// let the peer manager know this peer is in the process of disconnecting
self.peer_manager._disconnecting_peer(&peer_id);
// queue for disconnection without a goodbye message
debug!(self.log, "Received a Goodbye, queueing for disconnection";
"peer_id" => peer_id.to_string());
self.peers_to_dc.push(peer_id.clone());
// TODO: do not propagate
self.propagate_request(peer_request_id, peer_id, Request::Goodbye(reason));
debug!(
self.log, "Peer sent Goodbye";
"peer_id" => peer_id.to_string(),
"reason" => reason.to_string(),
"client" => self.network_globals.client(&peer_id).to_string(),
);
self.peers_to_dc.push_back(peer_id);
// NOTE: We currently do not inform the application that we are
// disconnecting here.
// The actual disconnection event will be relayed to the application. Ideally
// this time difference is short, but we may need to introduce a message to
// inform the application layer early.
}
/* Protocols propagated to the Network */
RPCRequest::Status(msg) => {
@@ -724,10 +554,15 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
&mut self,
cx: &mut Context,
) -> Poll<NBAction<BehaviourHandlerIn<TSpec>, BehaviourEvent<TSpec>>> {
// if there are any handler_events process them
if let Some(event) = self.handler_events.pop_front() {
return Poll::Ready(event);
}
// handle pending disconnections to perform
if !self.peers_to_dc.is_empty() {
if let Some(peer_id) = self.peers_to_dc.pop_front() {
return Poll::Ready(NBAction::NotifyHandler {
peer_id: self.peers_to_dc.remove(0),
peer_id,
handler: NotifyHandler::All,
event: BehaviourHandlerIn::Shutdown(None),
});
@@ -760,18 +595,18 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
PeerManagerEvent::MetaData(peer_id) => {
self.send_meta_data_request(peer_id);
}
PeerManagerEvent::DisconnectPeer(peer_id) => {
PeerManagerEvent::DisconnectPeer(peer_id, reason) => {
debug!(self.log, "PeerManager requested to disconnect a peer";
"peer_id" => peer_id.to_string());
// queue for disabling
self.peers_to_dc.push(peer_id.clone());
self.peers_to_dc.push_back(peer_id.clone());
// send one goodbye
return Poll::Ready(NBAction::NotifyHandler {
peer_id,
handler: NotifyHandler::Any,
event: BehaviourHandlerIn::Shutdown(Some((
RequestId::Behaviour,
RPCRequest::Goodbye(GoodbyeReason::Fault),
RPCRequest::Goodbye(reason),
))),
});
}
@@ -781,8 +616,8 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
}
}
if !self.events.is_empty() {
return Poll::Ready(NBAction::GenerateEvent(self.events.remove(0)));
if let Some(event) = self.events.pop_front() {
return Poll::Ready(NBAction::GenerateEvent(event));
}
Poll::Pending
@@ -817,21 +652,244 @@ impl<TSpec: EthSpec> Behaviour<TSpec> {
IdentifyEvent::Error { .. } => {}
}
}
/// Adds an event to the queue waking the current thread to process it.
fn add_event(&mut self, event: BehaviourEvent<TSpec>) {
self.events.push_back(event);
if let Some(waker) = &self.waker {
waker.wake_by_ref();
}
}
}
/// Calls the given function with the given args on all sub behaviours.
macro_rules! delegate_to_behaviours {
($self: ident, $fn: ident, $($arg: ident), *) => {
$self.gossipsub.$fn($($arg),*);
$self.eth2_rpc.$fn($($arg),*);
$self.identify.$fn($($arg),*);
};
}
impl<TSpec: EthSpec> NetworkBehaviour for Behaviour<TSpec> {
type ProtocolsHandler = BehaviourHandler<TSpec>;
type OutEvent = BehaviourEvent<TSpec>;
fn new_handler(&mut self) -> Self::ProtocolsHandler {
BehaviourHandler::new(&mut self.gossipsub, &mut self.eth2_rpc, &mut self.identify)
}
fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec<Multiaddr> {
self.peer_manager.addresses_of_peer(peer_id)
}
// This gets called every time a connection is closed.
fn inject_connection_closed(
&mut self,
peer_id: &PeerId,
conn_id: &ConnectionId,
endpoint: &ConnectedPoint,
) {
delegate_to_behaviours!(self, inject_connection_closed, peer_id, conn_id, endpoint);
}
// This gets called once there are no more active connections.
fn inject_disconnected(&mut self, peer_id: &PeerId) {
// Inform the peer manager.
self.peer_manager.notify_disconnect(&peer_id);
// Inform the application.
self.add_event(BehaviourEvent::PeerDisconnected(peer_id.clone()));
delegate_to_behaviours!(self, inject_disconnected, peer_id);
}
// This gets called every time a connection is established.
fn inject_connection_established(
&mut self,
peer_id: &PeerId,
conn_id: &ConnectionId,
endpoint: &ConnectedPoint,
) {
// If the peer is banned, send a goodbye and disconnect.
if self.peer_manager.is_banned(peer_id) {
self.peers_to_dc.push_back(peer_id.clone());
// send a goodbye on all possible handlers for this peer
self.handler_events.push_back(NBAction::NotifyHandler {
peer_id: peer_id.clone(),
handler: NotifyHandler::All,
event: BehaviourHandlerIn::Shutdown(Some((
RequestId::Behaviour,
RPCRequest::Goodbye(GoodbyeReason::Banned),
))),
});
return;
}
// notify the peer manager of a successful connection
match endpoint {
ConnectedPoint::Listener { .. } => {
self.peer_manager.connect_ingoing(&peer_id);
self.add_event(BehaviourEvent::PeerConnected(peer_id.clone()));
debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Incoming");
}
ConnectedPoint::Dialer { .. } => {
self.peer_manager.connect_outgoing(&peer_id);
self.add_event(BehaviourEvent::PeerDialed(peer_id.clone()));
debug!(self.log, "Connection established"; "peer_id" => peer_id.to_string(), "connection" => "Dialed");
}
}
// report the event to the application
delegate_to_behaviours!(
self,
inject_connection_established,
peer_id,
conn_id,
endpoint
);
}
// This gets called on the initial connection establishment.
fn inject_connected(&mut self, peer_id: &PeerId) {
// Drop any connection from a banned peer. The goodbye and disconnects are handled in
// `inject_connection_established()`, which gets called first.
if self.peer_manager.is_banned(peer_id) {
return;
}
delegate_to_behaviours!(self, inject_connected, peer_id);
}
fn inject_addr_reach_failure(
&mut self,
peer_id: Option<&PeerId>,
addr: &Multiaddr,
error: &dyn std::error::Error,
) {
delegate_to_behaviours!(self, inject_addr_reach_failure, peer_id, addr, error);
}
fn inject_dial_failure(&mut self, peer_id: &PeerId) {
// Could not dial the peer, inform the peer manager.
self.peer_manager.notify_dial_failure(&peer_id);
delegate_to_behaviours!(self, inject_dial_failure, peer_id);
}
fn inject_new_listen_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_new_listen_addr, addr);
}
fn inject_expired_listen_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_expired_listen_addr, addr);
}
fn inject_new_external_addr(&mut self, addr: &Multiaddr) {
delegate_to_behaviours!(self, inject_new_external_addr, addr);
}
fn inject_listener_error(&mut self, id: ListenerId, err: &(dyn std::error::Error + 'static)) {
delegate_to_behaviours!(self, inject_listener_error, id, err);
}
fn inject_listener_closed(&mut self, id: ListenerId, reason: Result<(), &std::io::Error>) {
delegate_to_behaviours!(self, inject_listener_closed, id, reason);
}
fn inject_event(
&mut self,
peer_id: PeerId,
conn_id: ConnectionId,
event: <Self::ProtocolsHandler as ProtocolsHandler>::OutEvent,
) {
match event {
// Events comming from the handler, redirected to each behaviour
BehaviourHandlerOut::Delegate(delegate) => match *delegate {
DelegateOut::Gossipsub(ev) => self.gossipsub.inject_event(peer_id, conn_id, ev),
DelegateOut::RPC(ev) => self.eth2_rpc.inject_event(peer_id, conn_id, ev),
DelegateOut::Identify(ev) => self.identify.inject_event(peer_id, conn_id, *ev),
},
/* Custom events sent BY the handler */
BehaviourHandlerOut::Custom => {
// TODO: implement
}
}
}
fn poll(
&mut self,
cx: &mut Context,
poll_params: &mut impl PollParameters,
) -> Poll<NBAction<<Self::ProtocolsHandler as ProtocolsHandler>::InEvent, Self::OutEvent>> {
// update the waker if needed
if let Some(waker) = &self.waker {
if waker.will_wake(cx.waker()) {
self.waker = Some(cx.waker().clone());
}
} else {
self.waker = Some(cx.waker().clone());
}
// TODO: move where it's less distracting
macro_rules! poll_behaviour {
/* $behaviour: The sub-behaviour being polled.
* $on_event_fn: Function to call if we get an event from the sub-behaviour.
* $notify_handler_event_closure: Closure mapping the received event type to
* the one that the handler should get.
*/
($behaviour: ident, $on_event_fn: ident, $notify_handler_event_closure: expr) => {
loop {
// poll the sub-behaviour
match self.$behaviour.poll(cx, poll_params) {
Poll::Ready(action) => match action {
// call the designated function to handle the event from sub-behaviour
NBAction::GenerateEvent(event) => self.$on_event_fn(event),
NBAction::DialAddress { address } => {
return Poll::Ready(NBAction::DialAddress { address })
}
NBAction::DialPeer { peer_id, condition } => {
return Poll::Ready(NBAction::DialPeer { peer_id, condition })
}
NBAction::NotifyHandler {
peer_id,
handler,
event,
} => {
return Poll::Ready(NBAction::NotifyHandler {
peer_id,
handler,
// call the closure mapping the received event to the needed one
// in order to notify the handler
event: BehaviourHandlerIn::Delegate(
$notify_handler_event_closure(event),
),
});
}
NBAction::ReportObservedAddr { address } => {
return Poll::Ready(NBAction::ReportObservedAddr { address })
}
},
Poll::Pending => break,
}
}
};
}
poll_behaviour!(gossipsub, on_gossip_event, DelegateIn::Gossipsub);
poll_behaviour!(eth2_rpc, on_rpc_event, DelegateIn::RPC);
poll_behaviour!(identify, on_identify_event, DelegateIn::Identify);
self.custom_poll(cx)
}
}
/* Public API types */
/// The type of RPC requests the Behaviour informs it has received and allows for sending.
///
// NOTE: This is an application-level wrapper over the lower network leve requests that can be
// sent. The main difference is the absense of the Ping and Metadata protocols, which don't
// NOTE: This is an application-level wrapper over the lower network level requests that can be
// sent. The main difference is the absence of the Ping, Metadata and Goodbye protocols, which don't
// leave the Behaviour. For all protocols managed by RPC see `RPCRequest`.
#[derive(Debug, Clone, PartialEq)]
pub enum Request {
/// A Status message.
Status(StatusMessage),
/// A Goobye message.
Goodbye(GoodbyeReason),
/// A blocks by range request.
BlocksByRange(BlocksByRangeRequest),
/// A request blocks root request.
@@ -843,7 +901,6 @@ impl<TSpec: EthSpec> std::convert::From<Request> for RPCRequest<TSpec> {
match req {
Request::BlocksByRoot(r) => RPCRequest::BlocksByRoot(r),
Request::BlocksByRange(r) => RPCRequest::BlocksByRange(r),
Request::Goodbye(r) => RPCRequest::Goodbye(r),
Request::Status(s) => RPCRequest::Status(s),
}
}
@@ -887,6 +944,12 @@ pub type PeerRequestId = (ConnectionId, SubstreamId);
/// The types of events than can be obtained from polling the behaviour.
#[derive(Debug)]
pub enum BehaviourEvent<TSpec: EthSpec> {
/// We have successfully dialed and connected to a peer.
PeerDialed(PeerId),
/// A peer has successfully dialed and connected to us.
PeerConnected(PeerId),
/// A peer has disconnected.
PeerDisconnected(PeerId),
/// An RPC Request that was sent failed.
RPCFailed {
/// The id of the failed request.