// Copyright 2021 Parity Technologies (UK) Ltd. // This file is part of Polkadot. // Polkadot 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. // Polkadot 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 Polkadot. If not, see . //! Overview over request/responses as used in `Polkadot`. //! //! enum Protocol .... List of all supported protocols. //! //! enum Requests .... List of all supported requests, each entry matches one in protocols, but //! has the actual request as payload. //! //! struct IncomingRequest .... wrapper for incoming requests, containing a sender for sending //! responses. //! //! struct OutgoingRequest .... wrapper for outgoing requests, containing a sender used by the //! networking code for delivering responses/delivery errors. //! //! trait `IsRequest` .... A trait describing a particular request. It is used for gathering meta //! data, like what is the corresponding response type. //! //! Versioned (v1 module): The actual requests and responses as sent over the network. use std::borrow::Cow; use std::time::Duration; use futures::channel::mpsc; use polkadot_node_primitives::MAX_COMPRESSED_POV_SIZE; use strum::EnumIter; pub use sc_network::config as network; pub use sc_network::config::RequestResponseConfig; /// All requests that can be sent to the network bridge. pub mod request; pub use request::{IncomingRequest, OutgoingRequest, Requests, Recipient, OutgoingResult}; ///// Multiplexer for incoming requests. // pub mod multiplexer; /// Actual versioned requests and responses, that are sent over the wire. pub mod v1; /// A protocol per subsystem seems to make the most sense, this way we don't need any dispatching /// within protocols. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, EnumIter)] pub enum Protocol { /// Protocol for chunk fetching, used by availability distribution and availability recovery. ChunkFetching, /// Protocol for fetching collations from collators. CollationFetching, /// Protocol for fetching seconded PoVs from validators of the same group. PoVFetching, /// Protocol for fetching available data. AvailableDataFetching, } /// Default request timeout in seconds. /// /// When decreasing this value, take into account that the very first request might need to open a /// connection, which can be slow. If this causes problems, we should ensure connectivity via peer /// sets. #[allow(dead_code)] const DEFAULT_REQUEST_TIMEOUT: Duration = Duration::from_secs(3); /// Request timeout where we can assume the connection is already open (e.g. we have peers in a /// peer set as well). const DEFAULT_REQUEST_TIMEOUT_CONNECTED: Duration = Duration::from_secs(1); /// Minimum bandwidth we expect for validators - 500Mbit/s is the recommendation, so approximately /// 50Meg bytes per second: const MIN_BANDWIDTH_BYTES: u64 = 50 * 1024 * 1024; /// Timeout for PoV like data, 2 times what it should take, assuming we can fully utilize the /// bandwidth. This amounts to two seconds right now. const POV_REQUEST_TIMEOUT_CONNECTED: Duration = Duration::from_millis(2 * 1000 * (MAX_COMPRESSED_POV_SIZE as u64) / MIN_BANDWIDTH_BYTES); impl Protocol { /// Get a configuration for a given Request response protocol. /// /// Returns a receiver for messages received on this protocol and the requested /// `ProtocolConfig`. /// /// See also `dispatcher::RequestDispatcher`, which makes use of this function and provides a more /// high-level interface. pub fn get_config( self, ) -> ( mpsc::Receiver, RequestResponseConfig, ) { let p_name = self.into_protocol_name(); let (tx, rx) = mpsc::channel(self.get_channel_size()); let cfg = match self { Protocol::ChunkFetching => RequestResponseConfig { name: p_name, max_request_size: 10_000, max_response_size: 10_000_000, // We are connected to all validators: request_timeout: DEFAULT_REQUEST_TIMEOUT_CONNECTED, inbound_queue: Some(tx), }, Protocol::CollationFetching => RequestResponseConfig { name: p_name, max_request_size: 10_000, max_response_size: MAX_COMPRESSED_POV_SIZE as u64, // Taken from initial implementation in collator protocol: request_timeout: POV_REQUEST_TIMEOUT_CONNECTED, inbound_queue: Some(tx), }, Protocol::PoVFetching => RequestResponseConfig { name: p_name, max_request_size: 1_000, max_response_size: MAX_COMPRESSED_POV_SIZE as u64, request_timeout: POV_REQUEST_TIMEOUT_CONNECTED, inbound_queue: Some(tx), }, Protocol::AvailableDataFetching => RequestResponseConfig { name: p_name, max_request_size: 1_000, // Available data size is dominated by the PoV size. max_response_size: MAX_COMPRESSED_POV_SIZE as u64, request_timeout: POV_REQUEST_TIMEOUT_CONNECTED, inbound_queue: Some(tx), }, }; (rx, cfg) } // Channel sizes for the supported protocols. fn get_channel_size(self) -> usize { match self { // Hundreds of validators will start requesting their chunks once they see a candidate // awaiting availability on chain. Given that they will see that block at different // times (due to network delays), 100 seems big enough to accomodate for "bursts", // assuming we can service requests relatively quickly, which would need to be measured // as well. Protocol::ChunkFetching => 100, // 10 seems reasonable, considering group sizes of max 10 validators. Protocol::CollationFetching => 10, // 10 seems reasonable, considering group sizes of max 10 validators. Protocol::PoVFetching => 10, // Validators are constantly self-selecting to request available data which may lead // to constant load and occasional burstiness. Protocol::AvailableDataFetching => 100, } } /// Get the protocol name of this protocol, as understood by substrate networking. pub fn into_protocol_name(self) -> Cow<'static, str> { self.get_protocol_name_static().into() } /// Get the protocol name associated with each peer set as static str. pub const fn get_protocol_name_static(self) -> &'static str { match self { Protocol::ChunkFetching => "/polkadot/req_chunk/1", Protocol::CollationFetching => "/polkadot/req_collation/1", Protocol::PoVFetching => "/polkadot/req_pov/1", Protocol::AvailableDataFetching => "/polkadot/req_available_data/1", } } }