mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-19 12:56:12 +00:00
Shift changes into message handler and simple sync for rpc-rewrite
This commit is contained in:
@@ -3,22 +3,19 @@ use crate::service::{NetworkMessage, OutgoingMessage};
|
||||
use crate::sync::SimpleSync;
|
||||
use beacon_chain::{BeaconChain, BeaconChainTypes};
|
||||
use crossbeam_channel::{unbounded as channel, Sender};
|
||||
use eth2_libp2p::rpc::methods::*;
|
||||
use eth2_libp2p::{
|
||||
behaviour::PubsubMessage,
|
||||
rpc::{methods::GoodbyeReason, RPCRequest, RPCResponse, RequestId},
|
||||
rpc::{RPCError, RPCErrorResponse, RPCRequest, RPCResponse, RequestId},
|
||||
PeerId, RPCEvent,
|
||||
};
|
||||
use futures::future;
|
||||
use slog::{debug, warn};
|
||||
use slog::{debug, error, warn};
|
||||
use ssz::Decode;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Instant;
|
||||
|
||||
/// Timeout for RPC requests.
|
||||
// const REQUEST_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
/// Timeout before banning a peer for non-identification.
|
||||
// const HELLO_TIMEOUT: Duration = Duration::from_secs(30);
|
||||
|
||||
/// Handles messages received from the network and client and organises syncing.
|
||||
pub struct MessageHandler<T: BeaconChainTypes> {
|
||||
/// Currently loaded and initialised beacon chain.
|
||||
@@ -32,7 +29,7 @@ pub struct MessageHandler<T: BeaconChainTypes> {
|
||||
}
|
||||
|
||||
/// Types of messages the handler can receive.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum HandlerMessage {
|
||||
/// We have initiated a connection to a new peer.
|
||||
PeerDialed(PeerId),
|
||||
@@ -87,6 +84,10 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
HandlerMessage::PeerDialed(peer_id) => {
|
||||
self.sync.on_connect(peer_id, &mut self.network_context);
|
||||
}
|
||||
// A peer has disconnected
|
||||
HandlerMessage::PeerDisconnected(peer_id) => {
|
||||
self.sync.on_disconnect(peer_id);
|
||||
}
|
||||
// we have received an RPC message request/response
|
||||
HandlerMessage::RPC(peer_id, rpc_event) => {
|
||||
self.handle_rpc_message(peer_id, rpc_event);
|
||||
@@ -105,9 +106,9 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
/// Handle RPC messages
|
||||
fn handle_rpc_message(&mut self, peer_id: PeerId, rpc_message: RPCEvent) {
|
||||
match rpc_message {
|
||||
RPCEvent::Request { id, body, .. // TODO: Clean up RPC Message types, have a cleaner type by this point.
|
||||
} => self.handle_rpc_request(peer_id, id, body),
|
||||
RPCEvent::Response { id, result, .. } => self.handle_rpc_response(peer_id, id, result),
|
||||
RPCEvent::Request(id, req) => self.handle_rpc_request(peer_id, id, req),
|
||||
RPCEvent::Response(id, resp) => self.handle_rpc_response(peer_id, id, resp),
|
||||
RPCEvent::Error(id, error) => self.handle_rpc_error(peer_id, id, error),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,58 +151,137 @@ impl<T: BeaconChainTypes + 'static> MessageHandler<T> {
|
||||
|
||||
/// An RPC response has been received from the network.
|
||||
// we match on id and ignore responses past the timeout.
|
||||
fn handle_rpc_response(&mut self, peer_id: PeerId, id: RequestId, response: RPCResponse) {
|
||||
// if response id is not related to a request, ignore (likely RPC timeout)
|
||||
fn handle_rpc_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
id: RequestId,
|
||||
error_response: RPCErrorResponse,
|
||||
) {
|
||||
//TODO: Potentially do not need to keep track of this at all. This has all been shifted
|
||||
//into libp2p stack. Tracking Id's will only be necessary if a response is important
|
||||
//relative to a specific request. Note: BeaconBlockBodies already returns with the data
|
||||
//associated with its request.
|
||||
// Currently leave this here for testing, to ensure it is redundant.
|
||||
if self
|
||||
.network_context
|
||||
.outstanding_outgoing_request_ids
|
||||
.remove(&(peer_id.clone(), id))
|
||||
.is_none()
|
||||
{
|
||||
warn!(
|
||||
// This should never happen. The RPC layer handles all timeouts and ensures a response
|
||||
// matches a request.
|
||||
debug_assert!(false);
|
||||
|
||||
error!(
|
||||
self.log,
|
||||
"Unknown ResponseId for incoming RPCRequest";
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
"request_id" => format!("{:?}", id)
|
||||
"request_id" => format!("{}", id)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
match response {
|
||||
RPCResponse::Hello(hello_message) => {
|
||||
self.sync
|
||||
.on_hello_response(peer_id, hello_message, &mut self.network_context);
|
||||
// an error could have occurred.
|
||||
// TODO: Handle Error gracefully
|
||||
match error_response {
|
||||
RPCErrorResponse::EncodingError => {
|
||||
warn!(self.log, "Encoding Error"; "peer" => format!("{:?}", peer_id), "request_id" => format!("{}",id))
|
||||
}
|
||||
RPCResponse::BeaconBlockRoots(response) => {
|
||||
self.sync.on_beacon_block_roots_response(
|
||||
peer_id,
|
||||
response,
|
||||
&mut self.network_context,
|
||||
);
|
||||
RPCErrorResponse::InvalidRequest(error) => {
|
||||
warn!(self.log, "";"peer" => format!("{:?}", peer_id), "Invalid Request" => error.as_string())
|
||||
}
|
||||
RPCResponse::BeaconBlockHeaders(response) => {
|
||||
self.sync.on_beacon_block_headers_response(
|
||||
peer_id,
|
||||
response,
|
||||
&mut self.network_context,
|
||||
);
|
||||
RPCErrorResponse::ServerError(error) => {
|
||||
warn!(self.log, "";"peer" => format!("{:?}", peer_id), "Server Error" => error.as_string())
|
||||
}
|
||||
RPCResponse::BeaconBlockBodies(response) => {
|
||||
self.sync.on_beacon_block_bodies_response(
|
||||
peer_id,
|
||||
response,
|
||||
&mut self.network_context,
|
||||
);
|
||||
RPCErrorResponse::Unknown(error) => {
|
||||
warn!(self.log, "";"peer" => format!("{:?}", peer_id), "Unknown Error" => error.as_string())
|
||||
}
|
||||
RPCResponse::BeaconChainState(_) => {
|
||||
// We do not implement this endpoint, it is not required and will only likely be
|
||||
// useful for light-client support in later phases.
|
||||
//
|
||||
// Theoretically, we shouldn't reach this code because we should never send a
|
||||
// beacon state RPC request.
|
||||
warn!(self.log, "BeaconChainState RPC call is not supported.");
|
||||
RPCErrorResponse::Success(response) => {
|
||||
match response {
|
||||
RPCResponse::Hello(hello_message) => {
|
||||
self.sync.on_hello_response(
|
||||
peer_id,
|
||||
hello_message,
|
||||
&mut self.network_context,
|
||||
);
|
||||
}
|
||||
RPCResponse::BeaconBlockRoots(response) => {
|
||||
self.sync.on_beacon_block_roots_response(
|
||||
peer_id,
|
||||
response,
|
||||
&mut self.network_context,
|
||||
);
|
||||
}
|
||||
RPCResponse::BeaconBlockHeaders(response) => {
|
||||
if let Some(decoded_block_headers) = self.decode_block_headers(response) {
|
||||
self.sync.on_beacon_block_headers_response(
|
||||
peer_id,
|
||||
decoded_block_headers,
|
||||
&mut self.network_context,
|
||||
);
|
||||
} else {
|
||||
warn!(self.log, "Peer sent invalid block headers";"peer" => format!("{:?}", peer_id))
|
||||
}
|
||||
}
|
||||
RPCResponse::BeaconBlockBodies(response) => {
|
||||
if let Some(decoded_block_bodies) = self.decode_block_bodies(response) {
|
||||
self.sync.on_beacon_block_bodies_response(
|
||||
peer_id,
|
||||
decoded_block_bodies,
|
||||
&mut self.network_context,
|
||||
);
|
||||
} else {
|
||||
warn!(self.log, "Peer sent invalid block bodies";"peer" => format!("{:?}", peer_id))
|
||||
}
|
||||
}
|
||||
RPCResponse::BeaconChainState(_) => {
|
||||
// We do not implement this endpoint, it is not required and will only likely be
|
||||
// useful for light-client support in later phases.
|
||||
//
|
||||
// Theoretically, we shouldn't reach this code because we should never send a
|
||||
// beacon state RPC request.
|
||||
warn!(self.log, "BeaconChainState RPC call is not supported.");
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies and decodes the ssz-encoded block bodies received from peers.
|
||||
fn decode_block_bodies(
|
||||
&self,
|
||||
bodies_response: BeaconBlockBodiesResponse,
|
||||
) -> Option<DecodedBeaconBlockBodiesResponse> {
|
||||
//TODO: Implement faster block verification before decoding entirely
|
||||
let simple_decoded_bodies =
|
||||
EncodeableBeaconBlockBodiesResponse::from_ssz_bytes(&bodies_response.block_bodies);
|
||||
|
||||
//TODO: Potentially improve the types used here for SSZ encoding/decoding
|
||||
if let Ok(simple_decoded_bodies) = simple_decoded_bodies {
|
||||
Some(DecodedBeaconBlockBodiesResponse {
|
||||
block_roots: bodies_response
|
||||
.block_roots
|
||||
.expect("Responses must have associated roots"),
|
||||
block_bodies: simple_decoded_bodies.block_bodies,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies and decodes the ssz-encoded block headers received from peers.
|
||||
fn decode_block_headers(
|
||||
&self,
|
||||
headers_response: BeaconBlockHeadersResponse,
|
||||
) -> Option<EncodeableBeaconBlockHeadersResponse> {
|
||||
//TODO: Implement faster header verification before decoding entirely
|
||||
EncodeableBeaconBlockHeadersResponse::from_ssz_bytes(&headers_response.headers).ok()
|
||||
}
|
||||
|
||||
/// Handle various RPC errors
|
||||
fn handle_rpc_error(&mut self, peer_id: PeerId, request_id: RequestId, error: RPCError) {
|
||||
//TODO: Handle error correctly
|
||||
warn!(self.log, "RPC Error"; "Peer" => format!("{:?}", peer_id), "Request Id" => format!("{}", request_id), "Error" => format!("{:?}", error));
|
||||
}
|
||||
|
||||
/// Handle RPC messages
|
||||
@@ -252,16 +332,10 @@ impl NetworkContext {
|
||||
self.outstanding_outgoing_request_ids
|
||||
.insert((peer_id.clone(), id), Instant::now());
|
||||
|
||||
self.send_rpc_event(
|
||||
peer_id,
|
||||
RPCEvent::Request {
|
||||
id,
|
||||
method_id: rpc_request.method_id(),
|
||||
body: rpc_request,
|
||||
},
|
||||
);
|
||||
self.send_rpc_event(peer_id, RPCEvent::Request(id, rpc_request));
|
||||
}
|
||||
|
||||
//TODO: Handle Error responses
|
||||
pub fn send_rpc_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
@@ -270,11 +344,7 @@ impl NetworkContext {
|
||||
) {
|
||||
self.send_rpc_event(
|
||||
peer_id,
|
||||
RPCEvent::Response {
|
||||
id: request_id,
|
||||
method_id: rpc_response.method_id(),
|
||||
result: rpc_response,
|
||||
},
|
||||
RPCEvent::Response(request_id, RPCErrorResponse::Success(rpc_response)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -291,7 +361,6 @@ impl NetworkContext {
|
||||
"Could not send RPC message to the network service"
|
||||
)
|
||||
});
|
||||
//
|
||||
}
|
||||
|
||||
/// Returns the next `RequestId` for sending an `RPCRequest` to the `peer_id`.
|
||||
@@ -299,9 +368,9 @@ impl NetworkContext {
|
||||
let next_id = self
|
||||
.outgoing_request_ids
|
||||
.entry(peer_id.clone())
|
||||
.and_modify(RequestId::increment)
|
||||
.or_insert_with(|| RequestId::from(1));
|
||||
.and_modify(|id| *id += 1)
|
||||
.or_insert_with(|| 0);
|
||||
|
||||
next_id.previous()
|
||||
*next_id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,13 +118,19 @@ fn network_service(
|
||||
trace!(log, "RPC Event: RPC message received: {:?}", rpc_event);
|
||||
message_handler_send
|
||||
.send(HandlerMessage::RPC(peer_id, rpc_event))
|
||||
.map_err(|_| "failed to send rpc to handler")?;
|
||||
.map_err(|_| "Failed to send rpc to handler")?;
|
||||
}
|
||||
Libp2pEvent::PeerDialed(peer_id) => {
|
||||
debug!(log, "Peer Dialed: {:?}", peer_id);
|
||||
message_handler_send
|
||||
.send(HandlerMessage::PeerDialed(peer_id))
|
||||
.map_err(|_| "failed to send rpc to handler")?;
|
||||
.map_err(|_| "Failed to send PeerDialed to handler")?;
|
||||
}
|
||||
Libp2pEvent::PeerDisconnected(peer_id) => {
|
||||
debug!(log, "Peer Disconnected: {:?}", peer_id);
|
||||
message_handler_send
|
||||
.send(HandlerMessage::PeerDisconnected(peer_id))
|
||||
.map_err(|_| "Failed to send PeerDisconnected to handler")?;
|
||||
}
|
||||
Libp2pEvent::PubsubMessage {
|
||||
source, message, ..
|
||||
@@ -176,7 +182,7 @@ fn network_service(
|
||||
}
|
||||
|
||||
/// Types of messages that the network service can receive.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum NetworkMessage {
|
||||
/// Send a message to libp2p service.
|
||||
//TODO: Define typing for messages across the wire
|
||||
@@ -189,7 +195,7 @@ pub enum NetworkMessage {
|
||||
}
|
||||
|
||||
/// Type of outgoing messages that can be sent through the network service.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub enum OutgoingMessage {
|
||||
/// Send an RPC request/response.
|
||||
RPC(RPCEvent),
|
||||
|
||||
@@ -5,6 +5,7 @@ use eth2_libp2p::rpc::methods::*;
|
||||
use eth2_libp2p::rpc::{RPCRequest, RPCResponse, RequestId};
|
||||
use eth2_libp2p::PeerId;
|
||||
use slog::{debug, error, info, o, trace, warn};
|
||||
use ssz::Encode;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
@@ -30,6 +31,7 @@ const SHOULD_NOT_FORWARD_GOSSIP_BLOCK: bool = false;
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct PeerSyncInfo {
|
||||
network_id: u8,
|
||||
chain_id: u64,
|
||||
latest_finalized_root: Hash256,
|
||||
latest_finalized_epoch: Epoch,
|
||||
best_root: Hash256,
|
||||
@@ -40,6 +42,7 @@ impl From<HelloMessage> for PeerSyncInfo {
|
||||
fn from(hello: HelloMessage) -> PeerSyncInfo {
|
||||
PeerSyncInfo {
|
||||
network_id: hello.network_id,
|
||||
chain_id: hello.chain_id,
|
||||
latest_finalized_root: hello.latest_finalized_root,
|
||||
latest_finalized_epoch: hello.latest_finalized_epoch,
|
||||
best_root: hello.best_root,
|
||||
@@ -107,6 +110,17 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
self.known_peers.remove(&peer_id);
|
||||
}
|
||||
|
||||
/// Handle a peer disconnect.
|
||||
///
|
||||
/// Removes the peer from `known_peers`.
|
||||
pub fn on_disconnect(&mut self, peer_id: PeerId) {
|
||||
info!(
|
||||
self.log, "Peer Disconnected";
|
||||
"peer" => format!("{:?}", peer_id),
|
||||
);
|
||||
self.known_peers.remove(&peer_id);
|
||||
}
|
||||
|
||||
/// Handle the connection of a new peer.
|
||||
///
|
||||
/// Sends a `Hello` message to the peer.
|
||||
@@ -200,7 +214,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
// If we have equal or better finalized epochs and best slots, we require nothing else from
|
||||
// this peer.
|
||||
//
|
||||
// We make an exception when our best slot is 0. Best slot does not indicate wether or
|
||||
// We make an exception when our best slot is 0. Best slot does not indicate whether or
|
||||
// not there is a block at slot zero.
|
||||
if (remote.latest_finalized_epoch <= local.latest_finalized_epoch)
|
||||
&& (remote.best_slot <= local.best_slot)
|
||||
@@ -398,6 +412,13 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
})
|
||||
.collect();
|
||||
|
||||
// ssz-encode the headers
|
||||
//TODO: Make this more elegant
|
||||
let headers = {
|
||||
let resp = EncodeableBeaconBlockHeadersResponse { headers };
|
||||
resp.as_ssz_bytes()
|
||||
};
|
||||
|
||||
network.send_rpc_response(
|
||||
peer_id,
|
||||
request_id,
|
||||
@@ -409,7 +430,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
pub fn on_beacon_block_headers_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
res: BeaconBlockHeadersResponse,
|
||||
res: EncodeableBeaconBlockHeadersResponse,
|
||||
network: &mut NetworkContext,
|
||||
) {
|
||||
debug!(
|
||||
@@ -471,10 +492,19 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
"returned" => block_bodies.len(),
|
||||
);
|
||||
|
||||
//TODO: Elegant ssz encoding. Either here or in the message handler
|
||||
let bytes = {
|
||||
let resp = EncodeableBeaconBlockBodiesResponse { block_bodies };
|
||||
resp.as_ssz_bytes()
|
||||
};
|
||||
|
||||
network.send_rpc_response(
|
||||
peer_id,
|
||||
request_id,
|
||||
RPCResponse::BeaconBlockBodies(BeaconBlockBodiesResponse { block_bodies }),
|
||||
RPCResponse::BeaconBlockBodies(BeaconBlockBodiesResponse {
|
||||
block_bodies: bytes,
|
||||
block_roots: None,
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -482,7 +512,7 @@ impl<T: BeaconChainTypes> SimpleSync<T> {
|
||||
pub fn on_beacon_block_bodies_response(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
res: BeaconBlockBodiesResponse,
|
||||
res: DecodedBeaconBlockBodiesResponse,
|
||||
network: &mut NetworkContext,
|
||||
) {
|
||||
debug!(
|
||||
@@ -802,7 +832,9 @@ fn hello_message<T: BeaconChainTypes>(beacon_chain: &BeaconChain<T>) -> HelloMes
|
||||
let state = &beacon_chain.head().beacon_state;
|
||||
|
||||
HelloMessage {
|
||||
//TODO: Correctly define the chain/network id
|
||||
network_id: spec.chain_id,
|
||||
chain_id: spec.chain_id as u64,
|
||||
latest_finalized_root: state.finalized_root,
|
||||
latest_finalized_epoch: state.finalized_epoch,
|
||||
best_root: beacon_chain.head().beacon_block_root,
|
||||
|
||||
Reference in New Issue
Block a user