//! The Ethereum 2.0 Wire Protocol //! //! This protocol is a purpose built Ethereum 2.0 libp2p protocol. It's role is to facilitate //! direct peer-to-peer communication primarily for sending/receiving chain information for //! syncing. use futures::prelude::*; use handler::RPCHandler; use libp2p::core::ConnectedPoint; use libp2p::swarm::{ protocols_handler::ProtocolsHandler, NetworkBehaviour, NetworkBehaviourAction, PollParameters, SubstreamProtocol, }; use libp2p::{Multiaddr, PeerId}; pub use methods::{ ErrorMessage, RPCErrorResponse, RPCResponse, RequestId, ResponseTermination, StatusMessage, }; pub use protocol::{RPCError, RPCProtocol, RPCRequest}; use slog::o; use std::marker::PhantomData; use std::time::Duration; use tokio::io::{AsyncRead, AsyncWrite}; pub(crate) mod codec; mod handler; pub mod methods; mod protocol; /// The return type used in the behaviour and the resultant event from the protocols handler. #[derive(Debug)] pub enum RPCEvent { /// An inbound/outbound request for RPC protocol. The first parameter is a sequential /// id which tracks an awaiting substream for the response. Request(RequestId, RPCRequest), /// A response that is being sent or has been received from the RPC protocol. The first parameter returns /// that which was sent with the corresponding request, the second is a single chunk of a /// response. Response(RequestId, RPCErrorResponse), /// An Error occurred. Error(RequestId, RPCError), } impl RPCEvent { pub fn id(&self) -> usize { match *self { RPCEvent::Request(id, _) => id, RPCEvent::Response(id, _) => id, RPCEvent::Error(id, _) => id, } } } impl std::fmt::Display for RPCEvent { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { RPCEvent::Request(id, req) => write!(f, "RPC Request(id: {}, {})", id, req), RPCEvent::Response(id, res) => write!(f, "RPC Response(id: {}, {})", id, res), RPCEvent::Error(id, err) => write!(f, "RPC Request(id: {}, error: {:?})", id, err), } } } /// Implements the libp2p `NetworkBehaviour` trait and therefore manages network-level /// logic. pub struct RPC { /// Queue of events to processed. events: Vec>, /// Pins the generic substream. marker: PhantomData<(TSubstream)>, /// Slog logger for RPC behaviour. log: slog::Logger, } impl RPC { pub fn new(log: slog::Logger) -> Self { let log = log.new(o!("service" => "libp2p_rpc")); RPC { events: Vec::new(), marker: PhantomData, log: log, } } /// Submits an RPC request. /// /// The peer must be connected for this to succeed. pub fn send_rpc(&mut self, peer_id: PeerId, rpc_event: RPCEvent) { self.events.push(NetworkBehaviourAction::SendEvent { peer_id, event: rpc_event, }); } } impl NetworkBehaviour for RPC where TSubstream: AsyncRead + AsyncWrite, { type ProtocolsHandler = RPCHandler; type OutEvent = RPCMessage; fn new_handler(&mut self) -> Self::ProtocolsHandler { RPCHandler::new( SubstreamProtocol::new(RPCProtocol), Duration::from_secs(30), &self.log, ) } // handled by discovery fn addresses_of_peer(&mut self, _peer_id: &PeerId) -> Vec { Vec::new() } fn inject_connected(&mut self, peer_id: PeerId, connected_point: ConnectedPoint) { // 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), )); } } fn inject_disconnected(&mut self, peer_id: &PeerId, _: ConnectedPoint) { // inform the rpc handler that the peer has disconnected self.events.push(NetworkBehaviourAction::GenerateEvent( RPCMessage::PeerDisconnected(peer_id.clone()), )); } fn inject_node_event( &mut self, source: PeerId, event: ::OutEvent, ) { // send the event to the user self.events .push(NetworkBehaviourAction::GenerateEvent(RPCMessage::RPC( source, event, ))); } fn poll( &mut self, _: &mut impl PollParameters, ) -> Async< NetworkBehaviourAction< ::InEvent, Self::OutEvent, >, > { if !self.events.is_empty() { return Async::Ready(self.events.remove(0)); } Async::NotReady } } /// Messages sent to the user from the RPC protocol. pub enum RPCMessage { RPC(PeerId, RPCEvent), PeerDialed(PeerId), PeerDisconnected(PeerId), }