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

@@ -2,7 +2,8 @@ use crate::rpc::methods::*;
use crate::rpc::{
codec::base::OutboundCodec,
protocol::{
ProtocolId, RPCError, RPC_BLOCKS_BY_RANGE, RPC_BLOCKS_BY_ROOT, RPC_GOODBYE, RPC_STATUS,
ProtocolId, RPCError, RPC_BLOCKS_BY_RANGE, RPC_BLOCKS_BY_ROOT, RPC_GOODBYE, RPC_META_DATA,
RPC_PING, RPC_STATUS,
},
};
use crate::rpc::{ErrorMessage, RPCErrorResponse, RPCRequest, RPCResponse};
@@ -48,6 +49,8 @@ impl<TSpec: EthSpec> Encoder for SSZInboundCodec<TSpec> {
RPCResponse::Status(res) => res.as_ssz_bytes(),
RPCResponse::BlocksByRange(res) => res.as_ssz_bytes(),
RPCResponse::BlocksByRoot(res) => res.as_ssz_bytes(),
RPCResponse::Pong(res) => res.data.as_ssz_bytes(),
RPCResponse::MetaData(res) => res.as_ssz_bytes(),
},
RPCErrorResponse::InvalidRequest(err) => err.as_ssz_bytes(),
RPCErrorResponse::ServerError(err) => err.as_ssz_bytes(),
@@ -103,6 +106,24 @@ impl<TSpec: EthSpec> Decoder for SSZInboundCodec<TSpec> {
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCRequest::Status(StatusMessage::from_ssz_bytes(
&packet,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => {
if packet.len() > 0 {
Err(RPCError::Custom(
"Get metadata request should be empty".into(),
))
} else {
Ok(Some(RPCRequest::MetaData(PhantomData)))
}
}
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
},
Ok(None) => Ok(None),
@@ -146,7 +167,8 @@ impl<TSpec: EthSpec> Encoder for SSZOutboundCodec<TSpec> {
RPCRequest::Goodbye(req) => req.as_ssz_bytes(),
RPCRequest::BlocksByRange(req) => req.as_ssz_bytes(),
RPCRequest::BlocksByRoot(req) => req.block_roots.as_ssz_bytes(),
RPCRequest::Phantom(_) => unreachable!("Never encode phantom data"),
RPCRequest::Ping(req) => req.as_ssz_bytes(),
RPCRequest::MetaData(_) => return Ok(()), // no metadata to encode
};
// length-prefix
self.inner
@@ -189,6 +211,18 @@ impl<TSpec: EthSpec> Decoder for SSZOutboundCodec<TSpec> {
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
"PING stream terminated unexpectedly".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => Err(RPCError::Custom(
"Metadata stream terminated unexpectedly".into(),
)), // cannot have an empty block message.
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
} else {
@@ -219,6 +253,18 @@ impl<TSpec: EthSpec> Decoder for SSZOutboundCodec<TSpec> {
)))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_PING => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::Pong(Ping {
data: u64::from_ssz_bytes(&raw_bytes)?,
}))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
RPC_META_DATA => match self.protocol.version.as_str() {
"1" => Ok(Some(RPCResponse::MetaData(MetaData::from_ssz_bytes(
&raw_bytes,
)?))),
_ => unreachable!("Cannot negotiate an unknown version"),
},
_ => unreachable!("Cannot negotiate an unknown protocol"),
}
}

View File

