Dispute distribution implementation (#3282)

* Dispute protocol.

* Dispute distribution protocol.

* Get network requests routed.

* WIP: Basic dispute sender logic.

* Basic validator determination logic.

* WIP: Getting things to typecheck.

* Slightly larger timeout.

* More typechecking stuff.

* Cleanup.

* Finished most of the sending logic.

* Handle active leaves updates

- Cleanup dead disputes
- Update sends for new sessions
- Retry on errors

* Pass sessions in already.

* Startup dispute sending.

* Provide incoming decoding facilities

and use them in statement-distribution.

* Relaxed runtime util requirements.

We only need a `SubsystemSender` not a full `SubsystemContext`.

* Better usability of incoming requests.

Make it possible to consume stuff without clones.

* Add basic receiver functionality.

* Cleanup + fixes for sender.

* One more sender fix.

* Start receiver.

* Make sure to send responses back.

* WIP: Exposed authority discovery

* Make tests pass.

* Fully featured receiver.

* Decrease cost of `NotAValidator`.

* Make `RuntimeInfo` LRU cache size configurable.

* Cache more sessions.

* Fix collator protocol.

* Disable metrics for now.

* Make dispute-distribution a proper subsystem.

* Fix naming.

* Code style fixes.

* Factored out 4x copied mock function.

* WIP: Tests.

* Whitespace cleanup.

* Accessor functions.

* More testing.

* More Debug instances.

* Fix busy loop.

* Working tests.

* More tests.

* Cleanup.

* Fix build.

* Basic receiving test.

* Non validator message gets dropped.

* More receiving tests.

* Test nested and subsequent imports.

* Fix spaces.

* Better formatted imports.

* Import cleanup.

* Metrics.

* Message -> MuxedMessage

* Message -> MuxedMessage

* More review remarks.

* Add missing metrics.rs.

* Fix flaky test.

* Dispute coordinator - deliver confirmations.

* Send out `DisputeMessage` on issue local statement.

* Unwire dispute distribution.

* Review remarks.

* Review remarks.

* Better docs.
This commit is contained in:
Robert Klotzner
2021-07-09 04:29:53 +02:00
committed by GitHub
parent 20993b32b1
commit b5257b2407
52 changed files with 4040 additions and 407 deletions
+16 -5
View File
@@ -24,6 +24,7 @@ use parity_scale_codec::{Encode, Decode};
use parking_lot::Mutex;
use futures::prelude::*;
use futures::stream::BoxStream;
use polkadot_subsystem::messages::DisputeDistributionMessage;
use sc_network::Event as NetworkEvent;
use sp_consensus::SyncOracle;
@@ -57,7 +58,8 @@ use polkadot_node_subsystem_util::metrics::{self, prometheus};
/// To be added to [`NetworkConfiguration::extra_sets`].
pub use polkadot_node_network_protocol::peer_set::{peer_sets_info, IsAuthority};
use std::collections::{HashMap, hash_map, HashSet};
use std::collections::HashSet;
use std::collections::{HashMap, hash_map};
use std::sync::Arc;
mod validator_discovery;
@@ -66,12 +68,14 @@ mod validator_discovery;
///
/// Defines the `Network` trait with an implementation for an `Arc<NetworkService>`.
mod network;
use network::{Network, send_message, get_peer_id_by_authority_id};
use network::{Network, send_message};
/// Request multiplexer for combining the multiple request sources into a single `Stream` of `AllMessages`.
mod multiplexer;
pub use multiplexer::RequestMultiplexer;
use crate::network::get_peer_id_by_authority_id;
#[cfg(test)]
mod tests;
@@ -304,7 +308,7 @@ impl<N, AD> NetworkBridge<N, AD> {
impl<Net, AD, Context> Subsystem<Context, SubsystemError> for NetworkBridge<Net, AD>
where
Net: Network + Sync,
AD: validator_discovery::AuthorityDiscovery,
AD: validator_discovery::AuthorityDiscovery + Clone,
Context: SubsystemContext<Message = NetworkBridgeMessage> + overseer::SubsystemContext<Message = NetworkBridgeMessage>,
{
fn start(mut self, ctx: Context) -> SpawnedSubsystem {
@@ -380,7 +384,7 @@ where
Context: SubsystemContext<Message = NetworkBridgeMessage>,
Context: overseer::SubsystemContext<Message = NetworkBridgeMessage>,
N: Network,
AD: validator_discovery::AuthorityDiscovery,
AD: validator_discovery::AuthorityDiscovery + Clone,
{
// This is kept sorted, descending, by block number.
let mut live_heads: Vec<ActivatedLeaf> = Vec::with_capacity(MAX_VIEW_HEADS);
@@ -877,7 +881,7 @@ async fn run_network<N, AD, Context>(
) -> SubsystemResult<()>
where
N: Network,
AD: validator_discovery::AuthorityDiscovery,
AD: validator_discovery::AuthorityDiscovery + Clone,
Context: SubsystemContext<Message=NetworkBridgeMessage> + overseer::SubsystemContext<Message=NetworkBridgeMessage>,
{
let shared = Shared::default();
@@ -894,6 +898,10 @@ where
.get_statement_fetching()
.expect("Gets initialized, must be `Some` on startup. qed.");
let dispute_receiver = request_multiplexer
.get_dispute_sending()
.expect("Gets initialized, must be `Some` on startup. qed.");
let (remote, network_event_handler) = handle_network_messages::<>(
ctx.sender().clone(),
network_service.clone(),
@@ -906,6 +914,9 @@ where
ctx.spawn("network-bridge-network-worker", Box::pin(remote))?;
ctx.send_message(
DisputeDistributionMessage::DisputeSendingReceiver(dispute_receiver)
).await;
ctx.send_message(
StatementDistributionMessage::StatementFetchingReceiver(statement_receiver)
).await;
@@ -15,6 +15,7 @@
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use std::pin::Pin;
use std::unreachable;
use futures::channel::mpsc;
use futures::stream::{FusedStream, Stream};
@@ -42,6 +43,7 @@ use polkadot_overseer::AllMessages;
pub struct RequestMultiplexer {
receivers: Vec<(Protocol, mpsc::Receiver<network::IncomingRequest>)>,
statement_fetching: Option<mpsc::Receiver<network::IncomingRequest>>,
dispute_sending: Option<mpsc::Receiver<network::IncomingRequest>>,
next_poll: usize,
}
@@ -68,6 +70,8 @@ impl RequestMultiplexer {
})
.unzip();
// Ok this code is ugly as hell, it is also a hack, see https://github.com/paritytech/polkadot/issues/2842.
// But it works and is executed on startup so, if anything is wrong here it will be noticed immediately.
let index = receivers.iter().enumerate().find_map(|(i, (p, _))|
if let Protocol::StatementFetching = p {
Some(i)
@@ -77,10 +81,20 @@ impl RequestMultiplexer {
).expect("Statement fetching must be registered. qed.");
let statement_fetching = Some(receivers.remove(index).1);
let index = receivers.iter().enumerate().find_map(|(i, (p, _))|
if let Protocol::DisputeSending = p {
Some(i)
} else {
None
}
).expect("Dispute sending must be registered. qed.");
let dispute_sending = Some(receivers.remove(index).1);
(
Self {
receivers,
statement_fetching,
dispute_sending,
next_poll: 0,
},
cfgs,
@@ -93,6 +107,13 @@ impl RequestMultiplexer {
pub fn get_statement_fetching(&mut self) -> Option<mpsc::Receiver<network::IncomingRequest>> {
std::mem::take(&mut self.statement_fetching)
}
/// Get the receiver for handling dispute sending requests.
///
/// This function will only return `Some` once.
pub fn get_dispute_sending(&mut self) -> Option<mpsc::Receiver<network::IncomingRequest>> {
std::mem::take(&mut self.dispute_sending)
}
}
impl Stream for RequestMultiplexer {
@@ -174,6 +195,9 @@ fn multiplex_single(
Protocol::StatementFetching => {
unreachable!("Statement fetching requests are handled directly. qed.");
}
Protocol::DisputeSending => {
unreachable!("Dispute sending request are handled directly. qed.");
}
};
Ok(r)
}
+45 -2
View File
@@ -67,7 +67,7 @@ struct TestNetwork {
_req_configs: Vec<RequestResponseConfig>,
}
#[derive(Clone)]
#[derive(Clone, Debug)]
struct TestAuthorityDiscovery;
// The test's view of the network. This receives updates from the subsystem in the form
@@ -688,6 +688,12 @@ fn peer_view_updates_sent_via_overseer() {
let view = view![Hash::repeat_byte(1)];
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -740,6 +746,12 @@ fn peer_messages_sent_via_overseer() {
ObservedRole::Full,
).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -812,6 +824,12 @@ fn peer_disconnect_from_just_one_peerset() {
network_handle.connect_peer(peer.clone(), PeerSet::Validation, ObservedRole::Full).await;
network_handle.connect_peer(peer.clone(), PeerSet::Collation, ObservedRole::Full).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -894,6 +912,12 @@ fn relays_collation_protocol_messages() {
let peer_a = PeerId::random();
let peer_b = PeerId::random();
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -992,6 +1016,12 @@ fn different_views_on_different_peer_sets() {
network_handle.connect_peer(peer.clone(), PeerSet::Validation, ObservedRole::Full).await;
network_handle.connect_peer(peer.clone(), PeerSet::Collation, ObservedRole::Full).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -1153,6 +1183,12 @@ fn send_messages_to_peers() {
network_handle.connect_peer(peer.clone(), PeerSet::Validation, ObservedRole::Full).await;
network_handle.connect_peer(peer.clone(), PeerSet::Collation, ObservedRole::Full).await;
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -1279,7 +1315,8 @@ fn spread_event_to_subsystems_is_up_to_date() {
AllMessages::ApprovalDistribution(_) => { cnt += 1; }
AllMessages::GossipSupport(_) => unreachable!("Not interested in network events"),
AllMessages::DisputeCoordinator(_) => unreachable!("Not interested in network events"),
AllMessages::DisputeParticipation(_) => unreachable!("Not interetsed in network events"),
AllMessages::DisputeParticipation(_) => unreachable!("Not interested in network events"),
AllMessages::DisputeDistribution(_) => unreachable!("Not interested in network events"),
AllMessages::ChainSelection(_) => unreachable!("Not interested in network events"),
// Add variants here as needed, `{ cnt += 1; }` for those that need to be
// notified, `unreachable!()` for those that should not.
@@ -1325,6 +1362,12 @@ fn our_view_updates_decreasing_order_and_limited_to_max() {
0,
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::DisputeDistribution(
DisputeDistributionMessage::DisputeSendingReceiver(_)
)
);
assert_matches!(
virtual_overseer.recv().await,
AllMessages::StatementDistribution(
@@ -21,37 +21,16 @@ use crate::Network;
use core::marker::PhantomData;
use std::collections::HashSet;
use async_trait::async_trait;
use futures::channel::oneshot;
use sc_network::multiaddr::Multiaddr;
use sc_authority_discovery::Service as AuthorityDiscoveryService;
use polkadot_node_network_protocol::PeerId;
use polkadot_primitives::v1::AuthorityDiscoveryId;
use polkadot_node_network_protocol::peer_set::{PeerSet, PerPeerSet};
pub use polkadot_node_network_protocol::authority_discovery::AuthorityDiscovery;
const LOG_TARGET: &str = "parachain::validator-discovery";
/// An abstraction over the authority discovery service.
#[async_trait]
pub trait AuthorityDiscovery: Send + Clone + 'static {
/// Get the addresses for the given [`AuthorityId`] from the local address cache.
async fn get_addresses_by_authority_id(&mut self, authority: AuthorityDiscoveryId) -> Option<Vec<Multiaddr>>;
/// Get the [`AuthorityId`] for the given [`PeerId`] from the local address cache.
async fn get_authority_id_by_peer_id(&mut self, peer_id: PeerId) -> Option<AuthorityDiscoveryId>;
}
#[async_trait]
impl AuthorityDiscovery for AuthorityDiscoveryService {
async fn get_addresses_by_authority_id(&mut self, authority: AuthorityDiscoveryId) -> Option<Vec<Multiaddr>> {
AuthorityDiscoveryService::get_addresses_by_authority_id(self, authority).await
}
async fn get_authority_id_by_peer_id(&mut self, peer_id: PeerId) -> Option<AuthorityDiscoveryId> {
AuthorityDiscoveryService::get_authority_id_by_peer_id(self, peer_id).await
}
}
pub(super) struct Service<N, AD> {
state: PerPeerSet<StatePerPeerSet>,
// PhantomData used to make the struct generic instead of having generic methods
@@ -147,9 +126,10 @@ mod tests {
use std::{borrow::Cow, collections::HashMap};
use futures::stream::BoxStream;
use async_trait::async_trait;
use sc_network::{Event as NetworkEvent, IfDisconnected};
use sp_keyring::Sr25519Keyring;
use polkadot_node_network_protocol::request_response::request::Requests;
use polkadot_node_network_protocol::{PeerId, request_response::request::Requests};
fn new_service() -> Service<TestNetwork, TestAuthorityDiscovery> {
Service::new()
@@ -164,7 +144,7 @@ mod tests {
peers_set: HashSet<Multiaddr>,
}
#[derive(Default, Clone)]
#[derive(Default, Clone, Debug)]
struct TestAuthorityDiscovery {
by_authority_id: HashMap<AuthorityDiscoveryId, Multiaddr>,
by_peer_id: HashMap<PeerId, AuthorityDiscoveryId>,