// Copyright (C) Parity Technologies (UK) Ltd. // This file is part of Pezkuwi. // Pezkuwi 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. // Pezkuwi 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 Pezkuwi. If not, see . use futures::{channel::oneshot, prelude::Future, FutureExt}; use codec::{Decode, Encode, Error as DecodingError}; use network::ProtocolName; use sc_network as network; use sc_network_types::PeerId; use pezkuwi_primitives::AuthorityDiscoveryId; use super::{v1, v2, IsRequest, Protocol}; /// All requests that can be sent to the network bridge via `NetworkBridgeTxMessage::SendRequest`. #[derive(Debug)] pub enum Requests { /// Request an availability chunk from a node. ChunkFetching(OutgoingRequest), /// Fetch a collation from a collator which previously announced it. CollationFetchingV1(OutgoingRequest), /// Fetch a PoV from a validator which previously sent out a seconded statement. PoVFetchingV1(OutgoingRequest), /// Request full available data from a node. AvailableDataFetchingV1(OutgoingRequest), /// Requests for notifying about an ongoing dispute. DisputeSendingV1(OutgoingRequest), /// Request a candidate and attestations. AttestedCandidateV2(OutgoingRequest), /// Fetch a collation from a collator which previously announced it. /// Compared to V1 it requires specifying which candidate is requested by its hash. CollationFetchingV2(OutgoingRequest), } impl Requests { /// Encode the request. /// /// The corresponding protocol is returned as well, as we are now leaving typed territory. /// /// Note: `Requests` is just an enum collecting all supported requests supported by network /// bridge, it is never sent over the wire. This function just encodes the individual requests /// contained in the `enum`. pub fn encode_request(self) -> (Protocol, OutgoingRequest>) { match self { Self::ChunkFetching(r) => r.encode_request(), Self::CollationFetchingV1(r) => r.encode_request(), Self::CollationFetchingV2(r) => r.encode_request(), Self::PoVFetchingV1(r) => r.encode_request(), Self::AvailableDataFetchingV1(r) => r.encode_request(), Self::DisputeSendingV1(r) => r.encode_request(), Self::AttestedCandidateV2(r) => r.encode_request(), } } } /// Used by the network to send us a response to a request. pub type ResponseSender = oneshot::Sender, ProtocolName), network::RequestFailure>>; /// Any error that can occur when sending a request. #[derive(Debug, thiserror::Error)] pub enum RequestError { /// Response could not be decoded. #[error("Response could not be decoded: {0}")] InvalidResponse(#[from] DecodingError), /// Some error in substrate/libp2p happened. #[error("{0}")] NetworkError(#[from] network::RequestFailure), /// Response got canceled by networking. #[error("Response channel got canceled")] Canceled(#[from] oneshot::Canceled), } impl RequestError { /// Whether the error represents some kind of timeout condition. pub fn is_timed_out(&self) -> bool { match self { Self::Canceled(_) | Self::NetworkError(network::RequestFailure::Obsolete) | Self::NetworkError(network::RequestFailure::Network( network::OutboundFailure::Timeout, )) => true, _ => false, } } } /// A request to be sent to the network bridge, including a sender for sending responses/failures. /// /// The network implementation will make use of that sender for informing the requesting subsystem /// about responses/errors. /// /// When using `Recipient::Peer`, keep in mind that no address (as in IP address and port) might /// be known for that specific peer. You are encouraged to use `Peer` for peers that you are /// expected to be already connected to. /// When using `Recipient::Authority`, the addresses can be found thanks to the authority /// discovery system. #[derive(Debug)] pub struct OutgoingRequest { /// Intended recipient of this request. pub peer: Recipient, /// The actual request to send over the wire. pub payload: Req, /// Optional fallback request and protocol. pub fallback_request: Option<(FallbackReq, Protocol)>, /// Sender which is used by networking to get us back a response. pub pending_response: ResponseSender, } /// Potential recipients of an outgoing request. #[derive(Debug, Eq, Hash, PartialEq, Clone)] pub enum Recipient { /// Recipient is a regular peer and we know its peer id. Peer(PeerId), /// Recipient is a validator, we address it via this `AuthorityDiscoveryId`. Authority(AuthorityDiscoveryId), } /// Responses received for an `OutgoingRequest`. pub type OutgoingResult = Result; impl OutgoingRequest where Req: IsRequest + Encode, Req::Response: Decode, FallbackReq: IsRequest + Encode, FallbackReq::Response: Decode, { /// Create a new `OutgoingRequest`. /// /// It will contain a sender that is used by the networking for sending back responses. The /// connected receiver is returned as the second element in the returned tuple. pub fn new( peer: Recipient, payload: Req, ) -> (Self, impl Future>) { let (tx, rx) = oneshot::channel(); let r = Self { peer, payload, pending_response: tx, fallback_request: None }; (r, receive_response::(rx.map(|r| r.map(|r| r.map(|(resp, _)| resp))))) } /// Create a new `OutgoingRequest` with a fallback in case the remote does not support this /// protocol. Useful when adding a new version of a req-response protocol, to achieve /// compatibility with the older version. /// /// Returns a raw `Vec` response over the channel. Use the associated `ProtocolName` to know /// which request was the successful one and appropriately decode the response. pub fn new_with_fallback( peer: Recipient, payload: Req, fallback_request: FallbackReq, ) -> (Self, impl Future, ProtocolName)>>) { let (tx, rx) = oneshot::channel(); let r = Self { peer, payload, pending_response: tx, fallback_request: Some((fallback_request, FallbackReq::PROTOCOL)), }; (r, async { Ok(rx.await??) }) } /// Encode a request into a `Vec`. /// /// As this throws away type information, we also return the `Protocol` this encoded request /// adheres to. pub fn encode_request(self) -> (Protocol, OutgoingRequest>) { let OutgoingRequest { peer, payload, pending_response, fallback_request } = self; let encoded = OutgoingRequest { peer, payload: payload.encode(), fallback_request: fallback_request.map(|(r, p)| (r.encode(), p)), pending_response, }; (Req::PROTOCOL, encoded) } } /// Future for actually receiving a typed response for an `OutgoingRequest`. async fn receive_response( rec: impl Future, network::RequestFailure>, oneshot::Canceled>>, ) -> OutgoingResult where Req: IsRequest, Req::Response: Decode, { let raw = rec.await??; Ok(Decode::decode(&mut raw.as_ref())?) }