@@ -1,5 +1,6 @@
//! Available RPC methods types and ids.
use crate::types::EnrBitfield;
use ssz_derive::{Decode, Encode};
use types::{Epoch, EthSpec, Hash256, SignedBeaconBlock, Slot};
@@ -28,6 +29,22 @@ pub struct StatusMessage {
pub head_slot: Slot,
}
/// The PING request/response message.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct Ping {
/// The metadata sequence number.
pub data: u64,
}
/// The METADATA response structure.
#[derive(Encode, Decode, Clone, Debug, PartialEq)]
pub struct MetaData<T: EthSpec> {
/// A sequential counter indicating when data gets modified.
pub seq_number: u64,
/// The persistent subnet bitfield.
pub attnets: EnrBitfield<T>,
}
/// The reason given for a `Goodbye` message.
///
/// Note: any unknown `u64::into(n)` will resolve to `Goodbye::Unknown` for any unknown `n`,
@@ -136,6 +153,12 @@ pub enum RPCResponse<T: EthSpec> {
/// A response to a get BLOCKS_BY_ROOT request.
BlocksByRoot(Box<SignedBeaconBlock<T>>),
/// A PONG response to a PING request.
Pong(Ping),
/// A response to a META_DATA request.
MetaData(MetaData<T>),
}
/// Indicates which response is being terminated by a stream termination response.
@@ -202,6 +225,8 @@ impl<T: EthSpec> RPCErrorResponse<T> {
RPCResponse::Status(_) => false,
RPCResponse::BlocksByRange(_) => true,
RPCResponse::BlocksByRoot(_) => true,
RPCResponse::Pong(_) => false,
RPCResponse::MetaData(_) => false,
},
RPCErrorResponse::InvalidRequest(_) => true,
RPCErrorResponse::ServerError(_) => true,
@@ -249,6 +274,8 @@ impl<T: EthSpec> std::fmt::Display for RPCResponse<T> {
RPCResponse::BlocksByRoot(block) => {
write!(f, "BlocksByRoot: BLock slot: {}", block.message.slot)
}
RPCResponse::Pong(ping) => write!(f, "Pong: {}", ping.data),
RPCResponse::MetaData(metadata) => write!(f, "Metadata: {}", metadata.seq_number),
}
}
}

View File

