// Copyright 2015-2018 Parity Technologies (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . //! I/O and event context generalizations. use network::{NetworkContext, PeerId, NodeId}; use super::{Announcement, LightProtocol, ReqId}; use super::error::Error; use request::NetworkRequests as Requests; /// An I/O context which allows sending and receiving packets as well as /// disconnecting peers. This is used as a generalization of the portions /// of a p2p network which the light protocol structure makes use of. pub trait IoContext { /// Send a packet to a specific peer. fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec); /// Respond to a peer's message. Only works if this context is a byproduct /// of a packet handler. fn respond(&self, packet_id: u8, packet_body: Vec); /// Disconnect a peer. fn disconnect_peer(&self, peer: PeerId); /// Disable a peer -- this is a disconnect + a time-out. fn disable_peer(&self, peer: PeerId); /// Get a peer's protocol version. fn protocol_version(&self, peer: PeerId) -> Option; /// Persistent peer id fn persistent_peer_id(&self, peer: PeerId) -> Option; } impl IoContext for T where T: ?Sized + NetworkContext { fn send(&self, peer: PeerId, packet_id: u8, packet_body: Vec) { if let Err(e) = self.send(peer, packet_id, packet_body) { debug!(target: "pip", "Error sending packet to peer {}: {}", peer, e); } } fn respond(&self, packet_id: u8, packet_body: Vec) { if let Err(e) = self.respond(packet_id, packet_body) { debug!(target: "pip", "Error responding to peer message: {}", e); } } fn disconnect_peer(&self, peer: PeerId) { trace!(target: "pip", "Initiating disconnect of peer {}", peer); NetworkContext::disconnect_peer(self, peer); } fn disable_peer(&self, peer: PeerId) { trace!(target: "pip", "Initiating disable of peer {}", peer); NetworkContext::disable_peer(self, peer); } fn protocol_version(&self, peer: PeerId) -> Option { self.protocol_version(self.subprotocol_name(), peer) } fn persistent_peer_id(&self, peer: PeerId) -> Option { self.session_info(peer).and_then(|info| info.id) } } /// Basic context for the protocol. pub trait BasicContext { /// Returns the relevant's peer persistent Id (aka NodeId). fn persistent_peer_id(&self, peer: PeerId) -> Option; /// Make a request from a peer. /// /// Fails on: nonexistent peer, network error, peer not server, /// insufficient credits. Does not check capabilities before sending. /// On success, returns a request id which can later be coordinated /// with an event. fn request_from(&self, peer: PeerId, request: Requests) -> Result; /// Make an announcement of new capabilities to the rest of the peers. // TODO: maybe just put this on a timer in LightProtocol? fn make_announcement(&self, announcement: Announcement); /// Disconnect a peer. fn disconnect_peer(&self, peer: PeerId); /// Disable a peer. fn disable_peer(&self, peer: PeerId); } /// Context for a protocol event which has a peer ID attached. pub trait EventContext: BasicContext { /// Get the peer relevant to the event e.g. message sender, /// disconnected/connected peer. fn peer(&self) -> PeerId; /// Treat the event context as a basic context. fn as_basic(&self) -> &BasicContext; } /// Basic context. pub struct TickCtx<'a> { /// Io context to enable dispatch. pub io: &'a IoContext, /// Protocol implementation. pub proto: &'a LightProtocol, } impl<'a> BasicContext for TickCtx<'a> { fn persistent_peer_id(&self, id: PeerId) -> Option { self.io.persistent_peer_id(id) } fn request_from(&self, peer: PeerId, requests: Requests) -> Result { self.proto.request_from(self.io, &peer, requests) } fn make_announcement(&self, announcement: Announcement) { self.proto.make_announcement(self.io, announcement); } fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } fn disable_peer(&self, peer: PeerId) { self.io.disable_peer(peer); } } /// Concrete implementation of `EventContext` over the light protocol struct and /// an io context. pub struct Ctx<'a> { /// Io context to enable immediate response to events. pub io: &'a IoContext, /// Protocol implementation. pub proto: &'a LightProtocol, /// Relevant peer for event. pub peer: PeerId, } impl<'a> BasicContext for Ctx<'a> { fn persistent_peer_id(&self, id: PeerId) -> Option { self.io.persistent_peer_id(id) } fn request_from(&self, peer: PeerId, requests: Requests) -> Result { self.proto.request_from(self.io, &peer, requests) } fn make_announcement(&self, announcement: Announcement) { self.proto.make_announcement(self.io, announcement); } fn disconnect_peer(&self, peer: PeerId) { self.io.disconnect_peer(peer); } fn disable_peer(&self, peer: PeerId) { self.io.disable_peer(peer); } } impl<'a> EventContext for Ctx<'a> { fn peer(&self) -> PeerId { self.peer } fn as_basic(&self) -> &BasicContext { &*self } }