// Copyright 2020 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 .
//! Utility function to make it easier to connect to validators.
use std::collections::HashMap;
use std::pin::Pin;
use futures::{
channel::mpsc,
task::{Poll, self},
stream,
StreamExt,
};
use streamunordered::{StreamUnordered, StreamYield};
use polkadot_node_subsystem::{
errors::RuntimeApiError,
messages::{AllMessages, NetworkBridgeMessage},
SubsystemContext,
};
use polkadot_primitives::v1::{
Hash, ValidatorId, AuthorityDiscoveryId, SessionIndex, Id as ParaId,
};
use polkadot_node_network_protocol::peer_set::PeerSet;
use sc_network::PeerId;
use crate::Error;
/// Utility function to make it easier to connect to validators.
pub async fn connect_to_validators(
ctx: &mut Context,
relay_parent: Hash,
validators: Vec,
peer_set: PeerSet,
) -> Result {
let current_index = crate::request_session_index_for_child_ctx(relay_parent, ctx).await?.await??;
connect_to_validators_in_session(
ctx,
relay_parent,
validators,
peer_set,
current_index,
).await
}
/// Utility function to make it easier to connect to validators in the given session.
pub async fn connect_to_validators_in_session(
ctx: &mut Context,
relay_parent: Hash,
validators: Vec,
peer_set: PeerSet,
session_index: SessionIndex,
) -> Result {
let session_info = crate::request_session_info_ctx(
relay_parent,
session_index,
ctx,
).await?.await??;
let (session_validators, discovery_keys) = match session_info {
Some(info) => (info.validators, info.discovery_keys),
None => return Err(RuntimeApiError::from(
format!("No SessionInfo found for the index {}", session_index)
).into()),
};
tracing::trace!(
target: "parachain::validator-discovery",
validators = ?validators,
discovery_keys = ?discovery_keys,
session_index,
"Trying to serve the validator discovery request",
);
let id_to_index = session_validators.iter()
.zip(0usize..)
.collect::>();
// We assume the same ordering in authorities as in validators so we can do an index search
let maybe_authorities: Vec<_> = validators.iter()
.map(|id| {
let validator_index = id_to_index.get(&id);
validator_index.and_then(|i| discovery_keys.get(*i).cloned())
})
.collect();
let authorities: Vec<_> = maybe_authorities.iter()
.cloned()
.filter_map(|id| id)
.collect();
let validator_map = validators.into_iter()
.zip(maybe_authorities.into_iter())
.filter_map(|(k, v)| v.map(|v| (v, k)))
.collect::>();
let connections = connect_to_authorities(ctx, authorities, peer_set).await;
Ok(ConnectionRequest {
validator_map,
connections,
})
}
/// A helper function for making a `ConnectToValidators` request.
pub async fn connect_to_authorities(
ctx: &mut Context,
validator_ids: Vec,
peer_set: PeerSet,
) -> mpsc::Receiver<(AuthorityDiscoveryId, PeerId)> {
const PEERS_CAPACITY: usize = 32;
let (connected, connected_rx) = mpsc::channel(PEERS_CAPACITY);
ctx.send_message(AllMessages::NetworkBridge(
NetworkBridgeMessage::ConnectToValidators {
validator_ids,
peer_set,
connected,
}
)).await;
connected_rx
}
/// Represents a discovered validator.
///
/// Result of [`ConnectionRequests::next`].
#[derive(Debug, PartialEq)]
pub struct DiscoveredValidator {
/// The relay parent associated with the connection request that returned a result.
pub relay_parent: Hash,
/// The para ID associated with the connection request that returned a result.
pub para_id: ParaId,
/// The [`ValidatorId`] that was resolved.
pub validator_id: ValidatorId,
/// The [`PeerId`] associated to the validator id.
pub peer_id: PeerId,
}
/// Used by [`ConnectionRequests::requests`] to map a [`ConnectionRequest`] item to a [`DiscoveredValidator`].
struct ConnectionRequestForRelayParentAndParaId {
request: ConnectionRequest,
relay_parent: Hash,
para_id: ParaId,
}
impl stream::Stream for ConnectionRequestForRelayParentAndParaId {
type Item = DiscoveredValidator;
fn poll_next(mut self: Pin<&mut Self>, cx: &mut task::Context) -> Poll