cargo +nightly fmt (#3540)

* cargo +nightly fmt

* add cargo-fmt check to ci

* update ci

* fmt

* fmt

* skip macro

* ignore bridges
This commit is contained in:
Shawn Tabrizi
2021-08-02 12:47:33 +02:00
committed by GitHub
parent 30e3012270
commit ff5d56fb76
350 changed files with 20617 additions and 21266 deletions
@@ -19,11 +19,10 @@
use thiserror::Error;
use polkadot_node_subsystem_util::{Fault, runtime, unwrap_non_fatal};
use polkadot_node_subsystem_util::{runtime, unwrap_non_fatal, Fault};
use polkadot_subsystem::SubsystemError;
use crate::LOG_TARGET;
use crate::sender;
use crate::{sender, LOG_TARGET};
#[derive(Debug, Error)]
#[error(transparent)]
@@ -53,7 +52,6 @@ impl From<sender::Error> for Error {
/// Fatal errors of this subsystem.
#[derive(Debug, Error)]
pub enum Fatal {
/// Receiving subsystem message from overseer failed.
#[error("Receiving message from overseer failed")]
SubsystemReceive(#[source] SubsystemError),
@@ -91,9 +89,7 @@ pub type FatalResult<T> = std::result::Result<T, Fatal>;
///
/// We basically always want to try and continue on error. This utility function is meant to
/// consume top-level errors by simply logging them
pub fn log_error(result: Result<()>, ctx: &'static str)
-> std::result::Result<(), Fatal>
{
pub fn log_error(result: Result<()>, ctx: &'static str) -> std::result::Result<(), Fatal> {
if let Some(error) = unwrap_non_fatal(result.map_err(|e| e.0))? {
tracing::warn!(target: LOG_TARGET, error = ?error, ctx);
}
@@ -24,21 +24,17 @@
//! The sender is responsible for getting our vote out, see [`sender`]. The receiver handles
//! incoming [`DisputeRequest`]s and offers spam protection, see [`receiver`].
use futures::channel::{mpsc};
use futures::{FutureExt, StreamExt, TryFutureExt};
use futures::{channel::mpsc, FutureExt, StreamExt, TryFutureExt};
use polkadot_node_network_protocol::authority_discovery::AuthorityDiscovery;
use sp_keystore::SyncCryptoStorePtr;
use polkadot_node_primitives::DISPUTE_WINDOW;
use polkadot_node_subsystem_util::{runtime, runtime::RuntimeInfo};
use polkadot_subsystem::{
overseer, messages::DisputeDistributionMessage, FromOverseer, OverseerSignal, SpawnedSubsystem,
messages::DisputeDistributionMessage, overseer, FromOverseer, OverseerSignal, SpawnedSubsystem,
SubsystemContext, SubsystemError,
};
use polkadot_node_subsystem_util::{
runtime,
runtime::RuntimeInfo,
};
/// ## The sender [`DisputeSender`]
///
@@ -85,8 +81,7 @@ use self::receiver::DisputesReceiver;
/// Error and [`Result`] type for this subsystem.
mod error;
use error::{Fatal, FatalResult};
use error::{Result, log_error};
use error::{log_error, Fatal, FatalResult, Result};
#[cfg(test)]
mod tests;
@@ -119,7 +114,8 @@ impl<Context, AD> overseer::Subsystem<Context, SubsystemError> for DisputeDistri
where
Context: SubsystemContext<Message = DisputeDistributionMessage>
+ overseer::SubsystemContext<Message = DisputeDistributionMessage>
+ Sync + Send,
+ Sync
+ Send,
AD: AuthorityDiscovery + Clone,
{
fn start(self, ctx: Context) -> SpawnedSubsystem {
@@ -128,10 +124,7 @@ where
.map_err(|e| SubsystemError::with_origin("dispute-distribution", e))
.boxed();
SpawnedSubsystem {
name: "dispute-distribution-subsystem",
future,
}
SpawnedSubsystem { name: "dispute-distribution-subsystem", future }
}
}
@@ -155,7 +148,8 @@ where
where
Context: SubsystemContext<Message = DisputeDistributionMessage>
+ overseer::SubsystemContext<Message = DisputeDistributionMessage>
+ Sync + Send,
+ Sync
+ Send,
{
loop {
let message = MuxedMessage::receive(&mut ctx, &mut self.sender_rx).await;
@@ -168,52 +162,43 @@ where
Ok(SignalResult::Continue) => Ok(()),
Err(f) => Err(f),
}
}
},
FromOverseer::Communication { msg } =>
self.handle_subsystem_message(&mut ctx, msg).await,
};
log_error(result, "on FromOverseer")?;
}
},
MuxedMessage::Sender(result) => {
self.disputes_sender.on_task_message(
result.ok_or(Fatal::SenderExhausted)?
)
.await;
}
self.disputes_sender
.on_task_message(result.ok_or(Fatal::SenderExhausted)?)
.await;
},
}
}
}
/// Handle overseer signals.
async fn handle_signals<Context: SubsystemContext> (
async fn handle_signals<Context: SubsystemContext>(
&mut self,
ctx: &mut Context,
signal: OverseerSignal,
) -> Result<SignalResult>
{
) -> Result<SignalResult> {
match signal {
OverseerSignal::Conclude =>
return Ok(SignalResult::Conclude),
OverseerSignal::Conclude => return Ok(SignalResult::Conclude),
OverseerSignal::ActiveLeaves(update) => {
self.disputes_sender.update_leaves(
ctx,
&mut self.runtime,
update
)
.await?;
}
OverseerSignal::BlockFinalized(_,_) => {}
self.disputes_sender.update_leaves(ctx, &mut self.runtime, update).await?;
},
OverseerSignal::BlockFinalized(_, _) => {},
};
Ok(SignalResult::Continue)
}
/// Handle `DisputeDistributionMessage`s.
async fn handle_subsystem_message<Context: SubsystemContext> (
async fn handle_subsystem_message<Context: SubsystemContext>(
&mut self,
ctx: &mut Context,
msg: DisputeDistributionMessage
) -> Result<()>
{
msg: DisputeDistributionMessage,
) -> Result<()> {
match msg {
DisputeDistributionMessage::SendDispute(dispute_msg) =>
self.disputes_sender.start_sender(ctx, &mut self.runtime, dispute_msg).await?,
@@ -223,14 +208,12 @@ where
ctx.sender().clone(),
receiver,
self.authority_discovery.clone(),
self.metrics.clone()
self.metrics.clone(),
);
ctx
.spawn("disputes-receiver", receiver.run().boxed(),)
ctx.spawn("disputes-receiver", receiver.run().boxed())
.map_err(Fatal::SpawnTask)?;
},
}
Ok(())
}
@@ -247,7 +230,8 @@ enum MuxedMessage {
impl MuxedMessage {
async fn receive(
ctx: &mut (impl SubsystemContext<Message = DisputeDistributionMessage> + overseer::SubsystemContext<Message = DisputeDistributionMessage>),
ctx: &mut (impl SubsystemContext<Message = DisputeDistributionMessage>
+ overseer::SubsystemContext<Message = DisputeDistributionMessage>),
from_sender: &mut mpsc::Receiver<TaskFinish>,
) -> Self {
// We are only fusing here to make `select` happy, in reality we will quit if the stream
@@ -14,9 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use polkadot_node_subsystem_util::metrics::prometheus::{Counter, U64, Registry, PrometheusError, CounterVec, Opts};
use polkadot_node_subsystem_util::metrics::prometheus;
use polkadot_node_subsystem_util::metrics;
use polkadot_node_subsystem_util::{
metrics,
metrics::{
prometheus,
prometheus::{Counter, CounterVec, Opts, PrometheusError, Registry, U64},
},
};
/// Label for success counters.
pub const SUCCEEDED: &'static str = "succeeded";
@@ -81,7 +85,7 @@ impl metrics::Metrics for Metrics {
"parachain_dispute_distribution_sent_requests",
"Total number of sent requests.",
),
&["success"]
&["success"],
)?,
registry,
)?,
@@ -98,7 +102,7 @@ impl metrics::Metrics for Metrics {
"parachain_dispute_distribution_imported_requests",
"Total number of imported requests.",
),
&["success"]
&["success"],
)?,
registry,
)?,
@@ -106,4 +110,3 @@ impl metrics::Metrics for Metrics {
Ok(Metrics(Some(metrics)))
}
}
@@ -19,9 +19,8 @@
use thiserror::Error;
use polkadot_node_network_protocol::PeerId;
use polkadot_node_network_protocol::request_response::request::ReceiveError;
use polkadot_node_subsystem_util::{Fault, runtime, unwrap_non_fatal};
use polkadot_node_network_protocol::{request_response::request::ReceiveError, PeerId};
use polkadot_node_subsystem_util::{runtime, unwrap_non_fatal, Fault};
use crate::LOG_TARGET;
@@ -100,9 +99,7 @@ pub type NonFatalResult<T> = std::result::Result<T, NonFatal>;
///
/// We basically always want to try and continue on error. This utility function is meant to
/// consume top-level errors by simply logging them
pub fn log_error(result: Result<()>)
-> std::result::Result<(), Fatal>
{
pub fn log_error(result: Result<()>) -> std::result::Result<(), Fatal> {
if let Some(error) = unwrap_non_fatal(result.map_err(|e| e.0))? {
tracing::warn!(target: LOG_TARGET, error = ?error);
}
@@ -14,47 +14,43 @@
// 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::{
collections::HashSet,
pin::Pin,
task::{Context, Poll},
};
use std::collections::HashSet;
use std::pin::Pin;
use std::task::{Context, Poll};
use futures::FutureExt;
use futures::Stream;
use futures::future::{BoxFuture, poll_fn};
use futures::stream::FusedStream;
use futures::{
channel::{mpsc, oneshot},
future::{poll_fn, BoxFuture},
stream::{FusedStream, FuturesUnordered, StreamExt},
FutureExt, Stream,
};
use lru::LruCache;
use futures::{channel::mpsc, channel::oneshot, stream::StreamExt, stream::FuturesUnordered};
use polkadot_node_network_protocol::{
PeerId,
UnifiedReputationChange as Rep,
authority_discovery::AuthorityDiscovery,
request_response::{
request::{OutgoingResponse, OutgoingResponseSender},
v1::{DisputeRequest, DisputeResponse},
IncomingRequest,
request::OutgoingResponse,
request::OutgoingResponseSender,
v1::DisputeRequest,
v1::DisputeResponse,
},
PeerId, UnifiedReputationChange as Rep,
};
use polkadot_node_primitives::DISPUTE_WINDOW;
use polkadot_node_subsystem_util::{
runtime,
runtime::RuntimeInfo,
};
use polkadot_node_subsystem_util::{runtime, runtime::RuntimeInfo};
use polkadot_subsystem::{
messages::{AllMessages, DisputeCoordinatorMessage, ImportStatementsResult},
SubsystemSender,
messages::{
AllMessages, DisputeCoordinatorMessage, ImportStatementsResult,
},
};
use crate::metrics::{FAILED, SUCCEEDED};
use crate::{LOG_TARGET, Metrics};
use crate::{
metrics::{FAILED, SUCCEEDED},
Metrics, LOG_TARGET,
};
mod error;
use self::error::{log_error, FatalResult, NonFatalResult, NonFatal, Fatal, Result};
use self::error::{log_error, Fatal, FatalResult, NonFatal, NonFatalResult, Result};
const COST_INVALID_REQUEST: Rep = Rep::CostMajor("Received message could not be decoded.");
const COST_INVALID_SIGNATURE: Rep = Rep::Malicious("Signatures were invalid.");
@@ -129,12 +125,13 @@ impl MuxedMessage {
return Poll::Ready(Ok(MuxedMessage::ConfirmedImport(v)))
}
Poll::Pending
}).await
})
.await
}
}
impl<Sender: SubsystemSender, AD> DisputesReceiver<Sender, AD>
where
where
AD: AuthorityDiscovery,
{
/// Create a new receiver which can be `run`.
@@ -167,41 +164,32 @@ where
pub async fn run(mut self) {
loop {
match log_error(self.run_inner().await) {
Ok(()) => {}
Ok(()) => {},
Err(Fatal::RequestChannelFinished) => {
tracing::debug!(
target: LOG_TARGET,
"Incoming request stream exhausted - shutting down?"
);
return
}
Err(err) => {
tracing::warn!(
target: LOG_TARGET,
?err,
"Dispute receiver died."
);
},
Err(err) => {
tracing::warn!(target: LOG_TARGET, ?err, "Dispute receiver died.");
return
}
},
}
}
}
/// Actual work happening here.
async fn run_inner(&mut self) -> Result<()> {
let msg = MuxedMessage::receive(
&mut self.pending_imports,
&mut self.receiver
)
.await?;
let msg = MuxedMessage::receive(&mut self.pending_imports, &mut self.receiver).await?;
let raw = match msg {
// We need to clean up futures, to make sure responses are sent:
MuxedMessage::ConfirmedImport(m_bad) => {
self.ban_bad_peer(m_bad)?;
return Ok(())
}
},
MuxedMessage::NewRequest(req) => req,
};
@@ -211,23 +199,20 @@ where
// Only accept messages from validators:
if self.authority_discovery.get_authority_id_by_peer_id(raw.peer).await.is_none() {
raw.pending_response.send(
sc_network::config::OutgoingResponse {
raw.pending_response
.send(sc_network::config::OutgoingResponse {
result: Err(()),
reputation_changes: vec![COST_NOT_A_VALIDATOR.into_base_rep()],
sent_feedback: None,
}
)
.map_err(|_| NonFatal::SendResponse(peer))?;
})
.map_err(|_| NonFatal::SendResponse(peer))?;
return Err(NonFatal::NotAValidator(peer).into())
}
let incoming = IncomingRequest::<DisputeRequest>::try_from_raw(
raw,
vec![COST_INVALID_REQUEST]
)
.map_err(NonFatal::FromRawRequest)?;
let incoming =
IncomingRequest::<DisputeRequest>::try_from_raw(raw, vec![COST_INVALID_REQUEST])
.map_err(NonFatal::FromRawRequest)?;
// Immediately drop requests from peers that already have requests in flight or have
// been banned recently (flood protection):
@@ -252,54 +237,49 @@ where
}
/// Start importing votes for the given request.
async fn start_import(
&mut self,
incoming: IncomingRequest<DisputeRequest>,
) -> Result<()> {
async fn start_import(&mut self, incoming: IncomingRequest<DisputeRequest>) -> Result<()> {
let IncomingRequest { peer, payload, pending_response } = incoming;
let IncomingRequest {
peer, payload, pending_response,
} = incoming;
let info = self.runtime.get_session_info_by_index(
&mut self.sender,
payload.0.candidate_receipt.descriptor.relay_parent,
payload.0.session_index
)
.await?;
let info = self
.runtime
.get_session_info_by_index(
&mut self.sender,
payload.0.candidate_receipt.descriptor.relay_parent,
payload.0.session_index,
)
.await?;
let votes_result = payload.0.try_into_signed_votes(&info.session_info);
let (candidate_receipt, valid_vote, invalid_vote) = match votes_result {
Err(()) => { // Signature invalid:
pending_response.send_outgoing_response(
OutgoingResponse {
Err(()) => {
// Signature invalid:
pending_response
.send_outgoing_response(OutgoingResponse {
result: Err(()),
reputation_changes: vec![COST_INVALID_SIGNATURE],
sent_feedback: None,
}
)
.map_err(|_| NonFatal::SetPeerReputation(peer))?;
})
.map_err(|_| NonFatal::SetPeerReputation(peer))?;
return Err(From::from(NonFatal::InvalidSignature(peer)))
}
},
Ok(votes) => votes,
};
let (pending_confirmation, confirmation_rx) = oneshot::channel();
let candidate_hash = candidate_receipt.hash();
self.sender.send_message(
AllMessages::DisputeCoordinator(
self.sender
.send_message(AllMessages::DisputeCoordinator(
DisputeCoordinatorMessage::ImportStatements {
candidate_hash,
candidate_receipt,
session: valid_vote.0.session_index(),
statements: vec![valid_vote, invalid_vote],
pending_confirmation,
}
)
)
.await;
},
))
.await;
self.pending_imports.push(peer, confirmation_rx, pending_response);
Ok(())
@@ -310,16 +290,16 @@ where
/// In addition we report import metrics.
fn ban_bad_peer(
&mut self,
result: NonFatalResult<(PeerId, ImportStatementsResult)>
result: NonFatalResult<(PeerId, ImportStatementsResult)>,
) -> NonFatalResult<()> {
match result? {
(_, ImportStatementsResult::ValidImport) => {
(_, ImportStatementsResult::ValidImport) => {
self.metrics.on_imported(SUCCEEDED);
}
},
(bad_peer, ImportStatementsResult::InvalidImport) => {
self.metrics.on_imported(FAILED);
self.banned_peers.put(bad_peer, ());
}
},
}
Ok(())
}
@@ -335,24 +315,22 @@ struct PendingImports {
impl PendingImports {
pub fn new() -> Self {
Self {
futures: FuturesUnordered::new(),
peers: HashSet::new(),
}
Self { futures: FuturesUnordered::new(), peers: HashSet::new() }
}
pub fn push(
&mut self,
peer: PeerId,
handled: oneshot::Receiver<ImportStatementsResult>,
pending_response: OutgoingResponseSender<DisputeRequest>
pending_response: OutgoingResponseSender<DisputeRequest>,
) {
self.peers.insert(peer);
self.futures.push(
async move {
let r = respond_to_request(peer, handled, pending_response).await;
(peer, r)
}.boxed()
}
.boxed(),
)
}
@@ -369,23 +347,19 @@ impl PendingImports {
impl Stream for PendingImports {
type Item = NonFatalResult<(PeerId, ImportStatementsResult)>;
fn poll_next(
mut self: Pin<&mut Self>,
ctx: &mut Context<'_>
) -> Poll<Option<Self::Item>> {
fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
match Pin::new(&mut self.futures).poll_next(ctx) {
Poll::Pending => Poll::Pending,
Poll::Ready(None) => Poll::Ready(None),
Poll::Ready(Some((peer, result))) => {
self.peers.remove(&peer);
Poll::Ready(Some(result.map(|r| (peer,r))))
}
Poll::Ready(Some(result.map(|r| (peer, r))))
},
}
}
}
impl FusedStream for PendingImports {
fn is_terminated(&self) -> bool {
fn is_terminated(&self) -> bool {
self.futures.is_terminated()
}
}
@@ -398,27 +372,21 @@ impl FusedStream for PendingImports {
async fn respond_to_request(
peer: PeerId,
handled: oneshot::Receiver<ImportStatementsResult>,
pending_response: OutgoingResponseSender<DisputeRequest>
pending_response: OutgoingResponseSender<DisputeRequest>,
) -> NonFatalResult<ImportStatementsResult> {
let result = handled
.await
.map_err(|_| NonFatal::ImportCanceled(peer))?
;
let result = handled.await.map_err(|_| NonFatal::ImportCanceled(peer))?;
let response = match result {
ImportStatementsResult::ValidImport =>
OutgoingResponse {
result: Ok(DisputeResponse::Confirmed),
reputation_changes: Vec::new(),
sent_feedback: None,
},
ImportStatementsResult::InvalidImport =>
OutgoingResponse {
result: Err(()),
reputation_changes: vec![COST_INVALID_CANDIDATE],
sent_feedback: None,
},
ImportStatementsResult::ValidImport => OutgoingResponse {
result: Ok(DisputeResponse::Confirmed),
reputation_changes: Vec::new(),
sent_feedback: None,
},
ImportStatementsResult::InvalidImport => OutgoingResponse {
result: Err(()),
reputation_changes: vec![COST_INVALID_CANDIDATE],
sent_feedback: None,
},
};
pending_response
@@ -19,11 +19,9 @@
use thiserror::Error;
use polkadot_node_subsystem_util::{Fault, runtime};
use polkadot_subsystem::SubsystemError;
use polkadot_node_primitives::disputes::DisputeMessageCheckError;
use polkadot_node_subsystem_util::{runtime, Fault};
use polkadot_subsystem::SubsystemError;
#[derive(Debug, Error)]
#[error(transparent)]
@@ -14,8 +14,7 @@
// 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::collections::{HashMap, HashSet, hash_map::Entry};
use std::collections::{hash_map::Entry, HashMap, HashSet};
use futures::channel::{mpsc, oneshot};
@@ -24,11 +23,10 @@ use polkadot_node_primitives::{CandidateVotes, DisputeMessage, SignedDisputeStat
use polkadot_node_subsystem_util::runtime::RuntimeInfo;
use polkadot_primitives::v1::{CandidateHash, DisputeStatement, Hash, SessionIndex};
use polkadot_subsystem::{
messages::{AllMessages, DisputeCoordinatorMessage},
ActiveLeavesUpdate, SubsystemContext,
messages::{AllMessages, DisputeCoordinatorMessage}
};
/// For each ongoing dispute we have a `SendTask` which takes care of it.
///
/// It is going to spawn real tasks as it sees fit for getting the votes of the particular dispute
@@ -39,10 +37,10 @@ pub use send_task::TaskFinish;
/// Error and [`Result`] type for sender
mod error;
pub use error::{Result, Error, Fatal, NonFatal};
pub use error::{Error, Fatal, NonFatal, Result};
use crate::{LOG_TARGET, Metrics};
use self::error::NonFatalResult;
use crate::{Metrics, LOG_TARGET};
/// The `DisputeSender` keeps track of all ongoing disputes we need to send statements out.
///
@@ -68,8 +66,7 @@ pub struct DisputeSender {
metrics: Metrics,
}
impl DisputeSender
{
impl DisputeSender {
/// Create a new `DisputeSender` which can be used to start dispute sendings.
pub fn new(tx: mpsc::Sender<TaskFinish>, metrics: Metrics) -> Self {
Self {
@@ -98,18 +95,13 @@ impl DisputeSender
"Dispute sending already active."
);
return Ok(())
}
},
Entry::Vacant(vacant) => {
let send_task = SendTask::new(
ctx,
runtime,
&self.active_sessions,
self.tx.clone(),
req,
)
.await?;
let send_task =
SendTask::new(ctx, runtime, &self.active_sessions, self.tx.clone(), req)
.await?;
vacant.insert(send_task);
}
},
}
Ok(())
}
@@ -143,9 +135,8 @@ impl DisputeSender
let active_disputes: HashSet<_> = active_disputes.into_iter().map(|(_, c)| c).collect();
// Cleanup obsolete senders:
self.disputes.retain(
|candidate_hash, _| active_disputes.contains(candidate_hash)
);
self.disputes
.retain(|candidate_hash, _| active_disputes.contains(candidate_hash));
for dispute in self.disputes.values_mut() {
if have_new_sessions || dispute.has_failed_sends() {
@@ -162,7 +153,6 @@ impl DisputeSender
/// Receive message from a sending task.
pub async fn on_task_message(&mut self, msg: TaskFinish) {
let TaskFinish { candidate_hash, receiver, result } = msg;
self.metrics.on_sent_request(result.as_metrics_label());
@@ -176,7 +166,7 @@ impl DisputeSender
"Received `FromSendingTask::Finished` for non existing dispute."
);
return
}
},
Some(task) => task,
};
task.on_finished_send(&receiver, result);
@@ -194,7 +184,9 @@ impl DisputeSender
let (session_index, candidate_hash) = dispute;
// We need some relay chain head for context for receiving session info information:
let ref_head = self.active_sessions.values().next().ok_or(NonFatal::NoActiveHeads)?;
let info = runtime.get_session_info_by_index(ctx.sender(), *ref_head, session_index).await?;
let info = runtime
.get_session_info_by_index(ctx.sender(), *ref_head, session_index)
.await?;
let our_index = match info.validator_info.our_index {
None => {
tracing::trace!(
@@ -202,7 +194,7 @@ impl DisputeSender
"Not a validator in that session - not starting dispute sending."
);
return Ok(())
}
},
Some(index) => index,
};
@@ -215,39 +207,25 @@ impl DisputeSender
"No votes for active dispute?! - possible, due to race."
);
return Ok(())
}
},
Some(votes) => votes,
};
let our_valid_vote = votes
.valid
.iter()
.find(|(_, i, _)| *i == our_index);
let our_valid_vote = votes.valid.iter().find(|(_, i, _)| *i == our_index);
let our_invalid_vote = votes
.invalid
.iter()
.find(|(_, i, _)| *i == our_index);
let our_invalid_vote = votes.invalid.iter().find(|(_, i, _)| *i == our_index);
let (valid_vote, invalid_vote) =
if let Some(our_valid_vote) = our_valid_vote {
// Get some invalid vote as well:
let invalid_vote = votes
.invalid
.get(0)
.ok_or(NonFatal::MissingVotesFromCoordinator)?;
(our_valid_vote, invalid_vote)
} else if let Some(our_invalid_vote) = our_invalid_vote {
// Get some valid vote as well:
let valid_vote = votes
.valid
.get(0)
.ok_or(NonFatal::MissingVotesFromCoordinator)?;
(valid_vote, our_invalid_vote)
} else {
return Err(From::from(NonFatal::MissingVotesFromCoordinator))
}
;
let (valid_vote, invalid_vote) = if let Some(our_valid_vote) = our_valid_vote {
// Get some invalid vote as well:
let invalid_vote = votes.invalid.get(0).ok_or(NonFatal::MissingVotesFromCoordinator)?;
(our_valid_vote, invalid_vote)
} else if let Some(our_invalid_vote) = our_invalid_vote {
// Get some valid vote as well:
let valid_vote = votes.valid.get(0).ok_or(NonFatal::MissingVotesFromCoordinator)?;
(valid_vote, our_invalid_vote)
} else {
return Err(From::from(NonFatal::MissingVotesFromCoordinator))
};
let (kind, valid_index, signature) = valid_vote;
let valid_public = info
.session_info
@@ -290,7 +268,7 @@ impl DisputeSender
invalid_signed,
*invalid_index,
votes.candidate_receipt,
&info.session_info
&info.session_info,
)
.map_err(NonFatal::InvalidDisputeFromCoordinator)?;
@@ -333,12 +311,13 @@ async fn get_active_session_indeces<Context: SubsystemContext>(
}
/// Retrieve Set of active disputes from the dispute coordinator.
async fn get_active_disputes<Context: SubsystemContext>(ctx: &mut Context)
-> NonFatalResult<Vec<(SessionIndex, CandidateHash)>> {
async fn get_active_disputes<Context: SubsystemContext>(
ctx: &mut Context,
) -> NonFatalResult<Vec<(SessionIndex, CandidateHash)>> {
let (tx, rx) = oneshot::channel();
ctx.send_message(AllMessages::DisputeCoordinator(
DisputeCoordinatorMessage::ActiveDisputes(tx)
))
ctx.send_message(AllMessages::DisputeCoordinator(DisputeCoordinatorMessage::ActiveDisputes(
tx,
)))
.await;
rx.await.map_err(|_| NonFatal::AskActiveDisputesCanceled)
}
@@ -351,10 +330,7 @@ async fn get_candidate_votes<Context: SubsystemContext>(
) -> NonFatalResult<Option<CandidateVotes>> {
let (tx, rx) = oneshot::channel();
ctx.send_message(AllMessages::DisputeCoordinator(
DisputeCoordinatorMessage::QueryCandidateVotes(
vec![(session_index, candidate_hash)],
tx
)
DisputeCoordinatorMessage::QueryCandidateVotes(vec![(session_index, candidate_hash)], tx),
))
.await;
rx.await
@@ -14,37 +14,32 @@
// 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::collections::{HashMap, HashSet};
use std::collections::HashMap;
use std::collections::HashSet;
use futures::Future;
use futures::FutureExt;
use futures::SinkExt;
use futures::channel::mpsc;
use futures::future::RemoteHandle;
use futures::{channel::mpsc, future::RemoteHandle, Future, FutureExt, SinkExt};
use polkadot_node_network_protocol::{
IfDisconnected,
request_response::{
OutgoingRequest, OutgoingResult, Recipient, Requests,
v1::{DisputeRequest, DisputeResponse},
}
OutgoingRequest, OutgoingResult, Recipient, Requests,
},
IfDisconnected,
};
use polkadot_node_subsystem_util::runtime::RuntimeInfo;
use polkadot_primitives::v1::{
AuthorityDiscoveryId, CandidateHash, Hash, SessionIndex, ValidatorIndex,
};
use polkadot_subsystem::{
SubsystemContext,
messages::{AllMessages, NetworkBridgeMessage},
SubsystemContext,
};
use super::error::{Fatal, Result};
use crate::LOG_TARGET;
use crate::metrics::FAILED;
use crate::metrics::SUCCEEDED;
use crate::{
metrics::{FAILED, SUCCEEDED},
LOG_TARGET,
};
/// Delivery status for a particular dispute.
///
@@ -104,27 +99,18 @@ impl TaskResult {
}
}
impl SendTask
{
impl SendTask {
/// Initiates sending a dispute message to peers.
pub async fn new<Context: SubsystemContext>(
ctx: &mut Context,
runtime: &mut RuntimeInfo,
active_sessions: &HashMap<SessionIndex,Hash>,
active_sessions: &HashMap<SessionIndex, Hash>,
tx: mpsc::Sender<TaskFinish>,
request: DisputeRequest,
) -> Result<Self> {
let mut send_task = Self {
request,
deliveries: HashMap::new(),
has_failed_sends: false,
tx,
};
send_task.refresh_sends(
ctx,
runtime,
active_sessions,
).await?;
let mut send_task =
Self { request, deliveries: HashMap::new(), has_failed_sends: false, tx };
send_task.refresh_sends(ctx, runtime, active_sessions).await?;
Ok(send_task)
}
@@ -150,12 +136,8 @@ impl SendTask
self.deliveries.retain(|k, _| new_authorities.contains(k));
// Start any new tasks that are needed:
let new_statuses = send_requests(
ctx,
self.tx.clone(),
add_authorities,
self.request.clone(),
).await?;
let new_statuses =
send_requests(ctx, self.tx.clone(), add_authorities, self.request.clone()).await?;
self.deliveries.extend(new_statuses.into_iter());
self.has_failed_sends = false;
@@ -180,7 +162,7 @@ impl SendTask
self.has_failed_sends = true;
// Remove state, so we know what to try again:
self.deliveries.remove(authority);
}
},
TaskResult::Succeeded => {
let status = match self.deliveries.get_mut(&authority) {
None => {
@@ -194,16 +176,15 @@ impl SendTask
"Received `FromSendingTask::Finished` for non existing task."
);
return
}
},
Some(status) => status,
};
// We are done here:
*status = DeliveryStatus::Succeeded;
}
},
}
}
/// Determine all validators that should receive the given dispute requests.
///
/// This is all parachain validators of the session the candidate occurred and all authorities
@@ -232,7 +213,8 @@ impl SendTask
// Current authorities:
for (session_index, head) in active_sessions.iter() {
let info = runtime.get_session_info_by_index(ctx.sender(), *head, *session_index).await?;
let info =
runtime.get_session_info_by_index(ctx.sender(), *head, *session_index).await?;
let session_info = &info.session_info;
let new_set = session_info
.discovery_keys
@@ -246,7 +228,6 @@ impl SendTask
}
}
/// Start sending of the given message to all given authorities.
///
/// And spawn tasks for handling the response.
@@ -260,10 +241,8 @@ async fn send_requests<Context: SubsystemContext>(
let mut reqs = Vec::with_capacity(receivers.len());
for receiver in receivers {
let (outgoing, pending_response) = OutgoingRequest::new(
Recipient::Authority(receiver.clone()),
req.clone(),
);
let (outgoing, pending_response) =
OutgoingRequest::new(Recipient::Authority(receiver.clone()), req.clone());
reqs.push(Requests::DisputeSending(outgoing));
@@ -275,8 +254,7 @@ async fn send_requests<Context: SubsystemContext>(
);
let (remote, remote_handle) = fut.remote_handle();
ctx.spawn("dispute-sender", remote.boxed())
.map_err(Fatal::SpawnTask)?;
ctx.spawn("dispute-sender", remote.boxed()).map_err(Fatal::SpawnTask)?;
statuses.insert(receiver, DeliveryStatus::Pending(remote_handle));
}
@@ -306,8 +284,8 @@ async fn wait_response_task(
%err,
"Error sending dispute statements to node."
);
TaskFinish { candidate_hash, receiver, result: TaskResult::Failed}
}
TaskFinish { candidate_hash, receiver, result: TaskResult::Failed }
},
Ok(DisputeResponse::Confirmed) => {
tracing::trace!(
target: LOG_TARGET,
@@ -316,7 +294,7 @@ async fn wait_response_task(
"Sending dispute message succeeded"
);
TaskFinish { candidate_hash, receiver, result: TaskResult::Succeeded }
}
},
};
if let Err(err) = tx.feed(msg).await {
tracing::debug!(
@@ -22,16 +22,16 @@ use std::{collections::HashMap, sync::Arc};
use async_trait::async_trait;
use lazy_static::lazy_static;
use polkadot_node_network_protocol::{PeerId, authority_discovery::AuthorityDiscovery};
use polkadot_node_network_protocol::{authority_discovery::AuthorityDiscovery, PeerId};
use sc_keystore::LocalKeystore;
use sp_application_crypto::AppKey;
use sp_keyring::{Sr25519Keyring};
use sp_keyring::Sr25519Keyring;
use sp_keystore::{SyncCryptoStore, SyncCryptoStorePtr};
use polkadot_node_primitives::{DisputeMessage, SignedDisputeStatement};
use polkadot_primitives::v1::{
CandidateDescriptor, CandidateHash, CandidateReceipt, Hash,
SessionIndex, SessionInfo, ValidatorId, ValidatorIndex, AuthorityDiscoveryId,
AuthorityDiscoveryId, CandidateDescriptor, CandidateHash, CandidateReceipt, Hash, SessionIndex,
SessionInfo, ValidatorId, ValidatorIndex,
};
pub const MOCK_SESSION_INDEX: SessionIndex = 1;
@@ -45,22 +45,19 @@ pub const MOCK_VALIDATORS: [Sr25519Keyring; 6] = [
Sr25519Keyring::Eve,
];
pub const MOCK_AUTHORITIES_NEXT_SESSION: [Sr25519Keyring;2] = [
Sr25519Keyring::One,
Sr25519Keyring::Two,
];
pub const MOCK_AUTHORITIES_NEXT_SESSION: [Sr25519Keyring; 2] =
[Sr25519Keyring::One, Sr25519Keyring::Two];
pub const FERDIE_INDEX: ValidatorIndex = ValidatorIndex(0);
pub const ALICE_INDEX: ValidatorIndex = ValidatorIndex(1);
lazy_static! {
/// Mocked `AuthorityDiscovery` service.
pub static ref MOCK_AUTHORITY_DISCOVERY: MockAuthorityDiscovery = MockAuthorityDiscovery::new();
// Creating an innocent looking `SessionInfo` is really expensive in a debug build. Around
// 700ms on my machine, We therefore cache those keys here:
pub static ref MOCK_VALIDATORS_DISCOVERY_KEYS: HashMap<Sr25519Keyring, AuthorityDiscoveryId> =
pub static ref MOCK_VALIDATORS_DISCOVERY_KEYS: HashMap<Sr25519Keyring, AuthorityDiscoveryId> =
MOCK_VALIDATORS
.iter()
.chain(MOCK_AUTHORITIES_NEXT_SESSION.iter())
@@ -92,13 +89,9 @@ pub static ref MOCK_NEXT_SESSION_INFO: SessionInfo =
};
}
pub fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt {
CandidateReceipt {
descriptor: CandidateDescriptor {
relay_parent,
..Default::default()
},
descriptor: CandidateDescriptor { relay_parent, ..Default::default() },
commitments_hash: Hash::random(),
}
}
@@ -106,15 +99,11 @@ pub fn make_candidate_receipt(relay_parent: Hash) -> CandidateReceipt {
pub async fn make_explicit_signed(
validator: Sr25519Keyring,
candidate_hash: CandidateHash,
valid: bool
valid: bool,
) -> SignedDisputeStatement {
let keystore: SyncCryptoStorePtr = Arc::new(LocalKeystore::in_memory());
SyncCryptoStore::sr25519_generate_new(
&*keystore,
ValidatorId::ID,
Some(&validator.to_seed()),
)
.expect("Insert key into keystore");
SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, Some(&validator.to_seed()))
.expect("Insert key into keystore");
SignedDisputeStatement::sign_explicit(
&keystore,
@@ -128,17 +117,18 @@ pub async fn make_explicit_signed(
.expect("Signing should work.")
}
pub async fn make_dispute_message(
candidate: CandidateReceipt,
valid_validator: ValidatorIndex,
invalid_validator: ValidatorIndex,
) -> DisputeMessage {
let candidate_hash = candidate.hash();
let valid_vote =
make_explicit_signed(MOCK_VALIDATORS[valid_validator.0 as usize], candidate_hash, true).await;
let valid_vote =
make_explicit_signed(MOCK_VALIDATORS[valid_validator.0 as usize], candidate_hash, true)
.await;
let invalid_vote =
make_explicit_signed(MOCK_VALIDATORS[invalid_validator.0 as usize], candidate_hash, false).await;
make_explicit_signed(MOCK_VALIDATORS[invalid_validator.0 as usize], candidate_hash, false)
.await;
DisputeMessage::from_signed_statements(
valid_vote,
valid_validator,
@@ -153,7 +143,7 @@ pub async fn make_dispute_message(
/// Dummy `AuthorityDiscovery` service.
#[derive(Debug, Clone)]
pub struct MockAuthorityDiscovery {
peer_ids: HashMap<Sr25519Keyring, PeerId>
peer_ids: HashMap<Sr25519Keyring, PeerId>,
}
impl MockAuthorityDiscovery {
@@ -178,13 +168,17 @@ impl MockAuthorityDiscovery {
#[async_trait]
impl AuthorityDiscovery for MockAuthorityDiscovery {
async fn get_addresses_by_authority_id(&mut self, _authority: polkadot_primitives::v1::AuthorityDiscoveryId)
-> Option<Vec<sc_network::Multiaddr>> {
panic!("Not implemented");
async fn get_addresses_by_authority_id(
&mut self,
_authority: polkadot_primitives::v1::AuthorityDiscoveryId,
) -> Option<Vec<sc_network::Multiaddr>> {
panic!("Not implemented");
}
async fn get_authority_id_by_peer_id(&mut self, peer_id: polkadot_node_network_protocol::PeerId)
-> Option<polkadot_primitives::v1::AuthorityDiscoveryId> {
async fn get_authority_id_by_peer_id(
&mut self,
peer_id: polkadot_node_network_protocol::PeerId,
) -> Option<polkadot_primitives::v1::AuthorityDiscoveryId> {
for (a, p) in self.peer_ids.iter() {
if p == &peer_id {
return Some(MOCK_VALIDATORS_DISCOVERY_KEYS.get(&a).unwrap().clone())
@@ -17,451 +17,448 @@
//! Subsystem unit tests
use std::collections::HashSet;
use std::sync::Arc;
use std::task::Poll;
use std::time::Duration;
use std::{collections::HashSet, sync::Arc, task::Poll, time::Duration};
use assert_matches::assert_matches;
use futures::{
channel::{oneshot, mpsc},
channel::{mpsc, oneshot},
future::poll_fn,
pin_mut,
SinkExt, Future
pin_mut, Future, SinkExt,
};
use futures_timer::Delay;
use parity_scale_codec::{Encode, Decode};
use parity_scale_codec::{Decode, Encode};
use polkadot_node_network_protocol::PeerId;
use polkadot_node_network_protocol::request_response::v1::DisputeRequest;
use polkadot_node_network_protocol::{request_response::v1::DisputeRequest, PeerId};
use sp_keyring::Sr25519Keyring;
use polkadot_node_network_protocol::{IfDisconnected, request_response::{Recipient, Requests, v1::DisputeResponse}};
use polkadot_node_network_protocol::{
request_response::{v1::DisputeResponse, Recipient, Requests},
IfDisconnected,
};
use polkadot_node_primitives::{CandidateVotes, UncheckedDisputeMessage};
use polkadot_primitives::v1::{AuthorityDiscoveryId, CandidateHash, Hash, SessionIndex, SessionInfo};
use polkadot_subsystem::messages::{DisputeCoordinatorMessage, ImportStatementsResult};
use polkadot_primitives::v1::{
AuthorityDiscoveryId, CandidateHash, Hash, SessionIndex, SessionInfo,
};
use polkadot_subsystem::{
ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, LeafStatus, OverseerSignal, Span,
messages::{
AllMessages, DisputeDistributionMessage, NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest
AllMessages, DisputeCoordinatorMessage, DisputeDistributionMessage, ImportStatementsResult,
NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest,
},
ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, LeafStatus, OverseerSignal, Span,
};
use polkadot_subsystem_testhelpers::{
mock::make_ferdie_keystore, subsystem_test_harness, TestSubsystemContextHandle,
};
use polkadot_subsystem_testhelpers::{TestSubsystemContextHandle, mock::make_ferdie_keystore, subsystem_test_harness};
use crate::{DisputeDistributionSubsystem, LOG_TARGET, Metrics};
use self::mock::{
ALICE_INDEX, FERDIE_INDEX, make_candidate_receipt, make_dispute_message,
MOCK_AUTHORITY_DISCOVERY, MOCK_SESSION_INDEX, MOCK_SESSION_INFO, MOCK_NEXT_SESSION_INDEX,
MOCK_NEXT_SESSION_INFO, FERDIE_DISCOVERY_KEY,
make_candidate_receipt, make_dispute_message, ALICE_INDEX, FERDIE_DISCOVERY_KEY, FERDIE_INDEX,
MOCK_AUTHORITY_DISCOVERY, MOCK_NEXT_SESSION_INDEX, MOCK_NEXT_SESSION_INFO, MOCK_SESSION_INDEX,
MOCK_SESSION_INFO,
};
use crate::{DisputeDistributionSubsystem, Metrics, LOG_TARGET};
/// Useful mock providers.
pub mod mock;
#[test]
fn send_dispute_sends_dispute() {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>|
async move {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>| async move {
let (_, _) = handle_subsystem_startup(&mut handle, None).await;
let (_, _) = handle_subsystem_startup(&mut handle, None).await;
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message = make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX).await;
handle
.send(FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone()),
})
.await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message =
make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX,).await;
handle.send(
FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone())
}
).await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, true).await;
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, true).await;
conclude(&mut handle).await;
conclude(&mut handle).await;
};
test_harness(test);
}
#[test]
fn received_request_triggers_import() {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>|
async move {
let (_, mut req_tx) = handle_subsystem_startup(&mut handle, None).await;
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>| async move {
let (_, mut req_tx) = handle_subsystem_startup(&mut handle, None).await;
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message =
make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX,).await;
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message = make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX).await;
// Non validator request should get dropped:
// Non validator request should get dropped:
let rx_response =
send_network_dispute_request(&mut req_tx, PeerId::random(), message.clone().into())
.await;
assert_matches!(
rx_response.await,
Ok(resp) => {
let sc_network::config::OutgoingResponse {
result: _,
reputation_changes,
sent_feedback: _,
} = resp;
// Peer should get punished:
assert_eq!(reputation_changes.len(), 1);
}
);
// Nested valid and invalid import.
//
// Nested requests from same peer should get dropped. For the invalid request even
// subsequent requests should get dropped.
nested_network_dispute_request(
&mut handle,
&mut req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Alice),
message.clone().into(),
ImportStatementsResult::InvalidImport,
true,
move |handle, req_tx, message| {
nested_network_dispute_request(
handle,
req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone().into(),
ImportStatementsResult::ValidImport,
false,
move |_, req_tx, message| async move {
// Another request from Alice should get dropped (request already in
// flight):
{
let rx_response = send_network_dispute_request(
req_tx,
MOCK_AUTHORITY_DISCOVERY
.get_peer_id_by_authority(Sr25519Keyring::Alice),
message.clone(),
)
.await;
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - other request already in flight"
);
}
);
}
// Another request from Bob should get dropped (request already in
// flight):
{
let rx_response = send_network_dispute_request(
req_tx,
MOCK_AUTHORITY_DISCOVERY
.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone(),
)
.await;
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - other request already in flight"
);
}
);
}
},
)
},
)
.await;
// Subsequent sends from Alice should fail (peer is banned):
{
let rx_response = send_network_dispute_request(
&mut req_tx,
PeerId::random(),
message.clone().into()
).await;
assert_matches!(
rx_response.await,
Ok(resp) => {
let sc_network::config::OutgoingResponse {
result: _,
reputation_changes,
sent_feedback: _,
} = resp;
// Peer should get punished:
assert_eq!(reputation_changes.len(), 1);
}
);
// Nested valid and invalid import.
//
// Nested requests from same peer should get dropped. For the invalid request even
// subsequent requests should get dropped.
nested_network_dispute_request(
&mut handle,
&mut req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Alice),
message.clone().into(),
ImportStatementsResult::InvalidImport,
true,
move |handle, req_tx, message|
nested_network_dispute_request(
handle,
req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone().into(),
ImportStatementsResult::ValidImport,
false,
move |_, req_tx, message| async move {
// Another request from Alice should get dropped (request already in
// flight):
{
let rx_response = send_network_dispute_request(
req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Alice),
message.clone(),
).await;
)
.await;
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - other request already in flight"
);
}
);
}
// Another request from Bob should get dropped (request already in
// flight):
{
let rx_response = send_network_dispute_request(
req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone(),
).await;
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - peer is banned."
);
}
);
}
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - other request already in flight"
);
}
);
}
}
)
).await;
// But should work fine for Bob:
nested_network_dispute_request(
&mut handle,
&mut req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone().into(),
ImportStatementsResult::ValidImport,
false,
|_, _, _| async {},
)
.await;
// Subsequent sends from Alice should fail (peer is banned):
{
let rx_response = send_network_dispute_request(
&mut req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Alice),
message.clone().into()
).await;
assert_matches!(
rx_response.await,
Err(err) => {
tracing::trace!(
target: LOG_TARGET,
?err,
"Request got dropped - peer is banned."
);
}
);
}
// But should work fine for Bob:
nested_network_dispute_request(
&mut handle,
&mut req_tx,
MOCK_AUTHORITY_DISCOVERY.get_peer_id_by_authority(Sr25519Keyring::Bob),
message.clone().into(),
ImportStatementsResult::ValidImport,
false,
|_, _, _| async {}
).await;
tracing::trace!(target: LOG_TARGET, "Concluding.");
conclude(&mut handle).await;
tracing::trace!(target: LOG_TARGET, "Concluding.");
conclude(&mut handle).await;
};
test_harness(test);
}
#[test]
fn disputes_are_recovered_at_startup() {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>|
async move {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>| async move {
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let (_, _) = handle_subsystem_startup(&mut handle, Some(candidate.hash())).await;
let (_, _) = handle_subsystem_startup(&mut handle, Some(candidate.hash())).await;
let message = make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX).await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::DisputeCoordinator(
DisputeCoordinatorMessage::QueryCandidateVotes(
query,
tx,
)
) => {
let (session_index, candidate_hash) = query.get(0).unwrap().clone();
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(candidate_hash, candidate.hash());
let unchecked: UncheckedDisputeMessage = message.into();
tx.send(vec![(session_index, candidate_hash, CandidateVotes {
candidate_receipt: candidate,
valid: vec![(
unchecked.valid_vote.kind,
unchecked.valid_vote.validator_index,
unchecked.valid_vote.signature
)],
invalid: vec![(
unchecked.invalid_vote.kind,
unchecked.invalid_vote.validator_index,
unchecked.invalid_vote.signature
)],
})])
.expect("Receiver should stay alive.");
}
);
let message =
make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX,).await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::DisputeCoordinator(
DisputeCoordinatorMessage::QueryCandidateVotes(
query,
tx,
)
) => {
let (session_index, candidate_hash) = query.get(0).unwrap().clone();
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(candidate_hash, candidate.hash());
let unchecked: UncheckedDisputeMessage = message.into();
tx.send(vec![(session_index, candidate_hash, CandidateVotes {
candidate_receipt: candidate,
valid: vec![(
unchecked.valid_vote.kind,
unchecked.valid_vote.validator_index,
unchecked.valid_vote.signature
)],
invalid: vec![(
unchecked.invalid_vote.kind,
unchecked.invalid_vote.validator_index,
unchecked.invalid_vote.signature
)],
})])
.expect("Receiver should stay alive.");
}
);
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, true).await;
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, true).await;
conclude(&mut handle).await;
conclude(&mut handle).await;
};
test_harness(test);
}
#[test]
fn send_dispute_gets_cleaned_up() {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>|
async move {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>| async move {
let (old_head, _) = handle_subsystem_startup(&mut handle, None).await;
let (old_head, _) = handle_subsystem_startup(&mut handle, None).await;
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message = make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX).await;
handle
.send(FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone()),
})
.await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message =
make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX,).await;
handle.send(
FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone())
}
).await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, false).await;
let expected_receivers = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
check_sent_requests(&mut handle, expected_receivers, false).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
activate_leaf(
&mut handle,
Hash::random(),
Some(old_head),
MOCK_SESSION_INDEX,
None,
// No disputes any more:
Vec::new(),
)
.await;
activate_leaf(
&mut handle,
Hash::random(),
Some(old_head),
MOCK_SESSION_INDEX,
None,
// No disputes any more:
Vec::new(),
).await;
// Yield, so subsystem can make progess:
Delay::new(Duration::from_millis(2)).await;
// Yield, so subsystem can make progess:
Delay::new(Duration::from_millis(2)).await;
conclude(&mut handle).await;
conclude(&mut handle).await;
};
test_harness(test);
}
#[test]
fn dispute_retries_and_works_across_session_boundaries() {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>|
async move {
let test = |mut handle: TestSubsystemContextHandle<DisputeDistributionMessage>| async move {
let (old_head, _) = handle_subsystem_startup(&mut handle, None).await;
let (old_head, _) = handle_subsystem_startup(&mut handle, None).await;
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message = make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX).await;
handle
.send(FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone()),
})
.await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let relay_parent = Hash::random();
let candidate = make_candidate_receipt(relay_parent);
let message =
make_dispute_message(candidate.clone(), ALICE_INDEX, FERDIE_INDEX,).await;
handle.send(
FromOverseer::Communication {
msg: DisputeDistributionMessage::SendDispute(message.clone())
}
).await;
// Requests needed session info:
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(
hash,
RuntimeApiRequest::SessionInfo(session_index, tx)
)
) => {
assert_eq!(session_index, MOCK_SESSION_INDEX);
assert_eq!(
hash,
message.candidate_receipt().descriptor.relay_parent
);
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
);
let expected_receivers: HashSet<_> = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
// Requests don't get confirmed - dispute is carried over to next session.
check_sent_requests(&mut handle, expected_receivers.clone(), false).await;
let expected_receivers: HashSet<_> = {
let info = &MOCK_SESSION_INFO;
info.discovery_keys
.clone()
.into_iter()
.filter(|a| a != &Sr25519Keyring::Ferdie.public().into())
.collect()
// All validators are also authorities in the first session, so we are
// done here.
};
// Requests don't get confirmed - dispute is carried over to next session.
check_sent_requests(&mut handle, expected_receivers.clone(), false).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
// Trigger retry:
let old_head2 = Hash::random();
activate_leaf(
&mut handle,
old_head2,
Some(old_head),
MOCK_SESSION_INDEX,
None,
vec![(MOCK_SESSION_INDEX, candidate.hash())],
)
.await;
// Trigger retry:
let old_head2 = Hash::random();
activate_leaf(
&mut handle,
old_head2,
Some(old_head),
MOCK_SESSION_INDEX,
None,
vec![(MOCK_SESSION_INDEX, candidate.hash())]
).await;
check_sent_requests(&mut handle, expected_receivers.clone(), false).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
check_sent_requests(&mut handle, expected_receivers.clone(), false).await;
// Give tasks a chance to finish:
Delay::new(Duration::from_millis(20)).await;
// Session change:
activate_leaf(
&mut handle,
Hash::random(),
Some(old_head2),
MOCK_NEXT_SESSION_INDEX,
Some(MOCK_NEXT_SESSION_INFO.clone()),
vec![(MOCK_SESSION_INDEX, candidate.hash())],
)
.await;
// Session change:
activate_leaf(
&mut handle,
Hash::random(),
Some(old_head2),
MOCK_NEXT_SESSION_INDEX,
Some(MOCK_NEXT_SESSION_INFO.clone()),
vec![(MOCK_SESSION_INDEX, candidate.hash())]
).await;
let expected_receivers = {
let validator_count = MOCK_SESSION_INFO.validators.len();
let old_validators = MOCK_SESSION_INFO
.discovery_keys
.clone()
.into_iter()
.take(validator_count)
.filter(|a| *a != *FERDIE_DISCOVERY_KEY);
let expected_receivers = {
let validator_count = MOCK_SESSION_INFO.validators.len();
let old_validators = MOCK_SESSION_INFO
.discovery_keys
.clone()
.into_iter()
.take(validator_count)
.filter(|a| *a != *FERDIE_DISCOVERY_KEY);
MOCK_NEXT_SESSION_INFO
.discovery_keys
.clone()
.into_iter()
.filter(|a| *a != *FERDIE_DISCOVERY_KEY)
.chain(old_validators)
.collect()
};
check_sent_requests(&mut handle, expected_receivers, true).await;
MOCK_NEXT_SESSION_INFO
.discovery_keys
.clone()
.into_iter()
.filter(|a| *a != *FERDIE_DISCOVERY_KEY)
.chain(old_validators)
.collect()
};
check_sent_requests(&mut handle, expected_receivers, true).await;
conclude(&mut handle).await;
conclude(&mut handle).await;
};
test_harness(test);
}
@@ -472,11 +469,8 @@ async fn send_network_dispute_request(
message: DisputeRequest,
) -> oneshot::Receiver<sc_network::config::OutgoingResponse> {
let (pending_response, rx_response) = oneshot::channel();
let req = sc_network::config::IncomingRequest {
peer,
payload: message.encode(),
pending_response,
};
let req =
sc_network::config::IncomingRequest { peer, payload: message.encode(), pending_response };
req_tx.feed(req).await.unwrap();
rx_response
}
@@ -492,30 +486,27 @@ async fn nested_network_dispute_request<'a, F, O>(
import_result: ImportStatementsResult,
need_session_info: bool,
inner: F,
)
where
F: FnOnce(
&'a mut TestSubsystemContextHandle<DisputeDistributionMessage>,
&'a mut mpsc::Sender<sc_network::config::IncomingRequest>,
DisputeRequest,
) -> O + 'a,
O: Future<Output = ()> + 'a
) where
F: FnOnce(
&'a mut TestSubsystemContextHandle<DisputeDistributionMessage>,
&'a mut mpsc::Sender<sc_network::config::IncomingRequest>,
DisputeRequest,
) -> O
+ 'a,
O: Future<Output = ()> + 'a,
{
let rx_response = send_network_dispute_request(
req_tx,
peer,
message.clone().into()
).await;
let rx_response = send_network_dispute_request(req_tx, peer, message.clone().into()).await;
if need_session_info {
// Subsystem might need `SessionInfo` for determining indices:
match handle.recv().await {
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_,
RuntimeApiRequest::SessionInfo(_, tx)
_,
RuntimeApiRequest::SessionInfo(_, tx),
)) => {
tx.send(Ok(Some(MOCK_SESSION_INFO.clone()))).expect("Receiver should stay alive.");
}
tx.send(Ok(Some(MOCK_SESSION_INFO.clone())))
.expect("Receiver should stay alive.");
},
unexpected => panic!("Unexpected message {:?}", unexpected),
}
}
@@ -580,21 +571,16 @@ async fn nested_network_dispute_request<'a, F, O>(
);
}
async fn conclude(
handle: &mut TestSubsystemContextHandle<DisputeDistributionMessage>,
) {
async fn conclude(handle: &mut TestSubsystemContextHandle<DisputeDistributionMessage>) {
// No more messages should be in the queue:
poll_fn(|ctx| {
let fut = handle.recv();
pin_mut!(fut);
// No requests should be inititated, as there is no longer any dispute active:
assert_matches!(
fut.poll(ctx),
Poll::Pending,
"No requests expected"
);
assert_matches!(fut.poll(ctx), Poll::Pending, "No requests expected");
Poll::Ready(())
}).await;
})
.await;
handle.send(FromOverseer::Signal(OverseerSignal::Conclude)).await;
}
@@ -612,19 +598,17 @@ async fn activate_leaf(
active_disputes: Vec<(SessionIndex, CandidateHash)>,
) {
let has_active_disputes = !active_disputes.is_empty();
handle.send(FromOverseer::Signal(
OverseerSignal::ActiveLeaves(
ActiveLeavesUpdate {
activated: Some(ActivatedLeaf {
hash: activate,
number: 10,
status: LeafStatus::Fresh,
span: Arc::new(Span::Disabled),
}),
deactivated: deactivate.into_iter().collect(),
}
)))
.await;
handle
.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate {
activated: Some(ActivatedLeaf {
hash: activate,
number: 10,
status: LeafStatus::Fresh,
span: Arc::new(Span::Disabled),
}),
deactivated: deactivate.into_iter().collect(),
})))
.await;
assert_matches!(
handle.recv().await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
@@ -667,10 +651,7 @@ async fn check_sent_requests(
confirm_receive: bool,
) {
let expected_receivers: HashSet<_> =
expected_receivers
.into_iter()
.map(Recipient::Authority)
.collect();
expected_receivers.into_iter().map(Recipient::Authority).collect();
// Sends to concerned validators:
assert_matches!(
@@ -710,11 +691,11 @@ async fn handle_subsystem_startup(
ongoing_dispute: Option<CandidateHash>,
) -> (Hash, mpsc::Sender<sc_network::config::IncomingRequest>) {
let (request_tx, request_rx) = mpsc::channel(5);
handle.send(
FromOverseer::Communication {
handle
.send(FromOverseer::Communication {
msg: DisputeDistributionMessage::DisputeSendingReceiver(request_rx),
}
).await;
})
.await;
let relay_parent = Hash::random();
activate_leaf(
@@ -723,19 +704,19 @@ async fn handle_subsystem_startup(
None,
MOCK_SESSION_INDEX,
Some(MOCK_SESSION_INFO.clone()),
ongoing_dispute.into_iter().map(|c| (MOCK_SESSION_INDEX, c)).collect()
).await;
ongoing_dispute.into_iter().map(|c| (MOCK_SESSION_INDEX, c)).collect(),
)
.await;
(relay_parent, request_tx)
}
/// Launch subsystem and provided test function
///
/// which simulates the overseer.
fn test_harness<TestFn, Fut>(test: TestFn)
where
TestFn: FnOnce(TestSubsystemContextHandle<DisputeDistributionMessage>) -> Fut,
Fut: Future<Output = ()>
Fut: Future<Output = ()>,
{
sp_tracing::try_init_simple();
let keystore = make_ferdie_keystore();
@@ -743,7 +724,7 @@ where
let subsystem = DisputeDistributionSubsystem::new(
keystore,
MOCK_AUTHORITY_DISCOVERY.clone(),
Metrics::new_dummy()
Metrics::new_dummy(),
);
let subsystem = |ctx| async {
@@ -755,9 +736,8 @@ where
?fatal,
"Dispute distribution exited with fatal error."
);
}
},
}
};
subsystem_test_harness(test, subsystem);
}