Generic request/response infrastructure for Polkadot (#2352)

* Move NetworkBridgeEvent to subsystem::messages.

It is not protocol related at all, it is in fact only part of the
subsystem communication as it gets wrapped into messages of each
subsystem.

* Request/response infrastructure is taking shape.

WIP: Does not compile.

* Multiplexer variant not supported by Rusts type system.

* request_response::request type checks.

* Cleanup.

* Minor fixes for request_response.

* Implement request sending + move multiplexer.

Request multiplexer is moved to bridge, as there the implementation is
more straight forward as we can specialize on `AllMessages` for the
multiplexing target.

Sending of requests is mostly complete, apart from a few `From`
instances. Receiving is also almost done, initializtion needs to be
fixed and the multiplexer needs to be invoked.

* Remove obsolete multiplexer.

* Initialize bridge with multiplexer.

* Finish generic request sending/receiving.

Subsystems are now able to receive and send requests and responses via
the overseer.

* Doc update.

* Fixes.

* Link issue for not yet implemented code.

* Fixes suggested by @ordian - thanks!

- start encoding at 0
- don't crash on zero protocols
- don't panic on not yet implemented request handling

* Update node/network/protocol/src/request_response/v1.rs

Use index 0 instead of 1.

Co-authored-by: Andronik Ordian <write@reusable.software>

* Update node/network/protocol/src/request_response.rs

Co-authored-by: Andronik Ordian <write@reusable.software>

* Fix existing tests.

* Better avoidance of division by zoro errors.

* Doc fixes.

* send_request -> start_request.

* Fix missing renamings.

* Update substrate.

* Pass TryConnect instead of true.

* Actually import `IfDisconnected`.

* Fix wrong import.

* Update node/network/bridge/src/lib.rs

typo

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>

* Update node/network/bridge/src/multiplexer.rs

Remove redundant import.

Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>

* Stop doing tracing from within `From` instance.

Thanks for the catch @tomaka!

* Get rid of redundant import.

* Formatting cleanup.

* Fix tests.

* Add link to issue.

* Clarify comments some more.

* Fix tests.

* Formatting fix.

* tabs

* Fix link

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>

* Use map_err.

Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>

* Improvements inspired by suggestions by @drahnr.

- Channel size is now determined by function.
- Explicitely scope NetworkService::start_request.

Co-authored-by: Andronik Ordian <write@reusable.software>
Co-authored-by: Pierre Krieger <pierre.krieger1708@gmail.com>
Co-authored-by: Bernhard Schuster <bernhard@ahoi.io>
This commit is contained in:
Robert Klotzner
2021-02-03 21:21:09 +01:00
committed by GitHub
parent 41eeb5716f
commit 0cb1ccd122
25 changed files with 964 additions and 260 deletions
@@ -0,0 +1,132 @@
// 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 <http://www.gnu.org/licenses/>.
use std::pin::Pin;
use futures::channel::mpsc;
use futures::stream::Stream;
use futures::task::{Context, Poll};
use strum::IntoEnumIterator;
use parity_scale_codec::{Decode, Error as DecodingError};
use sc_network::config as network;
use sc_network::PeerId;
use polkadot_node_network_protocol::request_response::{
request::IncomingRequest, v1, Protocol, RequestResponseConfig,
};
use polkadot_subsystem::messages::AllMessages;
/// Multiplex incoming network requests.
///
/// This multiplexer consumes all request streams and makes them a `Stream` of a single message
/// type, useful for the network bridge to send them via the `Overseer` to other subsystems.
pub struct RequestMultiplexer {
receivers: Vec<(Protocol, mpsc::Receiver<network::IncomingRequest>)>,
next_poll: usize,
}
/// Multiplexing can fail in case of invalid messages.
pub struct RequestMultiplexError {
/// The peer that sent the invalid message.
pub peer: PeerId,
/// The error that occurred.
pub error: DecodingError,
}
impl RequestMultiplexer {
/// Create a new `RequestMultiplexer`.
///
/// This function uses `Protocol::get_config` for each available protocol and creates a
/// `RequestMultiplexer` from it. The returned `RequestResponseConfig`s must be passed to the
/// network implementation.
pub fn new() -> (Self, Vec<RequestResponseConfig>) {
let (receivers, cfgs): (Vec<_>, Vec<_>) = Protocol::iter()
.map(|p| {
let (rx, cfg) = p.get_config();
((p, rx), cfg)
})
.unzip();
(
Self {
receivers,
next_poll: 0,
},
cfgs,
)
}
}
impl Stream for RequestMultiplexer {
type Item = Result<AllMessages, RequestMultiplexError>;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let len = self.receivers.len();
let mut count = len;
let mut i = self.next_poll;
let mut result = Poll::Ready(None);
// Poll streams in round robin fashion:
while count > 0 {
// % safe, because count initialized to len, loop would not be entered if 0, also
// length of receivers is fixed.
let (p, rx): &mut (_, _) = &mut self.receivers[i % len];
i += 1;
count -= 1;
match Pin::new(rx).poll_next(cx) {
// If at least one stream is pending, then we are not done yet (No
// Ready(None)).
Poll::Pending => result = Poll::Pending,
// Receiver is a fused stream, which allows for this simple handling of
// exhausted ones.
Poll::Ready(None) => {}
Poll::Ready(Some(v)) => {
result = Poll::Ready(Some(multiplex_single(*p, v)));
break;
}
}
}
self.next_poll = i;
result
}
}
/// Convert a single raw incoming request into a `MultiplexMessage`.
fn multiplex_single(
p: Protocol,
network::IncomingRequest {
payload,
peer,
pending_response,
}: network::IncomingRequest,
) -> Result<AllMessages, RequestMultiplexError> {
let r = match p {
Protocol::AvailabilityFetching => From::from(IncomingRequest::new(
peer,
decode_with_peer::<v1::AvailabilityFetchingRequest>(peer, payload)?,
pending_response,
)),
};
Ok(r)
}
fn decode_with_peer<Req: Decode>(
peer: PeerId,
payload: Vec<u8>,
) -> Result<Req, RequestMultiplexError> {
Req::decode(&mut payload.as_ref()).map_err(|error| RequestMultiplexError { peer, error })
}