@@ -13,10 +13,11 @@ use libp2p::swarm::{
};
use libp2p::{Multiaddr, PeerId};
pub use methods::{
ErrorMessage, RPCErrorResponse, RPCResponse, RequestId, ResponseTermination, StatusMessage,
ErrorMessage, MetaData, RPCErrorResponse, RPCResponse, RequestId, ResponseTermination,
StatusMessage,
};
pub use protocol::{RPCError, RPCProtocol, RPCRequest};
use slog::o;
use slog::{debug, o};
use std::marker::PhantomData;
use std::time::Duration;
use tokio::io::{AsyncRead, AsyncWrite};
@@ -120,9 +121,18 @@ where
// if initialised the connection, report this upwards to send the HELLO request
if let ConnectedPoint::Dialer { .. } = connected_point {
self.events.push(NetworkBehaviourAction::GenerateEvent(
RPCMessage::PeerDialed(peer_id),
RPCMessage::PeerDialed(peer_id.clone()),
));
}
// find the peer's meta-data
debug!(self.log, "Requesting new peer's metadata"; "peer_id" => format!("{}",peer_id));
let rpc_event =
RPCEvent::Request(RequestId::from(0usize), RPCRequest::MetaData(PhantomData));
self.events.push(NetworkBehaviourAction::SendEvent {
peer_id,
event: rpc_event,
});
}
fn inject_disconnected(&mut self, peer_id: &PeerId, _: ConnectedPoint) {

View File

@@ -9,24 +9,21 @@ use crate::rpc::{
},
methods::ResponseTermination,
};
use futures::{
future::{self, FutureResult},
sink, stream, Sink, Stream,
};
use futures::future::*;
use futures::{future, sink, stream, Sink, Stream};
use libp2p::core::{upgrade, InboundUpgrade, OutboundUpgrade, ProtocolName, UpgradeInfo};
use std::io;
use std::marker::PhantomData;
use std::time::Duration;
use tokio::codec::Framed;
use tokio::io::{AsyncRead, AsyncWrite};
use tokio::prelude::*;
use tokio::timer::timeout;
use tokio::util::FutureExt;
use tokio_io_timeout::TimeoutStream;
use types::EthSpec;
/// The maximum bytes that can be sent across the RPC.
const MAX_RPC_SIZE: usize = 4_194_304; // 4M
const MAX_RPC_SIZE: usize = 1_048_576; // 1M
/// The protocol prefix the RPC protocol id.
const PROTOCOL_PREFIX: &str = "/eth2/beacon_chain/req";
/// Time allowed for the first byte of a request to arrive before we time out (Time To First Byte).
@@ -44,6 +41,10 @@ pub const RPC_GOODBYE: &str = "goodbye";
pub const RPC_BLOCKS_BY_RANGE: &str = "beacon_blocks_by_range";
/// The `BlocksByRoot` protocol name.
pub const RPC_BLOCKS_BY_ROOT: &str = "beacon_blocks_by_root";
/// The `Ping` protocol name.
pub const RPC_PING: &str = "ping";
/// The `MetaData` protocol name.
pub const RPC_META_DATA: &str = "metadata";
#[derive(Debug, Clone)]
pub struct RPCProtocol<TSpec: EthSpec> {
@@ -54,18 +55,21 @@ impl<TSpec: EthSpec> UpgradeInfo for RPCProtocol<TSpec> {
type Info = ProtocolId;
type InfoIter = Vec<Self::Info>;
/// The list of supported RPC protocols for Lighthouse.
fn protocol_info(&self) -> Self::InfoIter {
vec![
ProtocolId::new(RPC_STATUS, "1", "ssz"),
ProtocolId::new(RPC_GOODBYE, "1", "ssz"),
ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz"),
ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz"),
ProtocolId::new(RPC_PING, "1", "ssz"),
ProtocolId::new(RPC_META_DATA, "1", "ssz"),
]
}
}
/// Tracks the types in a protocol id.
#[derive(Clone)]
#[derive(Clone, Debug)]
pub struct ProtocolId {
/// The rpc message type/name.
pub message_name: String,
@@ -125,13 +129,16 @@ where
type Output = InboundOutput<TSocket, TSpec>;
type Error = RPCError;
type Future = future::AndThen<
future::MapErr<
timeout::Timeout<stream::StreamFuture<InboundFramed<TSocket, TSpec>>>,
FnMapErr<TSocket, TSpec>,
>,
type Future = future::Either<
FutureResult<InboundOutput<TSocket, TSpec>, RPCError>,
FnAndThen<TSocket, TSpec>,
future::AndThen<
future::MapErr<
timeout::Timeout<stream::StreamFuture<InboundFramed<TSocket, TSpec>>>,
FnMapErr<TSocket, TSpec>,
>,
FutureResult<InboundOutput<TSocket, TSpec>, RPCError>,
FnAndThen<TSocket, TSpec>,
>,
>;
fn upgrade_inbound(
@@ -141,22 +148,36 @@ where
) -> Self::Future {
match protocol.encoding.as_str() {
"ssz" | _ => {
let protocol_name = protocol.message_name.clone();
let ssz_codec = BaseInboundCodec::new(SSZInboundCodec::new(protocol, MAX_RPC_SIZE));
let codec = InboundCodec::SSZ(ssz_codec);
let mut timed_socket = TimeoutStream::new(socket);
timed_socket.set_read_timeout(Some(Duration::from_secs(TTFB_TIMEOUT)));
Framed::new(timed_socket, codec)
.into_future()
.timeout(Duration::from_secs(REQUEST_TIMEOUT))
.map_err(RPCError::from as FnMapErr<TSocket, TSpec>)
.and_then({
|(req, stream)| match req {
Some(req) => futures::future::ok((req, stream)),
None => futures::future::err(RPCError::Custom(
"Stream terminated early".into(),
)),
}
} as FnAndThen<TSocket, TSpec>)
let socket = Framed::new(timed_socket, codec);
// MetaData requests should be empty, return the stream
if protocol_name == RPC_META_DATA {
futures::future::Either::A(futures::future::ok((
RPCRequest::MetaData(PhantomData),
socket,
)))
} else {
futures::future::Either::B(
socket
.into_future()
.timeout(Duration::from_secs(REQUEST_TIMEOUT))
.map_err(RPCError::from as FnMapErr<TSocket, TSpec>)
.and_then({
|(req, stream)| match req {
Some(request) => futures::future::ok((request, stream)),
None => futures::future::err(RPCError::Custom(
"Stream terminated early".into(),
)),
}
} as FnAndThen<TSocket, TSpec>),
)
}
}
}
}
@@ -173,7 +194,8 @@ pub enum RPCRequest<TSpec: EthSpec> {
Goodbye(GoodbyeReason),
BlocksByRange(BlocksByRangeRequest),
BlocksByRoot(BlocksByRootRequest),
Phantom(PhantomData<TSpec>),
Ping(Ping),
MetaData(PhantomData<TSpec>),
}
impl<TSpec: EthSpec> UpgradeInfo for RPCRequest<TSpec> {
@@ -195,7 +217,8 @@ impl<TSpec: EthSpec> RPCRequest<TSpec> {
RPCRequest::Goodbye(_) => vec![ProtocolId::new(RPC_GOODBYE, "1", "ssz")],
RPCRequest::BlocksByRange(_) => vec![ProtocolId::new(RPC_BLOCKS_BY_RANGE, "1", "ssz")],
RPCRequest::BlocksByRoot(_) => vec![ProtocolId::new(RPC_BLOCKS_BY_ROOT, "1", "ssz")],
RPCRequest::Phantom(_) => Vec::new(),
RPCRequest::Ping(_) => vec![ProtocolId::new(RPC_PING, "1", "ssz")],
RPCRequest::MetaData(_) => vec![ProtocolId::new(RPC_META_DATA, "1", "ssz")],
}
}
@@ -209,7 +232,8 @@ impl<TSpec: EthSpec> RPCRequest<TSpec> {
RPCRequest::Goodbye(_) => false,
RPCRequest::BlocksByRange(_) => true,
RPCRequest::BlocksByRoot(_) => true,
RPCRequest::Phantom(_) => unreachable!("Phantom should never be initialised"),
RPCRequest::Ping(_) => true,
RPCRequest::MetaData(_) => true,
}
}
@@ -221,7 +245,8 @@ impl<TSpec: EthSpec> RPCRequest<TSpec> {
RPCRequest::Goodbye(_) => false,
RPCRequest::BlocksByRange(_) => true,
RPCRequest::BlocksByRoot(_) => true,
RPCRequest::Phantom(_) => unreachable!("Phantom should never be initialised"),
RPCRequest::Ping(_) => false,
RPCRequest::MetaData(_) => false,
}
}
@@ -235,7 +260,8 @@ impl<TSpec: EthSpec> RPCRequest<TSpec> {
RPCRequest::BlocksByRoot(_) => ResponseTermination::BlocksByRoot,
RPCRequest::Status(_) => unreachable!(),
RPCRequest::Goodbye(_) => unreachable!(),
RPCRequest::Phantom(_) => unreachable!("Phantom should never be initialised"),
RPCRequest::Ping(_) => unreachable!(),
RPCRequest::MetaData(_) => unreachable!(),
}
}
}
@@ -361,7 +387,8 @@ impl<TSpec: EthSpec> std::fmt::Display for RPCRequest<TSpec> {
RPCRequest::Goodbye(reason) => write!(f, "Goodbye: {}", reason),
RPCRequest::BlocksByRange(req) => write!(f, "Blocks by range: {}", req),
RPCRequest::BlocksByRoot(req) => write!(f, "Blocks by root: {:?}", req),
RPCRequest::Phantom(_) => unreachable!("Phantom should never be initialised"),
RPCRequest::Ping(ping) => write!(f, "Ping: {}", ping.data),
RPCRequest::MetaData(_) => write!(f, "MetaData request"),
}
}
}