v0.11.1 Network update (#989)

* Minor log bumps

* Initial building of extended RPC methods

* Wire in extended RPC methods

* Merge initial peer management template

* Add a PeerDB and give the peer manager some basic functions

* Initial connection of peer manager

* Add peer manager to lighthouse

* Connect peer manager with new RPC methods

* Correct tests and metadata RPC

Co-authored-by: Diva <divma@protonmail.com>
This commit is contained in:
Age Manning
2020-04-08 01:08:05 +10:00
committed by GitHub
parent d7e2938296
commit b23f19272d
26 changed files with 1522 additions and 409 deletions

View File

@@ -1,5 +1,6 @@
use crate::discovery::Discovery;
use crate::rpc::{RPCEvent, RPCMessage, RPC};
use crate::discovery::{enr::Eth2Enr, Discovery};
use crate::peer_manager::{PeerManager, PeerManagerEvent};
use crate::rpc::*;
use crate::types::{GossipEncoding, GossipKind, GossipTopic};
use crate::{error, Enr, NetworkConfig, NetworkGlobals, PubsubMessage, TopicHash};
use futures::prelude::*;
@@ -14,6 +15,7 @@ use libp2p::{
};
use lru::LruCache;
use slog::{crit, debug, o, warn};
use std::marker::PhantomData;
use std::sync::Arc;
use types::{EnrForkId, EthSpec, SubnetId};
@@ -35,9 +37,15 @@ pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> {
identify: Identify<TSubstream>,
/// Discovery behaviour.
discovery: Discovery<TSubstream, TSpec>,
/// The peer manager that keeps track of peer's reputation and status.
#[behaviour(ignore)]
peer_manager: PeerManager<TSpec>,
/// The events generated by this behaviour to be consumed in the swarm poll.
#[behaviour(ignore)]
events: Vec<BehaviourEvent<TSpec>>,
/// The current meta data of the node, so respond to pings and get metadata
#[behaviour(ignore)]
meta_data: MetaData<TSpec>,
/// A cache of recently seen gossip messages. This is used to filter out any possible
/// duplicates that may still be seen over gossipsub.
#[behaviour(ignore)]
@@ -47,18 +55,20 @@ pub struct Behaviour<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> {
network_globals: Arc<NetworkGlobals<TSpec>>,
#[behaviour(ignore)]
/// Keeps track of the current EnrForkId for upgrading gossipsub topics.
// 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,
#[behaviour(ignore)]
/// Logger for behaviour actions.
log: slog::Logger,
}
/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, TSpec> {
pub fn new(
local_key: &Keypair,
net_conf: &NetworkConfig,
network_globals: Arc<NetworkGlobals<TSpec>>,
enr_fork_id: EnrForkId,
log: &slog::Logger,
) -> error::Result<Self> {
let local_peer_id = local_key.public().into_peer_id();
@@ -70,36 +80,48 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
local_key.public(),
);
let enr_fork_id = network_globals
.local_enr
.read()
.eth2()
.expect("Local ENR must have a fork id");
let attnets = network_globals
.local_enr
.read()
.bitfield::<TSpec>()
.expect("Local ENR must have subnet bitfield");
let meta_data = MetaData {
seq_number: 1,
attnets,
};
Ok(Behaviour {
eth2_rpc: RPC::new(log.clone()),
gossipsub: Gossipsub::new(local_peer_id, net_conf.gs_config.clone()),
discovery: Discovery::new(
local_key,
net_conf,
enr_fork_id.clone(),
network_globals.clone(),
log,
)?,
discovery: Discovery::new(local_key, net_conf, network_globals.clone(), log)?,
identify,
peer_manager: PeerManager::new(network_globals.clone(), log),
events: Vec::new(),
seen_gossip_messages: LruCache::new(100_000),
meta_data,
network_globals,
enr_fork_id,
log: behaviour_log,
})
}
/// Obtain a reference to the discovery protocol.
pub fn discovery(&self) -> &Discovery<TSubstream, TSpec> {
&self.discovery
}
/// Obtain a reference to the gossipsub protocol.
pub fn gs(&self) -> &Gossipsub<TSubstream> {
&self.gossipsub
}
}
/// Implements the combined behaviour for the libp2p service.
impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, TSpec> {
/* Pubsub behaviour functions */
/// Subscribes to a gossipsub topic kind, letting the network service determine the
@@ -225,6 +247,8 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
if let Err(e) = self.discovery.update_enr_bitfield(subnet_id, value) {
crit!(self.log, "Could not update ENR bitfield"; "error" => e);
}
// update the local meta data which informs our peers of the update during PINGS
self.update_metadata();
}
/// A request to search for peers connected to a long-lived subnet.
@@ -259,6 +283,45 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
// update the local reference
self.enr_fork_id = enr_fork_id;
}
/* Private internal functions */
/// Updates the current meta data of the node.
fn update_metadata(&mut self) {
self.meta_data.seq_number += 1;
self.meta_data.attnets = self
.discovery
.local_enr()
.bitfield::<TSpec>()
.expect("Local discovery must have bitfield");
}
/// Sends a PING/PONG request/response to a peer.
fn send_ping(&mut self, id: RequestId, peer_id: PeerId) {
let pong_response = RPCEvent::Response(
id,
RPCErrorResponse::Success(RPCResponse::Pong(crate::rpc::methods::Ping {
data: self.meta_data.seq_number,
})),
);
self.send_rpc(peer_id, pong_response);
}
/// Sends a METADATA request to a peer.
fn send_meta_data_request(&mut self, peer_id: PeerId) {
let metadata_request =
RPCEvent::Request(RequestId::from(0usize), RPCRequest::MetaData(PhantomData));
self.send_rpc(peer_id, metadata_request);
}
/// Sends a METADATA response to a peer.
fn send_meta_data_response(&mut self, id: RequestId, peer_id: PeerId) {
let metadata_response = RPCEvent::Response(
id,
RPCErrorResponse::Success(RPCResponse::MetaData(self.meta_data.clone())),
);
self.send_rpc(peer_id, metadata_response);
}
}
// Implement the NetworkBehaviourEventProcess trait so that we can derive NetworkBehaviour for Behaviour
@@ -277,7 +340,7 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec>
}
Ok(msg) => {
// if this message isn't a duplicate, notify the network
self.events.push(BehaviourEvent::GossipMessage {
self.events.push(BehaviourEvent::PubsubMessage {
id,
source: propagation_source,
topics: gs_msg.topics,
@@ -310,7 +373,41 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec>
self.events.push(BehaviourEvent::PeerDisconnected(peer_id))
}
RPCMessage::RPC(peer_id, rpc_event) => {
self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
// The METADATA and PING RPC responses are handled within the behaviour and not
// propagated
// TODO: Improve the RPC types to better handle this logic discrepancy
match rpc_event {
RPCEvent::Request(id, RPCRequest::Ping(ping)) => {
// inform the peer manager and send the response
self.peer_manager.ping_request(&peer_id, ping.data);
self.send_ping(id, peer_id);
}
RPCEvent::Request(id, RPCRequest::MetaData(_)) => {
// send the requested meta-data
self.send_meta_data_response(id, peer_id);
}
RPCEvent::Response(_, RPCErrorResponse::Success(RPCResponse::Pong(ping))) => {
self.peer_manager.pong_response(&peer_id, ping.data);
}
RPCEvent::Response(
_,
RPCErrorResponse::Success(RPCResponse::MetaData(meta_data)),
) => {
self.peer_manager.meta_data_response(&peer_id, meta_data);
}
RPCEvent::Request(_, RPCRequest::Status(_))
| RPCEvent::Response(_, RPCErrorResponse::Success(RPCResponse::Status(_))) => {
// inform the peer manager that we have received a status from a peer
self.peer_manager.peer_statusd(&peer_id);
// propagate the STATUS message upwards
self.events.push(BehaviourEvent::RPC(peer_id, rpc_event));
}
_ => {
// propagate all other RPC messages upwards
self.events.push(BehaviourEvent::RPC(peer_id, rpc_event))
}
}
}
}
}
@@ -321,6 +418,39 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> Behaviour<TSubstream, T
fn poll<TBehaviourIn>(
&mut self,
) -> Async<NetworkBehaviourAction<TBehaviourIn, BehaviourEvent<TSpec>>> {
// check the peer manager for events
loop {
match self.peer_manager.poll() {
Ok(Async::Ready(Some(event))) => match event {
PeerManagerEvent::Status(peer_id) => {
// it's time to status. We don't keep a beacon chain reference here, so we inform
// the network to send a status to this peer
return Async::Ready(NetworkBehaviourAction::GenerateEvent(
BehaviourEvent::StatusPeer(peer_id),
));
}
PeerManagerEvent::Ping(peer_id) => {
// send a ping to this peer
self.send_ping(RequestId::from(0usize), peer_id);
}
PeerManagerEvent::MetaData(peer_id) => {
self.send_meta_data_request(peer_id);
}
PeerManagerEvent::DisconnectPeer(_peer_id) => {
//TODO: Implement
}
PeerManagerEvent::BanPeer(_peer_id) => {
//TODO: Implement
}
},
Ok(Async::NotReady) => break,
Ok(Async::Ready(None)) | Err(_) => {
crit!(self.log, "Error polling peer manager");
break;
}
}
}
if !self.events.is_empty() {
return Async::Ready(NetworkBehaviourAction::GenerateEvent(self.events.remove(0)));
}
@@ -369,6 +499,7 @@ impl<TSubstream: AsyncRead + AsyncWrite, TSpec: EthSpec> NetworkBehaviourEventPr
}
/// The types of events than can be obtained from polling the behaviour.
#[derive(Debug)]
pub enum BehaviourEvent<TSpec: EthSpec> {
/// A received RPC event and the peer that it was received from.
RPC(PeerId, RPCEvent<TSpec>),
@@ -377,7 +508,7 @@ pub enum BehaviourEvent<TSpec: EthSpec> {
/// A peer has disconnected.
PeerDisconnected(PeerId),
/// A gossipsub message has been received.
GossipMessage {
PubsubMessage {
/// The gossipsub message id. Used when propagating blocks after validation.
id: MessageId,
/// The peer from which we received this message, not the peer that published it.
@@ -389,4 +520,6 @@ pub enum BehaviourEvent<TSpec: EthSpec> {
},
/// Subscribed to peer for given topic
PeerSubscribed(PeerId, TopicHash),
/// Inform the network to send a Status to this peer.
StatusPeer(PeerId),
}