mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 11:41:04 +00:00
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:
File diff suppressed because it is too large
Load Diff
@@ -14,17 +14,17 @@
|
||||
// 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::time::Duration;
|
||||
use futures::{future, Future, executor};
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
use futures::{executor, future, Future};
|
||||
use polkadot_node_network_protocol::{view, ObservedRole};
|
||||
use polkadot_node_primitives::approval::{
|
||||
AssignmentCertKind, VRFOutput, VRFProof, RELAY_VRF_MODULO_CONTEXT,
|
||||
};
|
||||
use polkadot_node_subsystem::messages::{AllMessages, ApprovalCheckError};
|
||||
use polkadot_node_subsystem_test_helpers as test_helpers;
|
||||
use polkadot_node_subsystem_util::TimeoutExt as _;
|
||||
use polkadot_node_network_protocol::{view, ObservedRole};
|
||||
use polkadot_node_primitives::approval::{
|
||||
AssignmentCertKind, RELAY_VRF_MODULO_CONTEXT, VRFOutput, VRFProof,
|
||||
};
|
||||
use super::*;
|
||||
use std::time::Duration;
|
||||
|
||||
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ApprovalDistributionMessage>;
|
||||
|
||||
@@ -34,10 +34,7 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
) -> State {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.filter(
|
||||
Some(LOG_TARGET),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(Some(LOG_TARGET), log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
@@ -52,14 +49,17 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::Conclude))
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Conclude send timeout");
|
||||
}, subsystem));
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::Conclude))
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Conclude send timeout");
|
||||
},
|
||||
subsystem,
|
||||
));
|
||||
}
|
||||
|
||||
state
|
||||
@@ -67,10 +67,7 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
async fn overseer_send(
|
||||
overseer: &mut VirtualOverseer,
|
||||
msg: ApprovalDistributionMessage,
|
||||
) {
|
||||
async fn overseer_send(overseer: &mut VirtualOverseer, msg: ApprovalDistributionMessage) {
|
||||
tracing::trace!(msg = ?msg, "Sending message");
|
||||
overseer
|
||||
.send(FromOverseer::Communication { msg })
|
||||
@@ -79,14 +76,8 @@ async fn overseer_send(
|
||||
.expect("msg send timeout");
|
||||
}
|
||||
|
||||
async fn overseer_signal_block_finalized(
|
||||
overseer: &mut VirtualOverseer,
|
||||
number: BlockNumber,
|
||||
) {
|
||||
tracing::trace!(
|
||||
?number,
|
||||
"Sending a finalized signal",
|
||||
);
|
||||
async fn overseer_signal_block_finalized(overseer: &mut VirtualOverseer, number: BlockNumber) {
|
||||
tracing::trace!(?number, "Sending a finalized signal",);
|
||||
// we don't care about the block hash
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::BlockFinalized(Hash::zero(), number)))
|
||||
@@ -95,15 +86,9 @@ async fn overseer_signal_block_finalized(
|
||||
.expect("signal send timeout");
|
||||
}
|
||||
|
||||
async fn overseer_recv(
|
||||
overseer: &mut VirtualOverseer,
|
||||
) -> AllMessages {
|
||||
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
||||
tracing::trace!("Waiting for a message");
|
||||
let msg = overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("msg recv timeout");
|
||||
let msg = overseer.recv().timeout(TIMEOUT).await.expect("msg recv timeout");
|
||||
|
||||
tracing::trace!(msg = ?msg, "Received message");
|
||||
|
||||
@@ -117,16 +102,21 @@ async fn setup_peer_with_view(
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(peer_id.clone(), ObservedRole::Full, None)
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer_id.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer_id.clone(), view)
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer_id.clone(),
|
||||
view,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
async fn send_message_from_peer(
|
||||
@@ -136,16 +126,15 @@ async fn send_message_from_peer(
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerMessage(peer_id.clone(), msg)
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
|
||||
peer_id.clone(),
|
||||
msg,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
fn fake_assignment_cert(
|
||||
block_hash: Hash,
|
||||
validator: ValidatorIndex,
|
||||
) -> IndirectAssignmentCert {
|
||||
fn fake_assignment_cert(block_hash: Hash, validator: ValidatorIndex) -> IndirectAssignmentCert {
|
||||
let ctx = schnorrkel::signing_context(RELAY_VRF_MODULO_CONTEXT);
|
||||
let msg = b"WhenParachains?";
|
||||
let mut prng = rand_core::OsRng;
|
||||
@@ -157,11 +146,9 @@ fn fake_assignment_cert(
|
||||
block_hash,
|
||||
validator,
|
||||
cert: AssignmentCert {
|
||||
kind: AssignmentCertKind::RelayVRFModulo {
|
||||
sample: 1,
|
||||
},
|
||||
kind: AssignmentCertKind::RelayVRFModulo { sample: 1 },
|
||||
vrf: (VRFOutput(out), VRFProof(proof)),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,7 +171,6 @@ async fn expect_reputation_change(
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/// import an assignment
|
||||
/// connect a new peer
|
||||
/// the new peer sends us the same assignment
|
||||
@@ -263,13 +249,7 @@ fn try_import_the_same_assignment() {
|
||||
expect_reputation_change(overseer, &peer_d, COST_UNEXPECTED_MESSAGE).await;
|
||||
expect_reputation_change(overseer, &peer_d, BENEFIT_VALID_MESSAGE).await;
|
||||
|
||||
assert!(overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.is_none(),
|
||||
"no message should be sent",
|
||||
);
|
||||
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent",);
|
||||
virtual_overseer
|
||||
});
|
||||
}
|
||||
@@ -311,7 +291,8 @@ fn spam_attack_results_in_negative_reputation_change() {
|
||||
let validator_index = ValidatorIndex(candidate_index as u32);
|
||||
let cert = fake_assignment_cert(hash_b, validator_index);
|
||||
(cert, candidate_index as u32)
|
||||
}).collect();
|
||||
})
|
||||
.collect();
|
||||
|
||||
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
|
||||
send_message_from_peer(overseer, peer, msg.clone()).await;
|
||||
@@ -338,10 +319,12 @@ fn spam_attack_results_in_negative_reputation_change() {
|
||||
// send a view update that removes block B from peer's view by bumping the finalized_number
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), View::with_finalized(2))
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
View::with_finalized(2),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
// send the assignments again
|
||||
send_message_from_peer(overseer, peer, msg.clone()).await;
|
||||
@@ -355,7 +338,6 @@ fn spam_attack_results_in_negative_reputation_change() {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/// Imagine we send a message to peer A and peer B.
|
||||
/// Upon receiving them, they both will try to send the message each other.
|
||||
/// This test makes sure they will not punish each other for such duplicate messages.
|
||||
@@ -389,16 +371,19 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() {
|
||||
let cert = fake_assignment_cert(hash, validator_index);
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index)
|
||||
).await;
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
||||
)
|
||||
.await;
|
||||
|
||||
// update peer view to include the hash
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), view![hash])
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
view![hash],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
// we should send them the assignment
|
||||
assert_matches!(
|
||||
@@ -420,13 +405,7 @@ fn peer_sending_us_the_same_we_just_sent_them_is_ok() {
|
||||
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
|
||||
send_message_from_peer(overseer, peer, msg.clone()).await;
|
||||
|
||||
assert!(overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.is_none(),
|
||||
"we should not punish the peer",
|
||||
);
|
||||
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "we should not punish the peer",);
|
||||
|
||||
// send the assignments again
|
||||
send_message_from_peer(overseer, peer, msg).await;
|
||||
@@ -469,8 +448,9 @@ fn import_approval_happy_path() {
|
||||
let cert = fake_assignment_cert(hash, validator_index);
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index)
|
||||
).await;
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(overseer).await,
|
||||
@@ -716,15 +696,9 @@ fn update_peer_view() {
|
||||
let cert_a = fake_assignment_cert(hash_a, ValidatorIndex(0));
|
||||
let cert_b = fake_assignment_cert(hash_b, ValidatorIndex(0));
|
||||
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert_a, 0)
|
||||
).await;
|
||||
overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_a, 0)).await;
|
||||
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert_b, 0)
|
||||
).await;
|
||||
overseer_send(overseer, ApprovalDistributionMessage::DistributeAssignment(cert_b, 0)).await;
|
||||
|
||||
// connect a peer
|
||||
setup_peer_with_view(overseer, peer, view![hash_a]).await;
|
||||
@@ -747,7 +721,8 @@ fn update_peer_view() {
|
||||
|
||||
assert_eq!(state.peer_views.get(peer).map(|v| v.finalized_number), Some(0));
|
||||
assert_eq!(
|
||||
state.blocks
|
||||
state
|
||||
.blocks
|
||||
.get(&hash_a)
|
||||
.unwrap()
|
||||
.known_by
|
||||
@@ -764,17 +739,20 @@ fn update_peer_view() {
|
||||
// update peer's view
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), View::new(vec![hash_b, hash_c, hash_d], 2))
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
View::new(vec![hash_b, hash_c, hash_d], 2),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
let cert_c = fake_assignment_cert(hash_c, ValidatorIndex(0));
|
||||
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert_c.clone(), 0)
|
||||
).await;
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert_c.clone(), 0),
|
||||
)
|
||||
.await;
|
||||
|
||||
// we should send relevant assignments to the peer
|
||||
assert_matches!(
|
||||
@@ -795,7 +773,8 @@ fn update_peer_view() {
|
||||
|
||||
assert_eq!(state.peer_views.get(peer).map(|v| v.finalized_number), Some(2));
|
||||
assert_eq!(
|
||||
state.blocks
|
||||
state
|
||||
.blocks
|
||||
.get(&hash_c)
|
||||
.unwrap()
|
||||
.known_by
|
||||
@@ -813,22 +792,17 @@ fn update_peer_view() {
|
||||
// update peer's view
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), View::with_finalized(finalized_number))
|
||||
)
|
||||
).await;
|
||||
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
View::with_finalized(finalized_number),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
virtual_overseer
|
||||
});
|
||||
|
||||
assert_eq!(state.peer_views.get(peer).map(|v| v.finalized_number), Some(finalized_number));
|
||||
assert!(
|
||||
state.blocks
|
||||
.get(&hash_c)
|
||||
.unwrap()
|
||||
.known_by
|
||||
.get(peer)
|
||||
.is_none()
|
||||
);
|
||||
assert!(state.blocks.get(&hash_c).unwrap().known_by.get(peer).is_none());
|
||||
}
|
||||
|
||||
/// E.g. if someone copies the keys...
|
||||
@@ -882,16 +856,11 @@ fn import_remotely_then_locally() {
|
||||
// import the same assignment locally
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index)
|
||||
).await;
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.is_none(),
|
||||
"no message should be sent",
|
||||
);
|
||||
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent",);
|
||||
|
||||
// send the approval remotely
|
||||
let approval = IndirectSignedApprovalVote {
|
||||
@@ -916,18 +885,9 @@ fn import_remotely_then_locally() {
|
||||
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
|
||||
|
||||
// import the same approval locally
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeApproval(approval)
|
||||
).await;
|
||||
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await;
|
||||
|
||||
assert!(overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.is_none(),
|
||||
"no message should be sent",
|
||||
);
|
||||
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent",);
|
||||
virtual_overseer
|
||||
});
|
||||
}
|
||||
@@ -967,13 +927,12 @@ fn sends_assignments_even_when_state_is_approved() {
|
||||
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index)
|
||||
).await;
|
||||
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
overseer,
|
||||
ApprovalDistributionMessage::DistributeApproval(approval.clone()),
|
||||
).await;
|
||||
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
|
||||
.await;
|
||||
|
||||
// connect the peer.
|
||||
setup_peer_with_view(overseer, peer, view![hash]).await;
|
||||
@@ -1007,13 +966,7 @@ fn sends_assignments_even_when_state_is_approved() {
|
||||
}
|
||||
);
|
||||
|
||||
assert!(overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.is_none(),
|
||||
"no message should be sent",
|
||||
);
|
||||
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent",);
|
||||
virtual_overseer
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ use thiserror::Error;
|
||||
|
||||
use futures::channel::oneshot;
|
||||
|
||||
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;
|
||||
@@ -113,9 +113,7 @@ pub type Result<T> = std::result::Result<T, Error>;
|
||||
///
|
||||
/// 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);
|
||||
}
|
||||
|
||||
@@ -19,15 +19,13 @@ use futures::{future::Either, FutureExt, StreamExt, TryFutureExt};
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
|
||||
use polkadot_subsystem::{
|
||||
messages::AvailabilityDistributionMessage, FromOverseer, OverseerSignal, SpawnedSubsystem,
|
||||
SubsystemContext, SubsystemError,
|
||||
overseer,
|
||||
messages::AvailabilityDistributionMessage, overseer, FromOverseer, OverseerSignal,
|
||||
SpawnedSubsystem, SubsystemContext, SubsystemError,
|
||||
};
|
||||
|
||||
/// Error and [`Result`] type for this subsystem.
|
||||
mod error;
|
||||
use error::Fatal;
|
||||
use error::{Result, log_error};
|
||||
use error::{log_error, Fatal, Result};
|
||||
|
||||
use polkadot_node_subsystem_util::runtime::RuntimeInfo;
|
||||
|
||||
@@ -70,19 +68,15 @@ where
|
||||
.map_err(|e| SubsystemError::with_origin("availability-distribution", e))
|
||||
.boxed();
|
||||
|
||||
SpawnedSubsystem {
|
||||
name: "availability-distribution-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "availability-distribution-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
impl AvailabilityDistributionSubsystem {
|
||||
|
||||
/// Create a new instance of the availability distribution.
|
||||
pub fn new(keystore: SyncCryptoStorePtr, metrics: Metrics) -> Self {
|
||||
let runtime = RuntimeInfo::new(Some(keystore));
|
||||
Self { runtime, metrics }
|
||||
Self { runtime, metrics }
|
||||
}
|
||||
|
||||
/// Start processing work as passed on from the Overseer.
|
||||
@@ -103,44 +97,41 @@ impl AvailabilityDistributionSubsystem {
|
||||
|
||||
// Handle task messages sending:
|
||||
let message = match action {
|
||||
Either::Left(subsystem_msg) => {
|
||||
subsystem_msg.map_err(|e| Fatal::IncomingMessageChannel(e))?
|
||||
}
|
||||
Either::Left(subsystem_msg) =>
|
||||
subsystem_msg.map_err(|e| Fatal::IncomingMessageChannel(e))?,
|
||||
Either::Right(from_task) => {
|
||||
let from_task = from_task.ok_or(Fatal::RequesterExhausted)?;
|
||||
ctx.send_message(from_task).await;
|
||||
continue;
|
||||
}
|
||||
continue
|
||||
},
|
||||
};
|
||||
match message {
|
||||
FromOverseer::Signal(OverseerSignal::ActiveLeaves(update)) => {
|
||||
log_error(
|
||||
requester.get_mut().update_fetching_heads(&mut ctx, &mut self.runtime, update).await,
|
||||
"Error in Requester::update_fetching_heads"
|
||||
requester
|
||||
.get_mut()
|
||||
.update_fetching_heads(&mut ctx, &mut self.runtime, update)
|
||||
.await,
|
||||
"Error in Requester::update_fetching_heads",
|
||||
)?;
|
||||
}
|
||||
FromOverseer::Signal(OverseerSignal::BlockFinalized(..)) => {}
|
||||
FromOverseer::Signal(OverseerSignal::Conclude) => {
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
FromOverseer::Signal(OverseerSignal::BlockFinalized(..)) => {},
|
||||
FromOverseer::Signal(OverseerSignal::Conclude) => return Ok(()),
|
||||
FromOverseer::Communication {
|
||||
msg: AvailabilityDistributionMessage::ChunkFetchingRequest(req),
|
||||
} => {
|
||||
answer_chunk_request_log(&mut ctx, req, &self.metrics).await
|
||||
}
|
||||
} => answer_chunk_request_log(&mut ctx, req, &self.metrics).await,
|
||||
FromOverseer::Communication {
|
||||
msg: AvailabilityDistributionMessage::PoVFetchingRequest(req),
|
||||
} => {
|
||||
answer_pov_request_log(&mut ctx, req, &self.metrics).await
|
||||
}
|
||||
} => answer_pov_request_log(&mut ctx, req, &self.metrics).await,
|
||||
FromOverseer::Communication {
|
||||
msg: AvailabilityDistributionMessage::FetchPoV {
|
||||
relay_parent,
|
||||
from_validator,
|
||||
candidate_hash,
|
||||
pov_hash,
|
||||
tx,
|
||||
},
|
||||
msg:
|
||||
AvailabilityDistributionMessage::FetchPoV {
|
||||
relay_parent,
|
||||
from_validator,
|
||||
candidate_hash,
|
||||
pov_hash,
|
||||
tx,
|
||||
},
|
||||
} => {
|
||||
log_error(
|
||||
pov_requester::fetch_pov(
|
||||
@@ -151,10 +142,11 @@ impl AvailabilityDistributionSubsystem {
|
||||
candidate_hash,
|
||||
pov_hash,
|
||||
tx,
|
||||
).await,
|
||||
"pov_requester::fetch_pov"
|
||||
)
|
||||
.await,
|
||||
"pov_requester::fetch_pov",
|
||||
)?;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
@@ -31,7 +35,6 @@ pub const NOT_FOUND: &'static str = "not-found";
|
||||
#[derive(Clone, Default)]
|
||||
pub struct Metrics(Option<MetricsInner>);
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MetricsInner {
|
||||
/// Number of chunks fetched.
|
||||
@@ -137,4 +140,3 @@ impl metrics::Metrics for Metrics {
|
||||
Ok(Metrics(Some(metrics)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,23 +16,26 @@
|
||||
|
||||
//! PoV requester takes care of requesting PoVs from validators of a backing group.
|
||||
|
||||
use futures::{FutureExt, channel::oneshot, future::BoxFuture};
|
||||
use futures::{channel::oneshot, future::BoxFuture, FutureExt};
|
||||
|
||||
use polkadot_subsystem::jaeger;
|
||||
use polkadot_node_network_protocol::request_response::{OutgoingRequest, Recipient, request::{RequestError, Requests},
|
||||
v1::{PoVFetchingRequest, PoVFetchingResponse}};
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateHash, Hash, ValidatorIndex,
|
||||
use polkadot_node_network_protocol::request_response::{
|
||||
request::{RequestError, Requests},
|
||||
v1::{PoVFetchingRequest, PoVFetchingResponse},
|
||||
OutgoingRequest, Recipient,
|
||||
};
|
||||
use polkadot_node_primitives::PoV;
|
||||
use polkadot_subsystem::{
|
||||
SubsystemContext,
|
||||
messages::{NetworkBridgeMessage, IfDisconnected}
|
||||
};
|
||||
use polkadot_node_subsystem_util::runtime::RuntimeInfo;
|
||||
use polkadot_primitives::v1::{CandidateHash, Hash, ValidatorIndex};
|
||||
use polkadot_subsystem::{
|
||||
jaeger,
|
||||
messages::{IfDisconnected, NetworkBridgeMessage},
|
||||
SubsystemContext,
|
||||
};
|
||||
|
||||
use crate::error::{Fatal, NonFatal};
|
||||
use crate::LOG_TARGET;
|
||||
use crate::{
|
||||
error::{Fatal, NonFatal},
|
||||
LOG_TARGET,
|
||||
};
|
||||
|
||||
/// Start background worker for taking care of fetching the requested `PoV` from the network.
|
||||
pub async fn fetch_pov<Context>(
|
||||
@@ -42,32 +45,31 @@ pub async fn fetch_pov<Context>(
|
||||
from_validator: ValidatorIndex,
|
||||
candidate_hash: CandidateHash,
|
||||
pov_hash: Hash,
|
||||
tx: oneshot::Sender<PoV>
|
||||
tx: oneshot::Sender<PoV>,
|
||||
) -> super::Result<()>
|
||||
where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
let info = &runtime.get_session_info(ctx.sender(), parent).await?.session_info;
|
||||
let authority_id = info.discovery_keys.get(from_validator.0 as usize)
|
||||
let authority_id = info
|
||||
.discovery_keys
|
||||
.get(from_validator.0 as usize)
|
||||
.ok_or(NonFatal::InvalidValidatorIndex)?
|
||||
.clone();
|
||||
let (req, pending_response) = OutgoingRequest::new(
|
||||
Recipient::Authority(authority_id),
|
||||
PoVFetchingRequest {
|
||||
candidate_hash,
|
||||
},
|
||||
PoVFetchingRequest { candidate_hash },
|
||||
);
|
||||
let full_req = Requests::PoVFetching(req);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendRequests(
|
||||
vec![full_req],
|
||||
// We are supposed to be connected to validators of our group via `PeerSet`,
|
||||
// but at session boundaries that is kind of racy, in case a connection takes
|
||||
// longer to get established, so we try to connect in any case.
|
||||
IfDisconnected::TryConnect
|
||||
)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::SendRequests(
|
||||
vec![full_req],
|
||||
// We are supposed to be connected to validators of our group via `PeerSet`,
|
||||
// but at session boundaries that is kind of racy, in case a connection takes
|
||||
// longer to get established, so we try to connect in any case.
|
||||
IfDisconnected::TryConnect,
|
||||
))
|
||||
.await;
|
||||
|
||||
let span = jaeger::Span::new(candidate_hash, "fetch-pov")
|
||||
.with_validator_index(from_validator)
|
||||
@@ -85,11 +87,7 @@ async fn fetch_pov_job(
|
||||
tx: oneshot::Sender<PoV>,
|
||||
) {
|
||||
if let Err(err) = do_fetch_pov(pov_hash, pending_response, span, tx).await {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"fetch_pov_job"
|
||||
);
|
||||
tracing::warn!(target: LOG_TARGET, ?err, "fetch_pov_job");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,15 +97,11 @@ async fn do_fetch_pov(
|
||||
pending_response: BoxFuture<'static, Result<PoVFetchingResponse, RequestError>>,
|
||||
_span: jaeger::Span,
|
||||
tx: oneshot::Sender<PoV>,
|
||||
)
|
||||
-> std::result::Result<(), NonFatal>
|
||||
{
|
||||
) -> std::result::Result<(), NonFatal> {
|
||||
let response = pending_response.await.map_err(NonFatal::FetchPoV)?;
|
||||
let pov = match response {
|
||||
PoVFetchingResponse::PoV(pov) => pov,
|
||||
PoVFetchingResponse::NoSuchPoV => {
|
||||
return Err(NonFatal::NoSuchPoV)
|
||||
}
|
||||
PoVFetchingResponse::NoSuchPoV => return Err(NonFatal::NoSuchPoV),
|
||||
};
|
||||
if pov.hash() == pov_hash {
|
||||
tx.send(pov).map_err(|_| NonFatal::SendResponse)
|
||||
@@ -124,38 +118,37 @@ mod tests {
|
||||
use parity_scale_codec::Encode;
|
||||
use sp_core::testing::TaskExecutor;
|
||||
|
||||
use polkadot_primitives::v1::{CandidateHash, Hash, ValidatorIndex};
|
||||
use polkadot_node_primitives::BlockData;
|
||||
use polkadot_primitives::v1::{CandidateHash, Hash, ValidatorIndex};
|
||||
use polkadot_subsystem::messages::{
|
||||
AllMessages, AvailabilityDistributionMessage, RuntimeApiMessage, RuntimeApiRequest,
|
||||
};
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
use polkadot_subsystem::messages::{AvailabilityDistributionMessage, RuntimeApiMessage, RuntimeApiRequest, AllMessages};
|
||||
use test_helpers::mock::make_ferdie_keystore;
|
||||
|
||||
use super::*;
|
||||
use crate::LOG_TARGET;
|
||||
use crate::tests::mock::{make_session_info};
|
||||
use crate::{tests::mock::make_session_info, LOG_TARGET};
|
||||
|
||||
#[test]
|
||||
fn rejects_invalid_pov() {
|
||||
sp_tracing::try_init_simple();
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![1,2,3,4,5,6]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![1, 2, 3, 4, 5, 6]) };
|
||||
test_run(Hash::default(), pov);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accepts_valid_pov() {
|
||||
sp_tracing::try_init_simple();
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![1,2,3,4,5,6]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![1, 2, 3, 4, 5, 6]) };
|
||||
test_run(pov.hash(), pov);
|
||||
}
|
||||
|
||||
fn test_run(pov_hash: Hash, pov: PoV) {
|
||||
let pool = TaskExecutor::new();
|
||||
let (mut context, mut virtual_overseer) =
|
||||
test_helpers::make_subsystem_context::<AvailabilityDistributionMessage, TaskExecutor>(pool.clone());
|
||||
let (mut context, mut virtual_overseer) = test_helpers::make_subsystem_context::<
|
||||
AvailabilityDistributionMessage,
|
||||
TaskExecutor,
|
||||
>(pool.clone());
|
||||
let keystore = make_ferdie_keystore();
|
||||
let mut runtime = polkadot_node_subsystem_util::runtime::RuntimeInfo::new(Some(keystore));
|
||||
|
||||
@@ -169,34 +162,33 @@ mod tests {
|
||||
CandidateHash::default(),
|
||||
pov_hash,
|
||||
tx,
|
||||
).await.expect("Should succeed");
|
||||
)
|
||||
.await
|
||||
.expect("Should succeed");
|
||||
};
|
||||
|
||||
let tester = async move {
|
||||
loop {
|
||||
match virtual_overseer.recv().await {
|
||||
AllMessages::RuntimeApi(
|
||||
RuntimeApiMessage::Request(
|
||||
_,
|
||||
RuntimeApiRequest::SessionIndexForChild(tx)
|
||||
)
|
||||
) => {
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
_,
|
||||
RuntimeApiRequest::SessionIndexForChild(tx),
|
||||
)) => {
|
||||
tx.send(Ok(0)).unwrap();
|
||||
}
|
||||
AllMessages::RuntimeApi(
|
||||
RuntimeApiMessage::Request(
|
||||
_,
|
||||
RuntimeApiRequest::SessionInfo(_, tx)
|
||||
)
|
||||
) => {
|
||||
},
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
_,
|
||||
RuntimeApiRequest::SessionInfo(_, tx),
|
||||
)) => {
|
||||
tx.send(Ok(Some(make_session_info()))).unwrap();
|
||||
}
|
||||
},
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests(mut reqs, _)) => {
|
||||
let req = assert_matches!(
|
||||
reqs.pop(),
|
||||
Some(Requests::PoVFetching(outgoing)) => {outgoing}
|
||||
);
|
||||
req.pending_response.send(Ok(PoVFetchingResponse::PoV(pov.clone()).encode()))
|
||||
req.pending_response
|
||||
.send(Ok(PoVFetchingResponse::PoV(pov.clone()).encode()))
|
||||
.unwrap();
|
||||
break
|
||||
},
|
||||
|
||||
@@ -16,28 +16,33 @@
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::channel::oneshot;
|
||||
use futures::future::select;
|
||||
use futures::{FutureExt, SinkExt};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
future::select,
|
||||
FutureExt, SinkExt,
|
||||
};
|
||||
|
||||
use polkadot_erasure_coding::branch_hash;
|
||||
use polkadot_node_network_protocol::request_response::{
|
||||
request::{OutgoingRequest, RequestError, Requests, Recipient},
|
||||
request::{OutgoingRequest, Recipient, RequestError, Requests},
|
||||
v1::{ChunkFetchingRequest, ChunkFetchingResponse},
|
||||
};
|
||||
use polkadot_primitives::v1::{AuthorityDiscoveryId, BlakeTwo256, CandidateHash, GroupIndex, Hash, HashT, OccupiedCore, SessionIndex};
|
||||
use polkadot_node_primitives::ErasureChunk;
|
||||
use polkadot_subsystem::messages::{
|
||||
AllMessages, AvailabilityStoreMessage, NetworkBridgeMessage, IfDisconnected,
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, BlakeTwo256, CandidateHash, GroupIndex, Hash, HashT, OccupiedCore,
|
||||
SessionIndex,
|
||||
};
|
||||
use polkadot_subsystem::{
|
||||
jaeger,
|
||||
messages::{AllMessages, AvailabilityStoreMessage, IfDisconnected, NetworkBridgeMessage},
|
||||
SubsystemContext,
|
||||
};
|
||||
use polkadot_subsystem::{SubsystemContext, jaeger};
|
||||
|
||||
use crate::{
|
||||
error::{Fatal, Result},
|
||||
metrics::{Metrics, FAILED, SUCCEEDED},
|
||||
requester::session_cache::{BadValidators, SessionInfo},
|
||||
LOG_TARGET,
|
||||
metrics::{Metrics, SUCCEEDED, FAILED},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -140,10 +145,7 @@ impl FetchTaskConfig {
|
||||
|
||||
// Don't run tasks for our backing group:
|
||||
if session_info.our_group == Some(core.group_responsible) {
|
||||
return FetchTaskConfig {
|
||||
live_in,
|
||||
prepared_running: None,
|
||||
};
|
||||
return FetchTaskConfig { live_in, prepared_running: None }
|
||||
}
|
||||
|
||||
let span = jaeger::Span::new(core.candidate_hash, "availability-distribution")
|
||||
@@ -165,10 +167,7 @@ impl FetchTaskConfig {
|
||||
sender,
|
||||
span,
|
||||
};
|
||||
FetchTaskConfig {
|
||||
live_in,
|
||||
prepared_running: Some(prepared_running),
|
||||
}
|
||||
FetchTaskConfig { live_in, prepared_running: Some(prepared_running) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -180,10 +179,7 @@ impl FetchTask {
|
||||
where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
let FetchTaskConfig {
|
||||
prepared_running,
|
||||
live_in,
|
||||
} = config;
|
||||
let FetchTaskConfig { prepared_running, live_in } = config;
|
||||
|
||||
if let Some(running) = prepared_running {
|
||||
let (handle, kill) = oneshot::channel();
|
||||
@@ -191,15 +187,9 @@ impl FetchTask {
|
||||
ctx.spawn("chunk-fetcher", running.run(kill).boxed())
|
||||
.map_err(|e| Fatal::SpawnTask(e))?;
|
||||
|
||||
Ok(FetchTask {
|
||||
live_in,
|
||||
state: FetchedState::Started(handle),
|
||||
})
|
||||
Ok(FetchTask { live_in, state: FetchedState::Started(handle) })
|
||||
} else {
|
||||
Ok(FetchTask {
|
||||
live_in,
|
||||
state: FetchedState::Canceled,
|
||||
})
|
||||
Ok(FetchTask { live_in, state: FetchedState::Canceled })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +251,9 @@ impl RunningTask {
|
||||
let mut bad_validators = Vec::new();
|
||||
let mut succeeded = false;
|
||||
let mut count: u32 = 0;
|
||||
let mut _span = self.span.child("fetch-task")
|
||||
let mut _span = self
|
||||
.span
|
||||
.child("fetch-task")
|
||||
.with_chunk_index(self.request.index.0)
|
||||
.with_relay_parent(self.relay_parent);
|
||||
// Try validators in reverse order:
|
||||
@@ -271,7 +263,7 @@ impl RunningTask {
|
||||
if count > 0 {
|
||||
self.metrics.on_retry();
|
||||
}
|
||||
count +=1;
|
||||
count += 1;
|
||||
|
||||
// Send request:
|
||||
let resp = match self.do_request(&validator).await {
|
||||
@@ -283,16 +275,14 @@ impl RunningTask {
|
||||
);
|
||||
self.metrics.on_fetch(FAILED);
|
||||
return
|
||||
}
|
||||
},
|
||||
Err(TaskError::PeerError) => {
|
||||
bad_validators.push(validator);
|
||||
continue
|
||||
}
|
||||
},
|
||||
};
|
||||
let chunk = match resp {
|
||||
ChunkFetchingResponse::Chunk(resp) => {
|
||||
resp.recombine_into_chunk(&self.request)
|
||||
}
|
||||
ChunkFetchingResponse::Chunk(resp) => resp.recombine_into_chunk(&self.request),
|
||||
ChunkFetchingResponse::NoSuchChunk => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -301,20 +291,20 @@ impl RunningTask {
|
||||
);
|
||||
bad_validators.push(validator);
|
||||
continue
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Data genuine?
|
||||
if !self.validate_chunk(&validator, &chunk) {
|
||||
bad_validators.push(validator);
|
||||
continue;
|
||||
continue
|
||||
}
|
||||
|
||||
// Ok, let's store it and be happy:
|
||||
self.store_chunk(chunk).await;
|
||||
succeeded = true;
|
||||
_span.add_string_tag("success", "true");
|
||||
break;
|
||||
break
|
||||
}
|
||||
_span.add_int_tag("tries", count as _);
|
||||
if succeeded {
|
||||
@@ -337,7 +327,7 @@ impl RunningTask {
|
||||
|
||||
self.sender
|
||||
.send(FromFetchTask::Message(AllMessages::NetworkBridge(
|
||||
NetworkBridgeMessage::SendRequests(vec![requests], IfDisconnected::TryConnect)
|
||||
NetworkBridgeMessage::SendRequests(vec![requests], IfDisconnected::TryConnect),
|
||||
)))
|
||||
.await
|
||||
.map_err(|_| TaskError::ShuttingDown)?;
|
||||
@@ -352,7 +342,7 @@ impl RunningTask {
|
||||
"Peer sent us invalid erasure chunk data"
|
||||
);
|
||||
Err(TaskError::PeerError)
|
||||
}
|
||||
},
|
||||
Err(RequestError::NetworkError(err)) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
@@ -361,13 +351,13 @@ impl RunningTask {
|
||||
"Some network error occurred when fetching erasure chunk"
|
||||
);
|
||||
Err(TaskError::PeerError)
|
||||
}
|
||||
},
|
||||
Err(RequestError::Canceled(oneshot::Canceled)) => {
|
||||
tracing::warn!(target: LOG_TARGET,
|
||||
origin= ?validator,
|
||||
"Erasure chunk request got canceled");
|
||||
Err(TaskError::PeerError)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,13 +373,13 @@ impl RunningTask {
|
||||
error = ?e,
|
||||
"Failed to calculate chunk merkle proof",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return false
|
||||
},
|
||||
};
|
||||
let erasure_chunk_hash = BlakeTwo256::hash(&chunk.chunk);
|
||||
if anticipated_hash != erasure_chunk_hash {
|
||||
tracing::warn!(target: LOG_TARGET, origin = ?validator, "Received chunk does not match merkle tree");
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
true
|
||||
}
|
||||
@@ -437,12 +427,9 @@ impl RunningTask {
|
||||
}
|
||||
|
||||
async fn conclude_fail(&mut self) {
|
||||
if let Err(err) = self.sender.send(FromFetchTask::Failed(self.request.candidate_hash)).await {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"Sending `Failed` message for task failed"
|
||||
);
|
||||
if let Err(err) = self.sender.send(FromFetchTask::Failed(self.request.candidate_hash)).await
|
||||
{
|
||||
tracing::warn!(target: LOG_TARGET, ?err, "Sending `Failed` message for task failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,24 +16,24 @@
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
use parity_scale_codec::Encode;
|
||||
|
||||
use futures::channel::{mpsc, oneshot};
|
||||
use futures::{executor, Future, FutureExt, StreamExt, select};
|
||||
use futures::task::{Poll, Context, noop_waker};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
executor, select,
|
||||
task::{noop_waker, Context, Poll},
|
||||
Future, FutureExt, StreamExt,
|
||||
};
|
||||
|
||||
use sc_network as network;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
use polkadot_primitives::v1::{CandidateHash, ValidatorIndex};
|
||||
use polkadot_node_network_protocol::request_response::{v1, Recipient};
|
||||
use polkadot_node_primitives::{BlockData, PoV};
|
||||
use polkadot_node_network_protocol::request_response::v1;
|
||||
use polkadot_node_network_protocol::request_response::Recipient;
|
||||
use polkadot_primitives::v1::{CandidateHash, ValidatorIndex};
|
||||
|
||||
use crate::metrics::Metrics;
|
||||
use crate::tests::mock::get_valid_chunk_data;
|
||||
use super::*;
|
||||
use crate::{metrics::Metrics, tests::mock::get_valid_chunk_data};
|
||||
|
||||
#[test]
|
||||
fn task_can_be_canceled() {
|
||||
@@ -54,16 +54,14 @@ fn task_does_not_accept_invalid_chunk() {
|
||||
let validators = vec![Sr25519Keyring::Alice.public().into()];
|
||||
task.group = validators;
|
||||
let test = TestRun {
|
||||
chunk_responses: {
|
||||
chunk_responses: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Alice.public().into()),
|
||||
ChunkFetchingResponse::Chunk(
|
||||
v1::ChunkResponse {
|
||||
chunk: vec![1,2,3],
|
||||
proof: vec![vec![9,8,2], vec![2,3,4]],
|
||||
}
|
||||
)
|
||||
ChunkFetchingResponse::Chunk(v1::ChunkResponse {
|
||||
chunk: vec![1, 2, 3],
|
||||
proof: vec![vec![9, 8, 2], vec![2, 3, 4]],
|
||||
}),
|
||||
);
|
||||
m
|
||||
},
|
||||
@@ -75,9 +73,7 @@ fn task_does_not_accept_invalid_chunk() {
|
||||
#[test]
|
||||
fn task_stores_valid_chunk() {
|
||||
let (mut task, rx) = get_test_running_task();
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![45, 46, 47]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![45, 46, 47]) };
|
||||
let (root_hash, chunk) = get_valid_chunk_data(pov);
|
||||
task.erasure_root = root_hash;
|
||||
task.request.index = chunk.index;
|
||||
@@ -86,16 +82,14 @@ fn task_stores_valid_chunk() {
|
||||
task.group = validators;
|
||||
|
||||
let test = TestRun {
|
||||
chunk_responses: {
|
||||
chunk_responses: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Alice.public().into()),
|
||||
ChunkFetchingResponse::Chunk(
|
||||
v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}
|
||||
)
|
||||
ChunkFetchingResponse::Chunk(v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}),
|
||||
);
|
||||
m
|
||||
},
|
||||
@@ -111,27 +105,23 @@ fn task_stores_valid_chunk() {
|
||||
#[test]
|
||||
fn task_does_not_accept_wrongly_indexed_chunk() {
|
||||
let (mut task, rx) = get_test_running_task();
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![45, 46, 47]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![45, 46, 47]) };
|
||||
let (root_hash, chunk) = get_valid_chunk_data(pov);
|
||||
task.erasure_root = root_hash;
|
||||
task.request.index = ValidatorIndex(chunk.index.0+1);
|
||||
task.request.index = ValidatorIndex(chunk.index.0 + 1);
|
||||
|
||||
let validators = vec![Sr25519Keyring::Alice.public().into()];
|
||||
task.group = validators;
|
||||
|
||||
let test = TestRun {
|
||||
chunk_responses: {
|
||||
chunk_responses: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Alice.public().into()),
|
||||
ChunkFetchingResponse::Chunk(
|
||||
v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}
|
||||
)
|
||||
ChunkFetchingResponse::Chunk(v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}),
|
||||
);
|
||||
m
|
||||
},
|
||||
@@ -144,46 +134,44 @@ fn task_does_not_accept_wrongly_indexed_chunk() {
|
||||
#[test]
|
||||
fn task_stores_valid_chunk_if_there_is_one() {
|
||||
let (mut task, rx) = get_test_running_task();
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![45, 46, 47]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![45, 46, 47]) };
|
||||
let (root_hash, chunk) = get_valid_chunk_data(pov);
|
||||
task.erasure_root = root_hash;
|
||||
task.request.index = chunk.index;
|
||||
|
||||
let validators = [
|
||||
// Only Alice has valid chunk - should succeed, even though she is tried last.
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob, Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave, Sr25519Keyring::Eve,
|
||||
]
|
||||
.iter().map(|v| v.public().into()).collect::<Vec<_>>();
|
||||
// Only Alice has valid chunk - should succeed, even though she is tried last.
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave,
|
||||
Sr25519Keyring::Eve,
|
||||
]
|
||||
.iter()
|
||||
.map(|v| v.public().into())
|
||||
.collect::<Vec<_>>();
|
||||
task.group = validators;
|
||||
|
||||
let test = TestRun {
|
||||
chunk_responses: {
|
||||
chunk_responses: {
|
||||
let mut m = HashMap::new();
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Alice.public().into()),
|
||||
ChunkFetchingResponse::Chunk(
|
||||
v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}
|
||||
)
|
||||
ChunkFetchingResponse::Chunk(v1::ChunkResponse {
|
||||
chunk: chunk.chunk.clone(),
|
||||
proof: chunk.proof,
|
||||
}),
|
||||
);
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Bob.public().into()),
|
||||
ChunkFetchingResponse::NoSuchChunk
|
||||
ChunkFetchingResponse::NoSuchChunk,
|
||||
);
|
||||
m.insert(
|
||||
Recipient::Authority(Sr25519Keyring::Charlie.public().into()),
|
||||
ChunkFetchingResponse::Chunk(
|
||||
v1::ChunkResponse {
|
||||
chunk: vec![1,2,3],
|
||||
proof: vec![vec![9,8,2], vec![2,3,4]],
|
||||
}
|
||||
)
|
||||
ChunkFetchingResponse::Chunk(v1::ChunkResponse {
|
||||
chunk: vec![1, 2, 3],
|
||||
proof: vec![vec![9, 8, 2], vec![2, 3, 4]],
|
||||
}),
|
||||
);
|
||||
|
||||
m
|
||||
@@ -205,7 +193,6 @@ struct TestRun {
|
||||
valid_chunks: HashSet<Vec<u8>>,
|
||||
}
|
||||
|
||||
|
||||
impl TestRun {
|
||||
fn run(self, task: RunningTask, rx: mpsc::Receiver<FromFetchTask>) {
|
||||
sp_tracing::try_init_simple();
|
||||
@@ -228,8 +215,7 @@ impl TestRun {
|
||||
match msg {
|
||||
FromFetchTask::Concluded(_) => break,
|
||||
FromFetchTask::Failed(_) => break,
|
||||
FromFetchTask::Message(msg) =>
|
||||
end_ok = self.handle_message(msg).await,
|
||||
FromFetchTask::Message(msg) => end_ok = self.handle_message(msg).await,
|
||||
}
|
||||
}
|
||||
if !end_ok {
|
||||
@@ -242,44 +228,50 @@ impl TestRun {
|
||||
/// end.
|
||||
async fn handle_message(&self, msg: AllMessages) -> bool {
|
||||
match msg {
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests(reqs, IfDisconnected::TryConnect)) => {
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests(
|
||||
reqs,
|
||||
IfDisconnected::TryConnect,
|
||||
)) => {
|
||||
let mut valid_responses = 0;
|
||||
for req in reqs {
|
||||
let req = match req {
|
||||
Requests::ChunkFetching(req) => req,
|
||||
_ => panic!("Unexpected request"),
|
||||
};
|
||||
let response = self.chunk_responses.get(&req.peer)
|
||||
.ok_or(network::RequestFailure::Refused);
|
||||
let response =
|
||||
self.chunk_responses.get(&req.peer).ok_or(network::RequestFailure::Refused);
|
||||
|
||||
if let Ok(ChunkFetchingResponse::Chunk(resp)) = &response {
|
||||
if self.valid_chunks.contains(&resp.chunk) {
|
||||
valid_responses += 1;
|
||||
}
|
||||
}
|
||||
req.pending_response.send(response.map(Encode::encode))
|
||||
req.pending_response
|
||||
.send(response.map(Encode::encode))
|
||||
.expect("Sending response should succeed");
|
||||
}
|
||||
return (valid_responses == 0) && self.valid_chunks.is_empty()
|
||||
}
|
||||
AllMessages::AvailabilityStore(
|
||||
AvailabilityStoreMessage::StoreChunk { chunk, tx, .. }
|
||||
) => {
|
||||
},
|
||||
AllMessages::AvailabilityStore(AvailabilityStoreMessage::StoreChunk {
|
||||
chunk,
|
||||
tx,
|
||||
..
|
||||
}) => {
|
||||
assert!(self.valid_chunks.contains(&chunk.chunk));
|
||||
tx.send(Ok(())).expect("Answering fetching task should work");
|
||||
return true
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
tracing::debug!(target: LOG_TARGET, "Unexpected message");
|
||||
return false
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a `RunningTask` filled with dummy values.
|
||||
fn get_test_running_task() -> (RunningTask, mpsc::Receiver<FromFetchTask>) {
|
||||
let (tx,rx) = mpsc::channel(0);
|
||||
let (tx, rx) = mpsc::channel(0);
|
||||
|
||||
(
|
||||
RunningTask {
|
||||
@@ -287,7 +279,7 @@ fn get_test_running_task() -> (RunningTask, mpsc::Receiver<FromFetchTask>) {
|
||||
group_index: GroupIndex(0),
|
||||
group: Vec::new(),
|
||||
request: ChunkFetchingRequest {
|
||||
candidate_hash: CandidateHash([43u8;32].into()),
|
||||
candidate_hash: CandidateHash([43u8; 32].into()),
|
||||
index: ValidatorIndex(0),
|
||||
},
|
||||
erasure_root: Hash::repeat_byte(99),
|
||||
@@ -296,6 +288,6 @@ fn get_test_running_task() -> (RunningTask, mpsc::Receiver<FromFetchTask>) {
|
||||
metrics: Metrics::new_dummy(),
|
||||
span: jaeger::Span::Disabled,
|
||||
},
|
||||
rx
|
||||
rx,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@
|
||||
//! Requester takes care of requesting erasure chunks for candidates that are pending
|
||||
//! availability.
|
||||
|
||||
use std::collections::{
|
||||
hash_map::{Entry, HashMap},
|
||||
hash_set::HashSet,
|
||||
use std::{
|
||||
collections::{
|
||||
hash_map::{Entry, HashMap},
|
||||
hash_set::HashSet,
|
||||
},
|
||||
iter::IntoIterator,
|
||||
pin::Pin,
|
||||
};
|
||||
use std::iter::IntoIterator;
|
||||
use std::pin::Pin;
|
||||
|
||||
use futures::{
|
||||
channel::mpsc,
|
||||
@@ -30,20 +32,18 @@ use futures::{
|
||||
Stream,
|
||||
};
|
||||
|
||||
use polkadot_node_subsystem_util::runtime::{RuntimeInfo, get_occupied_cores};
|
||||
use polkadot_node_subsystem_util::runtime::{get_occupied_cores, RuntimeInfo};
|
||||
use polkadot_primitives::v1::{CandidateHash, Hash, OccupiedCore};
|
||||
use polkadot_subsystem::{
|
||||
messages::AllMessages,
|
||||
ActiveLeavesUpdate, SubsystemContext, ActivatedLeaf,
|
||||
messages::AllMessages, ActivatedLeaf, ActiveLeavesUpdate, SubsystemContext,
|
||||
};
|
||||
|
||||
use super::{LOG_TARGET, Metrics};
|
||||
use super::{Metrics, LOG_TARGET};
|
||||
|
||||
/// Cache for session information.
|
||||
mod session_cache;
|
||||
use session_cache::SessionCache;
|
||||
|
||||
|
||||
/// A task fetching a particular chunk.
|
||||
mod fetch_task;
|
||||
use fetch_task::{FetchTask, FetchTaskConfig, FromFetchTask};
|
||||
@@ -81,13 +81,7 @@ impl Requester {
|
||||
/// by advancing the stream.
|
||||
pub fn new(metrics: Metrics) -> Self {
|
||||
let (tx, rx) = mpsc::channel(1);
|
||||
Requester {
|
||||
fetches: HashMap::new(),
|
||||
session_cache: SessionCache::new(),
|
||||
tx,
|
||||
rx,
|
||||
metrics,
|
||||
}
|
||||
Requester { fetches: HashMap::new(), session_cache: SessionCache::new(), tx, rx, metrics }
|
||||
}
|
||||
/// Update heads that need availability distribution.
|
||||
///
|
||||
@@ -101,15 +95,8 @@ impl Requester {
|
||||
where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?update,
|
||||
"Update fetching heads"
|
||||
);
|
||||
let ActiveLeavesUpdate {
|
||||
activated,
|
||||
deactivated,
|
||||
} = update;
|
||||
tracing::trace!(target: LOG_TARGET, ?update, "Update fetching heads");
|
||||
let ActiveLeavesUpdate { activated, deactivated } = update;
|
||||
// Order important! We need to handle activated, prior to deactivated, otherwise we might
|
||||
// cancel still needed jobs.
|
||||
self.start_requesting_chunks(ctx, runtime, activated.into_iter()).await?;
|
||||
@@ -194,7 +181,7 @@ impl Requester {
|
||||
e.insert(FetchTask::start(task_cfg, ctx).await?);
|
||||
}
|
||||
// Not a validator, nothing to do.
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -204,28 +191,21 @@ impl Requester {
|
||||
impl Stream for Requester {
|
||||
type Item = AllMessages;
|
||||
|
||||
fn poll_next(
|
||||
mut self: Pin<&mut Self>,
|
||||
ctx: &mut Context,
|
||||
) -> Poll<Option<AllMessages>> {
|
||||
fn poll_next(mut self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<AllMessages>> {
|
||||
loop {
|
||||
match Pin::new(&mut self.rx).poll_next(ctx) {
|
||||
Poll::Ready(Some(FromFetchTask::Message(m))) =>
|
||||
return Poll::Ready(Some(m)),
|
||||
Poll::Ready(Some(FromFetchTask::Message(m))) => return Poll::Ready(Some(m)),
|
||||
Poll::Ready(Some(FromFetchTask::Concluded(Some(bad_boys)))) => {
|
||||
self.session_cache.report_bad_log(bad_boys);
|
||||
continue
|
||||
}
|
||||
Poll::Ready(Some(FromFetchTask::Concluded(None))) =>
|
||||
continue,
|
||||
},
|
||||
Poll::Ready(Some(FromFetchTask::Concluded(None))) => continue,
|
||||
Poll::Ready(Some(FromFetchTask::Failed(candidate_hash))) => {
|
||||
// Make sure we retry on next block still pending availability.
|
||||
self.fetches.remove(&candidate_hash);
|
||||
}
|
||||
Poll::Ready(None) =>
|
||||
return Poll::Ready(None),
|
||||
Poll::Pending =>
|
||||
return Poll::Pending,
|
||||
},
|
||||
Poll::Ready(None) => return Poll::Ready(None),
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,6 @@ use crate::{
|
||||
///
|
||||
/// It should be ensured that a cached session stays live in the cache as long as we might need it.
|
||||
pub struct SessionCache {
|
||||
|
||||
/// Look up cached sessions by `SessionIndex`.
|
||||
///
|
||||
/// Note: Performance of fetching is really secondary here, but we need to ensure we are going
|
||||
@@ -110,12 +109,11 @@ impl SessionCache {
|
||||
|
||||
if let Some(o_info) = self.session_info_cache.get(&session_index) {
|
||||
tracing::trace!(target: LOG_TARGET, session_index, "Got session from lru");
|
||||
return Ok(Some(with_info(o_info)));
|
||||
return Ok(Some(with_info(o_info)))
|
||||
}
|
||||
|
||||
if let Some(info) = self
|
||||
.query_info_from_runtime(ctx, runtime, parent, session_index)
|
||||
.await?
|
||||
if let Some(info) =
|
||||
self.query_info_from_runtime(ctx, runtime, parent, session_index).await?
|
||||
{
|
||||
tracing::trace!(target: LOG_TARGET, session_index, "Calling `with_info`");
|
||||
let r = with_info(&info);
|
||||
@@ -132,7 +130,7 @@ impl SessionCache {
|
||||
/// Not being able to report bad validators is not fatal, so we should not shutdown the
|
||||
/// subsystem on this.
|
||||
pub fn report_bad_log(&mut self, report: BadValidators) {
|
||||
if let Err(err) = self.report_bad(report) {
|
||||
if let Err(err) = self.report_bad(report) {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
err = ?err,
|
||||
@@ -150,10 +148,9 @@ impl SessionCache {
|
||||
.session_info_cache
|
||||
.get_mut(&report.session_index)
|
||||
.ok_or(NonFatal::NoSuchCachedSession)?;
|
||||
let group = session
|
||||
.validator_groups
|
||||
.get_mut(report.group_index.0 as usize)
|
||||
.expect("A bad validator report must contain a valid group for the reported session. qed.");
|
||||
let group = session.validator_groups.get_mut(report.group_index.0 as usize).expect(
|
||||
"A bad validator report must contain a valid group for the reported session. qed.",
|
||||
);
|
||||
let bad_set = report.bad_validators.iter().collect::<HashSet<_>>();
|
||||
|
||||
// Get rid of bad boys:
|
||||
@@ -212,12 +209,7 @@ impl SessionCache {
|
||||
})
|
||||
.collect();
|
||||
|
||||
let info = SessionInfo {
|
||||
validator_groups,
|
||||
our_index,
|
||||
session_index,
|
||||
our_group,
|
||||
};
|
||||
let info = SessionInfo { validator_groups, our_index, session_index, our_group };
|
||||
return Ok(Some(info))
|
||||
}
|
||||
return Ok(None)
|
||||
|
||||
@@ -21,15 +21,15 @@ use std::sync::Arc;
|
||||
use futures::channel::oneshot;
|
||||
|
||||
use polkadot_node_network_protocol::request_response::{request::IncomingRequest, v1};
|
||||
use polkadot_primitives::v1::{CandidateHash, ValidatorIndex};
|
||||
use polkadot_node_primitives::{AvailableData, ErasureChunk};
|
||||
use polkadot_subsystem::{
|
||||
messages::AvailabilityStoreMessage,
|
||||
SubsystemContext, jaeger,
|
||||
};
|
||||
use polkadot_primitives::v1::{CandidateHash, ValidatorIndex};
|
||||
use polkadot_subsystem::{jaeger, messages::AvailabilityStoreMessage, SubsystemContext};
|
||||
|
||||
use crate::error::{NonFatal, Result};
|
||||
use crate::{LOG_TARGET, metrics::{Metrics, SUCCEEDED, FAILED, NOT_FOUND}};
|
||||
use crate::{
|
||||
error::{NonFatal, Result},
|
||||
metrics::{Metrics, FAILED, NOT_FOUND, SUCCEEDED},
|
||||
LOG_TARGET,
|
||||
};
|
||||
|
||||
/// Variant of `answer_pov_request` that does Prometheus metric and logging on errors.
|
||||
///
|
||||
@@ -38,14 +38,12 @@ pub async fn answer_pov_request_log<Context>(
|
||||
ctx: &mut Context,
|
||||
req: IncomingRequest<v1::PoVFetchingRequest>,
|
||||
metrics: &Metrics,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
let res = answer_pov_request(ctx, req).await;
|
||||
match res {
|
||||
Ok(result) =>
|
||||
metrics.on_served_pov(if result {SUCCEEDED} else {NOT_FOUND}),
|
||||
Ok(result) => metrics.on_served_pov(if result { SUCCEEDED } else { NOT_FOUND }),
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
@@ -53,7 +51,7 @@ where
|
||||
"Serving PoV failed with error"
|
||||
);
|
||||
metrics.on_served_pov(FAILED);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,8 +68,7 @@ where
|
||||
{
|
||||
let res = answer_chunk_request(ctx, req).await;
|
||||
match res {
|
||||
Ok(result) =>
|
||||
metrics.on_served_chunk(if result {SUCCEEDED} else {NOT_FOUND}),
|
||||
Ok(result) => metrics.on_served_chunk(if result { SUCCEEDED } else { NOT_FOUND }),
|
||||
Err(err) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
@@ -79,7 +76,7 @@ where
|
||||
"Serving chunk failed with error"
|
||||
);
|
||||
metrics.on_served_chunk(FAILED);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +101,7 @@ where
|
||||
Some(av_data) => {
|
||||
let pov = Arc::try_unwrap(av_data.pov).unwrap_or_else(|a| (&*a).clone());
|
||||
v1::PoVFetchingResponse::PoV(pov)
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
req.send_response(response).map_err(|_| NonFatal::SendResponse)?;
|
||||
@@ -123,8 +120,7 @@ where
|
||||
{
|
||||
let span = jaeger::Span::new(req.payload.candidate_hash, "answer-chunk-request");
|
||||
|
||||
let _child_span = span.child("answer-chunk-request")
|
||||
.with_chunk_index(req.payload.index.0);
|
||||
let _child_span = span.child("answer-chunk-request").with_chunk_index(req.payload.index.0);
|
||||
|
||||
let chunk = query_chunk(ctx, req.payload.candidate_hash, req.payload.index).await?;
|
||||
|
||||
@@ -158,10 +154,8 @@ where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
ctx.send_message(
|
||||
AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx),
|
||||
)
|
||||
.await;
|
||||
ctx.send_message(AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx))
|
||||
.await;
|
||||
|
||||
let result = rx.await.map_err(|e| {
|
||||
tracing::trace!(
|
||||
@@ -185,10 +179,8 @@ where
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
ctx.send_message(
|
||||
AvailabilityStoreMessage::QueryAvailableData(candidate_hash, tx),
|
||||
)
|
||||
.await;
|
||||
ctx.send_message(AvailabilityStoreMessage::QueryAvailableData(candidate_hash, tx))
|
||||
.await;
|
||||
|
||||
let result = rx.await.map_err(|e| NonFatal::QueryAvailableDataResponseChannel(e))?;
|
||||
Ok(result)
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
//! Helper functions and tools to generate mock data useful for testing this subsystem.
|
||||
|
||||
use std::sync::Arc;
|
||||
@@ -22,43 +21,44 @@ use std::sync::Arc;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks};
|
||||
use polkadot_node_primitives::{AvailableData, BlockData, ErasureChunk, PoV};
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateCommitments, CandidateDescriptor, CandidateHash,
|
||||
CommittedCandidateReceipt, GroupIndex, Hash, HeadData, Id as ParaId,
|
||||
OccupiedCore, PersistedValidationData, SessionInfo, ValidatorIndex
|
||||
CandidateCommitments, CandidateDescriptor, CandidateHash, CommittedCandidateReceipt,
|
||||
GroupIndex, Hash, HeadData, Id as ParaId, OccupiedCore, PersistedValidationData, SessionInfo,
|
||||
ValidatorIndex,
|
||||
};
|
||||
use polkadot_node_primitives::{PoV, ErasureChunk, AvailableData, BlockData};
|
||||
|
||||
|
||||
/// Create dummy session info with two validator groups.
|
||||
pub fn make_session_info() -> SessionInfo {
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Ferdie, // <- this node, role: validator
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave,
|
||||
Sr25519Keyring::Eve,
|
||||
Sr25519Keyring::One,
|
||||
];
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Ferdie, // <- this node, role: validator
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
Sr25519Keyring::Dave,
|
||||
Sr25519Keyring::Eve,
|
||||
Sr25519Keyring::One,
|
||||
];
|
||||
|
||||
let validator_groups: Vec<Vec<ValidatorIndex>> = [vec![5, 0, 3], vec![1, 6, 2, 4]]
|
||||
.iter().map(|g| g.into_iter().map(|v| ValidatorIndex(*v)).collect()).collect();
|
||||
let validator_groups: Vec<Vec<ValidatorIndex>> = [vec![5, 0, 3], vec![1, 6, 2, 4]]
|
||||
.iter()
|
||||
.map(|g| g.into_iter().map(|v| ValidatorIndex(*v)).collect())
|
||||
.collect();
|
||||
|
||||
SessionInfo {
|
||||
discovery_keys: validators.iter().map(|k| k.public().into()).collect(),
|
||||
// Not used:
|
||||
n_cores: validator_groups.len() as u32,
|
||||
validator_groups,
|
||||
// Not used values:
|
||||
validators: validators.iter().map(|k| k.public().into()).collect(),
|
||||
assignment_keys: Vec::new(),
|
||||
zeroth_delay_tranche_width: 0,
|
||||
relay_vrf_modulo_samples: 0,
|
||||
n_delay_tranches: 0,
|
||||
no_show_slots: 0,
|
||||
needed_approvals: 0,
|
||||
}
|
||||
SessionInfo {
|
||||
discovery_keys: validators.iter().map(|k| k.public().into()).collect(),
|
||||
// Not used:
|
||||
n_cores: validator_groups.len() as u32,
|
||||
validator_groups,
|
||||
// Not used values:
|
||||
validators: validators.iter().map(|k| k.public().into()).collect(),
|
||||
assignment_keys: Vec::new(),
|
||||
zeroth_delay_tranche_width: 0,
|
||||
relay_vrf_modulo_samples: 0,
|
||||
n_delay_tranches: 0,
|
||||
no_show_slots: 0,
|
||||
needed_approvals: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Builder for constructing occupied cores.
|
||||
@@ -72,9 +72,7 @@ pub struct OccupiedCoreBuilder {
|
||||
|
||||
impl OccupiedCoreBuilder {
|
||||
pub fn build(self) -> (OccupiedCore, (CandidateHash, ErasureChunk)) {
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![45, 46, 47]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![45, 46, 47]) };
|
||||
let pov_hash = pov.hash();
|
||||
let (erasure_root, chunk) = get_valid_chunk_data(pov.clone());
|
||||
let candidate_receipt = TestCandidateBuilder {
|
||||
@@ -83,7 +81,8 @@ impl OccupiedCoreBuilder {
|
||||
relay_parent: self.relay_parent,
|
||||
erasure_root,
|
||||
..Default::default()
|
||||
}.build();
|
||||
}
|
||||
.build();
|
||||
let core = OccupiedCore {
|
||||
next_up_on_available: None,
|
||||
occupied_since: 0,
|
||||
@@ -117,10 +116,7 @@ impl TestCandidateBuilder {
|
||||
erasure_root: self.erasure_root,
|
||||
..Default::default()
|
||||
},
|
||||
commitments: CandidateCommitments {
|
||||
head_data: self.head_data,
|
||||
..Default::default()
|
||||
},
|
||||
commitments: CandidateCommitments { head_data: self.head_data, ..Default::default() },
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,18 +130,18 @@ pub fn get_valid_chunk_data(pov: PoV) -> (Hash, ErasureChunk) {
|
||||
max_pov_size: 1024,
|
||||
relay_parent_storage_root: Default::default(),
|
||||
};
|
||||
let available_data = AvailableData {
|
||||
validation_data: persisted, pov: Arc::new(pov),
|
||||
};
|
||||
let available_data = AvailableData { validation_data: persisted, pov: Arc::new(pov) };
|
||||
let chunks = obtain_chunks(fake_validator_count, &available_data).unwrap();
|
||||
let branches = branches(chunks.as_ref());
|
||||
let root = branches.root();
|
||||
let chunk = branches.enumerate()
|
||||
.map(|(index, (proof, chunk))| ErasureChunk {
|
||||
chunk: chunk.to_vec(),
|
||||
index: ValidatorIndex(index as _),
|
||||
proof,
|
||||
})
|
||||
.next().expect("There really should be 10 chunks.");
|
||||
let chunk = branches
|
||||
.enumerate()
|
||||
.map(|(index, (proof, chunk))| ErasureChunk {
|
||||
chunk: chunk.to_vec(),
|
||||
index: ValidatorIndex(index as _),
|
||||
proof,
|
||||
})
|
||||
.next()
|
||||
.expect("There really should be 10 chunks.");
|
||||
(root, chunk)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ use super::*;
|
||||
|
||||
mod state;
|
||||
/// State for test harnesses.
|
||||
use state::{TestState, TestHarness};
|
||||
use state::{TestHarness, TestState};
|
||||
|
||||
/// Mock data useful for testing.
|
||||
pub(crate) mod mock;
|
||||
@@ -60,9 +60,7 @@ fn test_harness<T: Future<Output = ()>>(
|
||||
#[test]
|
||||
fn check_basic() {
|
||||
let state = TestState::default();
|
||||
test_harness(state.keystore.clone(), move |harness| {
|
||||
state.run(harness)
|
||||
});
|
||||
test_harness(state.keystore.clone(), move |harness| state.run(harness));
|
||||
}
|
||||
|
||||
/// Check whether requester tries all validators in group.
|
||||
@@ -75,9 +73,7 @@ fn check_fetch_tries_all() {
|
||||
v.push(None);
|
||||
v.push(None);
|
||||
}
|
||||
test_harness(state.keystore.clone(), move |harness| {
|
||||
state.run(harness)
|
||||
});
|
||||
test_harness(state.keystore.clone(), move |harness| state.run(harness));
|
||||
}
|
||||
|
||||
/// Check whether requester tries all validators in group
|
||||
@@ -87,10 +83,9 @@ fn check_fetch_tries_all() {
|
||||
#[test]
|
||||
fn check_fetch_retry() {
|
||||
let mut state = TestState::default();
|
||||
state.cores.insert(
|
||||
state.relay_chain[2],
|
||||
state.cores.get(&state.relay_chain[1]).unwrap().clone(),
|
||||
);
|
||||
state
|
||||
.cores
|
||||
.insert(state.relay_chain[2], state.cores.get(&state.relay_chain[1]).unwrap().clone());
|
||||
// We only care about the first three blocks.
|
||||
// 1. scheduled
|
||||
// 2. occupied
|
||||
@@ -98,20 +93,18 @@ fn check_fetch_retry() {
|
||||
state.relay_chain.truncate(3);
|
||||
|
||||
// Get rid of unused valid chunks:
|
||||
let valid_candidate_hashes: HashSet<_> = state.cores
|
||||
let valid_candidate_hashes: HashSet<_> = state
|
||||
.cores
|
||||
.get(&state.relay_chain[1])
|
||||
.iter()
|
||||
.flat_map(|v| v.iter())
|
||||
.filter_map(|c| {
|
||||
match c {
|
||||
CoreState::Occupied(core) => Some(core.candidate_hash),
|
||||
_ => None,
|
||||
}
|
||||
.filter_map(|c| match c {
|
||||
CoreState::Occupied(core) => Some(core.candidate_hash),
|
||||
_ => None,
|
||||
})
|
||||
.collect();
|
||||
state.valid_chunks.retain(|(ch, _)| valid_candidate_hashes.contains(ch));
|
||||
|
||||
|
||||
for (_, v) in state.chunks.iter_mut() {
|
||||
// This should still succeed as cores are still pending availability on next block.
|
||||
v.push(None);
|
||||
@@ -120,7 +113,5 @@ fn check_fetch_retry() {
|
||||
v.push(None);
|
||||
v.push(None);
|
||||
}
|
||||
test_harness(state.keystore.clone(), move |harness| {
|
||||
state.run(harness)
|
||||
});
|
||||
test_harness(state.keystore.clone(), move |harness| state.run(harness));
|
||||
}
|
||||
|
||||
@@ -14,38 +14,44 @@
|
||||
// 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}, sync::Arc, time::Duration};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_subsystem_testhelpers::TestSubsystemContextHandle;
|
||||
|
||||
use futures::{FutureExt, channel::oneshot, SinkExt, channel::mpsc, StreamExt};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
FutureExt, SinkExt, StreamExt,
|
||||
};
|
||||
use futures_timer::Delay;
|
||||
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
use sp_core::{traits::SpawnNamed, testing::TaskExecutor};
|
||||
use sc_network as network;
|
||||
use sc_network::IfDisconnected;
|
||||
use sc_network::config as netconfig;
|
||||
use sc_network::{config as netconfig, IfDisconnected};
|
||||
use sp_core::{testing::TaskExecutor, traits::SpawnNamed};
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
|
||||
use polkadot_subsystem::{
|
||||
ActiveLeavesUpdate, FromOverseer, OverseerSignal, ActivatedLeaf, LeafStatus,
|
||||
messages::{
|
||||
AllMessages, AvailabilityDistributionMessage, AvailabilityStoreMessage, NetworkBridgeMessage,
|
||||
RuntimeApiMessage, RuntimeApiRequest,
|
||||
}
|
||||
};
|
||||
use polkadot_primitives::v1::{CandidateHash, CoreState, GroupIndex, Hash, Id
|
||||
as ParaId, ScheduledCore, SessionInfo,
|
||||
ValidatorIndex
|
||||
};
|
||||
use polkadot_node_primitives::ErasureChunk;
|
||||
use polkadot_node_network_protocol::{
|
||||
jaeger,
|
||||
request_response::{IncomingRequest, OutgoingRequest, Requests, v1}
|
||||
request_response::{v1, IncomingRequest, OutgoingRequest, Requests},
|
||||
};
|
||||
use polkadot_node_primitives::ErasureChunk;
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateHash, CoreState, GroupIndex, Hash, Id as ParaId, ScheduledCore, SessionInfo,
|
||||
ValidatorIndex,
|
||||
};
|
||||
use polkadot_subsystem::{
|
||||
messages::{
|
||||
AllMessages, AvailabilityDistributionMessage, AvailabilityStoreMessage,
|
||||
NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest,
|
||||
},
|
||||
ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, LeafStatus, OverseerSignal,
|
||||
};
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
use test_helpers::{SingleItemSink, mock::make_ferdie_keystore};
|
||||
use test_helpers::{mock::make_ferdie_keystore, SingleItemSink};
|
||||
|
||||
use super::mock::{make_session_info, OccupiedCoreBuilder};
|
||||
use crate::LOG_TARGET;
|
||||
@@ -94,35 +100,32 @@ impl Default for TestState {
|
||||
let mut cores = HashMap::new();
|
||||
let mut chunks = HashMap::new();
|
||||
|
||||
cores.insert(relay_chain[0],
|
||||
cores.insert(
|
||||
relay_chain[0],
|
||||
vec![
|
||||
CoreState::Scheduled(ScheduledCore {
|
||||
para_id: chain_ids[0],
|
||||
collator: None,
|
||||
}),
|
||||
CoreState::Scheduled(ScheduledCore {
|
||||
para_id: chain_ids[1],
|
||||
collator: None,
|
||||
}),
|
||||
]
|
||||
CoreState::Scheduled(ScheduledCore { para_id: chain_ids[0], collator: None }),
|
||||
CoreState::Scheduled(ScheduledCore { para_id: chain_ids[1], collator: None }),
|
||||
],
|
||||
);
|
||||
|
||||
let heads = {
|
||||
let heads = {
|
||||
let mut advanced = relay_chain.iter();
|
||||
advanced.next();
|
||||
relay_chain.iter().zip(advanced)
|
||||
};
|
||||
for (relay_parent, relay_child) in heads {
|
||||
let (p_cores, p_chunks): (Vec<_>, Vec<_>) = chain_ids.iter().enumerate()
|
||||
let (p_cores, p_chunks): (Vec<_>, Vec<_>) = chain_ids
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, para_id)| {
|
||||
let (core, chunk) = OccupiedCoreBuilder {
|
||||
group_responsible: GroupIndex(i as _),
|
||||
para_id: *para_id,
|
||||
relay_parent: relay_parent.clone(),
|
||||
}.build();
|
||||
}
|
||||
.build();
|
||||
(CoreState::Occupied(core), chunk)
|
||||
}
|
||||
)
|
||||
})
|
||||
.unzip();
|
||||
cores.insert(relay_child.clone(), p_cores);
|
||||
// Skip chunks for our own group (won't get fetched):
|
||||
@@ -146,11 +149,12 @@ impl Default for TestState {
|
||||
}
|
||||
|
||||
impl TestState {
|
||||
|
||||
/// Run, but fail after some timeout.
|
||||
pub async fn run(self, harness: TestHarness) {
|
||||
// Make sure test won't run forever.
|
||||
let f = self.run_inner(harness.pool, harness.virtual_overseer).timeout(Duration::from_secs(10));
|
||||
let f = self
|
||||
.run_inner(harness.pool, harness.virtual_overseer)
|
||||
.timeout(Duration::from_secs(10));
|
||||
assert!(f.await.is_some(), "Test ran into timeout");
|
||||
}
|
||||
|
||||
@@ -167,17 +171,19 @@ impl TestState {
|
||||
let updates = {
|
||||
let mut advanced = self.relay_chain.iter();
|
||||
advanced.next();
|
||||
self
|
||||
.relay_chain.iter().zip(advanced)
|
||||
.map(|(old, new)| ActiveLeavesUpdate {
|
||||
activated: Some(ActivatedLeaf {
|
||||
hash: new.clone(),
|
||||
number: 1,
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
}),
|
||||
deactivated: vec![old.clone()].into(),
|
||||
}).collect::<Vec<_>>()
|
||||
self.relay_chain
|
||||
.iter()
|
||||
.zip(advanced)
|
||||
.map(|(old, new)| ActiveLeavesUpdate {
|
||||
activated: Some(ActivatedLeaf {
|
||||
hash: new.clone(),
|
||||
number: 1,
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
}),
|
||||
deactivated: vec![old.clone()].into(),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
};
|
||||
|
||||
// We should be storing all valid chunks during execution:
|
||||
@@ -190,24 +196,27 @@ impl TestState {
|
||||
// Spawning necessary as incoming queue can only hold a single item, we don't want to dead
|
||||
// lock ;-)
|
||||
let update_tx = tx.clone();
|
||||
executor.spawn("Sending active leaves updates", async move {
|
||||
for update in updates {
|
||||
overseer_signal(
|
||||
update_tx.clone(),
|
||||
OverseerSignal::ActiveLeaves(update)
|
||||
).await;
|
||||
// We need to give the subsystem a little time to do its job, otherwise it will
|
||||
// cancel jobs as obsolete:
|
||||
Delay::new(Duration::from_millis(20)).await;
|
||||
executor.spawn(
|
||||
"Sending active leaves updates",
|
||||
async move {
|
||||
for update in updates {
|
||||
overseer_signal(update_tx.clone(), OverseerSignal::ActiveLeaves(update)).await;
|
||||
// We need to give the subsystem a little time to do its job, otherwise it will
|
||||
// cancel jobs as obsolete:
|
||||
Delay::new(Duration::from_millis(20)).await;
|
||||
}
|
||||
}
|
||||
}.boxed());
|
||||
.boxed(),
|
||||
);
|
||||
|
||||
while remaining_stores > 0
|
||||
{
|
||||
while remaining_stores > 0 {
|
||||
tracing::trace!(target: LOG_TARGET, remaining_stores, "Stores left to go");
|
||||
let msg = overseer_recv(&mut rx).await;
|
||||
match msg {
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests(reqs, IfDisconnected::TryConnect)) => {
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::SendRequests(
|
||||
reqs,
|
||||
IfDisconnected::TryConnect,
|
||||
)) => {
|
||||
for req in reqs {
|
||||
// Forward requests:
|
||||
let in_req = to_incoming_req(&executor, req);
|
||||
@@ -215,50 +224,61 @@ impl TestState {
|
||||
executor.spawn(
|
||||
"Request forwarding",
|
||||
overseer_send(
|
||||
tx.clone(),
|
||||
AvailabilityDistributionMessage::ChunkFetchingRequest(in_req)
|
||||
).boxed()
|
||||
tx.clone(),
|
||||
AvailabilityDistributionMessage::ChunkFetchingRequest(in_req),
|
||||
)
|
||||
.boxed(),
|
||||
);
|
||||
}
|
||||
}
|
||||
AllMessages::AvailabilityStore(AvailabilityStoreMessage::QueryChunk(candidate_hash, validator_index, tx)) => {
|
||||
let chunk = self.chunks.get_mut(&(candidate_hash, validator_index)).map(Vec::pop).flatten().flatten();
|
||||
tx.send(chunk)
|
||||
.expect("Receiver is expected to be alive");
|
||||
}
|
||||
AllMessages::AvailabilityStore(AvailabilityStoreMessage::StoreChunk{candidate_hash, chunk, tx, ..}) => {
|
||||
},
|
||||
AllMessages::AvailabilityStore(AvailabilityStoreMessage::QueryChunk(
|
||||
candidate_hash,
|
||||
validator_index,
|
||||
tx,
|
||||
)) => {
|
||||
let chunk = self
|
||||
.chunks
|
||||
.get_mut(&(candidate_hash, validator_index))
|
||||
.map(Vec::pop)
|
||||
.flatten()
|
||||
.flatten();
|
||||
tx.send(chunk).expect("Receiver is expected to be alive");
|
||||
},
|
||||
AllMessages::AvailabilityStore(AvailabilityStoreMessage::StoreChunk {
|
||||
candidate_hash,
|
||||
chunk,
|
||||
tx,
|
||||
..
|
||||
}) => {
|
||||
assert!(
|
||||
self.valid_chunks.contains(&(candidate_hash, chunk.index)),
|
||||
"Only valid chunks should ever get stored."
|
||||
);
|
||||
tx.send(Ok(()))
|
||||
.expect("Receiver is expected to be alive");
|
||||
tx.send(Ok(())).expect("Receiver is expected to be alive");
|
||||
tracing::trace!(target: LOG_TARGET, "'Stored' fetched chunk.");
|
||||
remaining_stores -= 1;
|
||||
}
|
||||
},
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(hash, req)) => {
|
||||
match req {
|
||||
RuntimeApiRequest::SessionIndexForChild(tx) => {
|
||||
// Always session index 1 for now:
|
||||
tx.send(Ok(1))
|
||||
.expect("Receiver should still be alive");
|
||||
}
|
||||
tx.send(Ok(1)).expect("Receiver should still be alive");
|
||||
},
|
||||
RuntimeApiRequest::SessionInfo(_, tx) => {
|
||||
tx.send(Ok(Some(self.session_info.clone())))
|
||||
.expect("Receiver should be alive.");
|
||||
}
|
||||
.expect("Receiver should be alive.");
|
||||
},
|
||||
RuntimeApiRequest::AvailabilityCores(tx) => {
|
||||
tracing::trace!(target: LOG_TARGET, cores= ?self.cores[&hash], hash = ?hash, "Sending out cores for hash");
|
||||
tx.send(Ok(self.cores[&hash].clone()))
|
||||
.expect("Receiver should still be alive");
|
||||
}
|
||||
.expect("Receiver should still be alive");
|
||||
},
|
||||
_ => {
|
||||
panic!("Unexpected runtime request: {:?}", req);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,9 +292,7 @@ async fn overseer_signal(
|
||||
) {
|
||||
let msg = msg.into();
|
||||
tracing::trace!(target: LOG_TARGET, msg = ?msg, "sending message");
|
||||
tx.send(FromOverseer::Signal(msg))
|
||||
.await
|
||||
.expect("Test subsystem no longer live");
|
||||
tx.send(FromOverseer::Signal(msg)).await.expect("Test subsystem no longer live");
|
||||
}
|
||||
|
||||
async fn overseer_send(
|
||||
@@ -283,42 +301,44 @@ async fn overseer_send(
|
||||
) {
|
||||
let msg = msg.into();
|
||||
tracing::trace!(target: LOG_TARGET, msg = ?msg, "sending message");
|
||||
tx.send(FromOverseer::Communication { msg }).await
|
||||
tx.send(FromOverseer::Communication { msg })
|
||||
.await
|
||||
.expect("Test subsystem no longer live");
|
||||
tracing::trace!(target: LOG_TARGET, "sent message");
|
||||
}
|
||||
|
||||
|
||||
async fn overseer_recv(
|
||||
rx: &mut mpsc::UnboundedReceiver<AllMessages>,
|
||||
) -> AllMessages {
|
||||
async fn overseer_recv(rx: &mut mpsc::UnboundedReceiver<AllMessages>) -> AllMessages {
|
||||
tracing::trace!(target: LOG_TARGET, "waiting for message ...");
|
||||
rx.next().await.expect("Test subsystem no longer live")
|
||||
}
|
||||
|
||||
fn to_incoming_req(
|
||||
executor: &TaskExecutor,
|
||||
outgoing: Requests
|
||||
outgoing: Requests,
|
||||
) -> IncomingRequest<v1::ChunkFetchingRequest> {
|
||||
match outgoing {
|
||||
Requests::ChunkFetching(OutgoingRequest { payload, pending_response, .. }) => {
|
||||
let (tx, rx): (oneshot::Sender<netconfig::OutgoingResponse>, oneshot::Receiver<_>)
|
||||
= oneshot::channel();
|
||||
executor.spawn("Message forwarding", async {
|
||||
let response = rx.await;
|
||||
let payload = response.expect("Unexpected canceled request").result;
|
||||
pending_response.send(payload.map_err(|_| network::RequestFailure::Refused))
|
||||
.expect("Sending response is expected to work");
|
||||
}.boxed()
|
||||
let (tx, rx): (oneshot::Sender<netconfig::OutgoingResponse>, oneshot::Receiver<_>) =
|
||||
oneshot::channel();
|
||||
executor.spawn(
|
||||
"Message forwarding",
|
||||
async {
|
||||
let response = rx.await;
|
||||
let payload = response.expect("Unexpected canceled request").result;
|
||||
pending_response
|
||||
.send(payload.map_err(|_| network::RequestFailure::Refused))
|
||||
.expect("Sending response is expected to work");
|
||||
}
|
||||
.boxed(),
|
||||
);
|
||||
|
||||
IncomingRequest::new(
|
||||
// We don't really care:
|
||||
network::PeerId::random(),
|
||||
payload,
|
||||
tx
|
||||
tx,
|
||||
)
|
||||
}
|
||||
},
|
||||
_ => panic!("Unexpected request!"),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,40 +18,42 @@
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use std::collections::{HashMap, VecDeque};
|
||||
use std::pin::Pin;
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
pin::Pin,
|
||||
};
|
||||
|
||||
use futures::{channel::oneshot, prelude::*, stream::FuturesUnordered};
|
||||
use futures::future::{BoxFuture, RemoteHandle, FutureExt};
|
||||
use futures::task::{Context, Poll};
|
||||
use futures::{
|
||||
channel::oneshot,
|
||||
future::{BoxFuture, FutureExt, RemoteHandle},
|
||||
prelude::*,
|
||||
stream::FuturesUnordered,
|
||||
task::{Context, Poll},
|
||||
};
|
||||
use lru::LruCache;
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, CandidateReceipt, CandidateHash,
|
||||
Hash, ValidatorId, ValidatorIndex,
|
||||
SessionInfo, SessionIndex, BlakeTwo256, HashT, GroupIndex, BlockNumber,
|
||||
use polkadot_erasure_coding::{branch_hash, branches, obtain_chunks_v1, recovery_threshold};
|
||||
use polkadot_node_network_protocol::{
|
||||
request_response::{
|
||||
self as req_res, request::RequestError, OutgoingRequest, Recipient, Requests,
|
||||
},
|
||||
IfDisconnected,
|
||||
};
|
||||
use polkadot_node_primitives::{AvailableData, ErasureChunk};
|
||||
use polkadot_node_subsystem_util::request_session_info;
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, BlakeTwo256, BlockNumber, CandidateHash, CandidateReceipt, GroupIndex,
|
||||
Hash, HashT, SessionIndex, SessionInfo, ValidatorId, ValidatorIndex,
|
||||
};
|
||||
use polkadot_node_primitives::{ErasureChunk, AvailableData};
|
||||
use polkadot_subsystem::{
|
||||
overseer::{self, Subsystem},
|
||||
SubsystemContext, SubsystemResult, SubsystemError, SpawnedSubsystem, FromOverseer,
|
||||
OverseerSignal, ActiveLeavesUpdate, SubsystemSender,
|
||||
errors::RecoveryError,
|
||||
jaeger,
|
||||
messages::{
|
||||
AvailabilityStoreMessage, AvailabilityRecoveryMessage, NetworkBridgeMessage,
|
||||
},
|
||||
messages::{AvailabilityRecoveryMessage, AvailabilityStoreMessage, NetworkBridgeMessage},
|
||||
overseer::{self, Subsystem},
|
||||
ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext,
|
||||
SubsystemError, SubsystemResult, SubsystemSender,
|
||||
};
|
||||
use polkadot_node_network_protocol::{
|
||||
IfDisconnected,
|
||||
request_response::{
|
||||
self as req_res, OutgoingRequest, Recipient, Requests,
|
||||
request::RequestError,
|
||||
},
|
||||
};
|
||||
use polkadot_node_subsystem_util::request_session_info;
|
||||
use polkadot_erasure_coding::{branches, branch_hash, recovery_threshold, obtain_chunks_v1};
|
||||
|
||||
mod error;
|
||||
|
||||
@@ -82,9 +84,8 @@ struct RequestChunksPhase {
|
||||
// request the chunk from them.
|
||||
shuffling: VecDeque<ValidatorIndex>,
|
||||
received_chunks: HashMap<ValidatorIndex, ErasureChunk>,
|
||||
requesting_chunks: FuturesUnordered<BoxFuture<
|
||||
'static,
|
||||
Result<Option<ErasureChunk>, (ValidatorIndex, RequestError)>>,
|
||||
requesting_chunks: FuturesUnordered<
|
||||
BoxFuture<'static, Result<Option<ErasureChunk>, (ValidatorIndex, RequestError)>>,
|
||||
>,
|
||||
}
|
||||
|
||||
@@ -125,9 +126,7 @@ impl RequestFromBackersPhase {
|
||||
fn new(mut backers: Vec<ValidatorIndex>) -> Self {
|
||||
backers.shuffle(&mut rand::thread_rng());
|
||||
|
||||
RequestFromBackersPhase {
|
||||
shuffled_backers: backers,
|
||||
}
|
||||
RequestFromBackersPhase { shuffled_backers: backers }
|
||||
}
|
||||
|
||||
// Run this phase to completion.
|
||||
@@ -144,29 +143,41 @@ impl RequestFromBackersPhase {
|
||||
);
|
||||
loop {
|
||||
// Pop the next backer, and proceed to next phase if we're out.
|
||||
let validator_index = self.shuffled_backers.pop().ok_or_else(|| RecoveryError::Unavailable)?;
|
||||
let validator_index =
|
||||
self.shuffled_backers.pop().ok_or_else(|| RecoveryError::Unavailable)?;
|
||||
|
||||
// Request data.
|
||||
let (req, res) = OutgoingRequest::new(
|
||||
Recipient::Authority(params.validator_authority_keys[validator_index.0 as usize].clone()),
|
||||
Recipient::Authority(
|
||||
params.validator_authority_keys[validator_index.0 as usize].clone(),
|
||||
),
|
||||
req_res::v1::AvailableDataFetchingRequest { candidate_hash: params.candidate_hash },
|
||||
);
|
||||
|
||||
sender.send_message(NetworkBridgeMessage::SendRequests(
|
||||
vec![Requests::AvailableDataFetching(req)],
|
||||
IfDisconnected::TryConnect,
|
||||
).into()).await;
|
||||
sender
|
||||
.send_message(
|
||||
NetworkBridgeMessage::SendRequests(
|
||||
vec![Requests::AvailableDataFetching(req)],
|
||||
IfDisconnected::TryConnect,
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match res.await {
|
||||
Ok(req_res::v1::AvailableDataFetchingResponse::AvailableData(data)) => {
|
||||
if reconstructed_data_matches_root(params.validators.len(), ¶ms.erasure_root, &data) {
|
||||
if reconstructed_data_matches_root(
|
||||
params.validators.len(),
|
||||
¶ms.erasure_root,
|
||||
&data,
|
||||
) {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
candidate_hash = ?params.candidate_hash,
|
||||
"Received full data",
|
||||
);
|
||||
|
||||
return Ok(data);
|
||||
return Ok(data)
|
||||
} else {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -177,8 +188,8 @@ impl RequestFromBackersPhase {
|
||||
|
||||
// it doesn't help to report the peer with req/res.
|
||||
}
|
||||
}
|
||||
Ok(req_res::v1::AvailableDataFetchingResponse::NoSuchData) => {}
|
||||
},
|
||||
Ok(req_res::v1::AvailableDataFetchingResponse::NoSuchData) => {},
|
||||
Err(e) => tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
candidate_hash = ?params.candidate_hash,
|
||||
@@ -239,34 +250,34 @@ impl RequestChunksPhase {
|
||||
index: validator_index,
|
||||
};
|
||||
|
||||
let (req, res) = OutgoingRequest::new(
|
||||
Recipient::Authority(validator),
|
||||
raw_request.clone(),
|
||||
);
|
||||
let (req, res) =
|
||||
OutgoingRequest::new(Recipient::Authority(validator), raw_request.clone());
|
||||
|
||||
sender.send_message(NetworkBridgeMessage::SendRequests(
|
||||
vec![Requests::ChunkFetching(req)],
|
||||
IfDisconnected::TryConnect,
|
||||
).into()).await;
|
||||
sender
|
||||
.send_message(
|
||||
NetworkBridgeMessage::SendRequests(
|
||||
vec![Requests::ChunkFetching(req)],
|
||||
IfDisconnected::TryConnect,
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
self.requesting_chunks.push(Box::pin(async move {
|
||||
match res.await {
|
||||
Ok(req_res::v1::ChunkFetchingResponse::Chunk(chunk))
|
||||
=> Ok(Some(chunk.recombine_into_chunk(&raw_request))),
|
||||
Ok(req_res::v1::ChunkFetchingResponse::Chunk(chunk)) =>
|
||||
Ok(Some(chunk.recombine_into_chunk(&raw_request))),
|
||||
Ok(req_res::v1::ChunkFetchingResponse::NoSuchChunk) => Ok(None),
|
||||
Err(e) => Err((validator_index, e)),
|
||||
}
|
||||
}));
|
||||
} else {
|
||||
break;
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn wait_for_chunks(
|
||||
&mut self,
|
||||
params: &InteractionParams,
|
||||
) {
|
||||
async fn wait_for_chunks(&mut self, params: &InteractionParams) {
|
||||
// Wait for all current requests to conclude or time-out, or until we reach enough chunks.
|
||||
while let Some(request_result) = self.requesting_chunks.next().await {
|
||||
match request_result {
|
||||
@@ -275,11 +286,9 @@ impl RequestChunksPhase {
|
||||
|
||||
let validator_index = chunk.index;
|
||||
|
||||
if let Ok(anticipated_hash) = branch_hash(
|
||||
¶ms.erasure_root,
|
||||
&chunk.proof,
|
||||
chunk.index.0 as usize,
|
||||
) {
|
||||
if let Ok(anticipated_hash) =
|
||||
branch_hash(¶ms.erasure_root, &chunk.proof, chunk.index.0 as usize)
|
||||
{
|
||||
let erasure_chunk_hash = BlakeTwo256::hash(&chunk.chunk);
|
||||
|
||||
if erasure_chunk_hash != anticipated_hash {
|
||||
@@ -303,8 +312,8 @@ impl RequestChunksPhase {
|
||||
"Invalid Merkle proof",
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(None) => {}
|
||||
},
|
||||
Ok(None) => {},
|
||||
Err((validator_index, e)) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -314,17 +323,19 @@ impl RequestChunksPhase {
|
||||
);
|
||||
|
||||
match e {
|
||||
RequestError::InvalidResponse(_) => {}
|
||||
RequestError::InvalidResponse(_) => {},
|
||||
RequestError::NetworkError(_) | RequestError::Canceled(_) => {
|
||||
self.shuffling.push_front(validator_index);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Stop waiting for requests when we either can already recover the data
|
||||
// or have gotten firm 'No' responses from enough validators.
|
||||
if self.can_conclude(params) { break }
|
||||
if self.can_conclude(params) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,9 +347,11 @@ impl RequestChunksPhase {
|
||||
// First query the store for any chunks we've got.
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
sender.send_message(
|
||||
AvailabilityStoreMessage::QueryAllChunks(params.candidate_hash, tx).into()
|
||||
).await;
|
||||
sender
|
||||
.send_message(
|
||||
AvailabilityStoreMessage::QueryAllChunks(params.candidate_hash, tx).into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match rx.await {
|
||||
Ok(chunks) => {
|
||||
@@ -350,14 +363,14 @@ impl RequestChunksPhase {
|
||||
for chunk in chunks {
|
||||
self.received_chunks.insert(chunk.index, chunk);
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(oneshot::Canceled) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
candidate_hash = ?params.candidate_hash,
|
||||
"Failed to reach the availability store"
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,7 +386,7 @@ impl RequestChunksPhase {
|
||||
"Data recovery is not possible",
|
||||
);
|
||||
|
||||
return Err(RecoveryError::Unavailable);
|
||||
return Err(RecoveryError::Unavailable)
|
||||
}
|
||||
|
||||
self.launch_parallel_requests(params, sender).await;
|
||||
@@ -388,7 +401,11 @@ impl RequestChunksPhase {
|
||||
self.received_chunks.values().map(|c| (&c.chunk[..], c.index.0 as usize)),
|
||||
) {
|
||||
Ok(data) => {
|
||||
if reconstructed_data_matches_root(params.validators.len(), ¶ms.erasure_root, &data) {
|
||||
if reconstructed_data_matches_root(
|
||||
params.validators.len(),
|
||||
¶ms.erasure_root,
|
||||
&data,
|
||||
) {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
candidate_hash = ?params.candidate_hash,
|
||||
@@ -407,7 +424,7 @@ impl RequestChunksPhase {
|
||||
|
||||
Err(RecoveryError::Invalid)
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
@@ -419,7 +436,7 @@ impl RequestChunksPhase {
|
||||
|
||||
Err(RecoveryError::Invalid)
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -447,8 +464,8 @@ fn reconstructed_data_matches_root(
|
||||
err = ?e,
|
||||
"Failed to obtain chunks",
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return false
|
||||
},
|
||||
};
|
||||
|
||||
let branches = branches(&chunks);
|
||||
@@ -461,20 +478,23 @@ impl<S: SubsystemSender> Interaction<S> {
|
||||
// First just see if we have the data available locally.
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
self.sender.send_message(
|
||||
AvailabilityStoreMessage::QueryAvailableData(self.params.candidate_hash, tx).into()
|
||||
).await;
|
||||
self.sender
|
||||
.send_message(
|
||||
AvailabilityStoreMessage::QueryAvailableData(self.params.candidate_hash, tx)
|
||||
.into(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match rx.await {
|
||||
Ok(Some(data)) => return Ok(data),
|
||||
Ok(None) => {}
|
||||
Ok(None) => {},
|
||||
Err(oneshot::Canceled) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
candidate_hash = ?self.params.candidate_hash,
|
||||
"Failed to reach the availability store",
|
||||
)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -486,16 +506,14 @@ impl<S: SubsystemSender> Interaction<S> {
|
||||
match from_backers.run(&self.params, &mut self.sender).await {
|
||||
Ok(data) => break Ok(data),
|
||||
Err(RecoveryError::Invalid) => break Err(RecoveryError::Invalid),
|
||||
Err(RecoveryError::Unavailable) => {
|
||||
self.phase = InteractionPhase::RequestChunks(
|
||||
RequestChunksPhase::new(self.params.validators.len() as _)
|
||||
)
|
||||
}
|
||||
Err(RecoveryError::Unavailable) =>
|
||||
self.phase = InteractionPhase::RequestChunks(RequestChunksPhase::new(
|
||||
self.params.validators.len() as _,
|
||||
)),
|
||||
}
|
||||
}
|
||||
InteractionPhase::RequestChunks(ref mut from_all) => {
|
||||
break from_all.run(&self.params, &mut self.sender).await;
|
||||
}
|
||||
},
|
||||
InteractionPhase::RequestChunks(ref mut from_all) =>
|
||||
break from_all.run(&self.params, &mut self.sender).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -514,7 +532,7 @@ impl Future for InteractionHandle {
|
||||
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||
let mut indices_to_remove = Vec::new();
|
||||
for (i, awaiting) in self.awaiting.iter_mut().enumerate().rev() {
|
||||
if let Poll::Ready(()) = awaiting.poll_canceled(cx) {
|
||||
if let Poll::Ready(()) = awaiting.poll_canceled(cx) {
|
||||
indices_to_remove.push(i);
|
||||
}
|
||||
}
|
||||
@@ -537,7 +555,7 @@ impl Future for InteractionHandle {
|
||||
"All receivers for available data dropped.",
|
||||
);
|
||||
|
||||
return Poll::Ready(None);
|
||||
return Poll::Ready(None)
|
||||
}
|
||||
|
||||
let remote = &mut self.remote;
|
||||
@@ -580,21 +598,16 @@ where
|
||||
Context: overseer::SubsystemContext<Message = AvailabilityRecoveryMessage>,
|
||||
{
|
||||
fn start(self, ctx: Context) -> SpawnedSubsystem {
|
||||
let future = self.run(ctx)
|
||||
let future = self
|
||||
.run(ctx)
|
||||
.map_err(|e| SubsystemError::with_origin("availability-recovery", e))
|
||||
.boxed();
|
||||
SpawnedSubsystem {
|
||||
name: "availability-recovery-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "availability-recovery-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles a signal from the overseer.
|
||||
async fn handle_signal(
|
||||
state: &mut State,
|
||||
signal: OverseerSignal,
|
||||
) -> SubsystemResult<bool> {
|
||||
async fn handle_signal(state: &mut State, signal: OverseerSignal) -> SubsystemResult<bool> {
|
||||
match signal {
|
||||
OverseerSignal::Conclude => Ok(true),
|
||||
OverseerSignal::ActiveLeaves(ActiveLeavesUpdate { activated, .. }) => {
|
||||
@@ -606,8 +619,8 @@ async fn handle_signal(
|
||||
}
|
||||
|
||||
Ok(false)
|
||||
}
|
||||
OverseerSignal::BlockFinalized(_, _) => Ok(false)
|
||||
},
|
||||
OverseerSignal::BlockFinalized(_, _) => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,18 +649,14 @@ where
|
||||
|
||||
let phase = backing_group
|
||||
.and_then(|g| session_info.validator_groups.get(g.0 as usize))
|
||||
.map(|group| InteractionPhase::RequestFromBackers(
|
||||
RequestFromBackersPhase::new(group.clone())
|
||||
))
|
||||
.unwrap_or_else(|| InteractionPhase::RequestChunks(
|
||||
RequestChunksPhase::new(params.validators.len() as _)
|
||||
));
|
||||
.map(|group| {
|
||||
InteractionPhase::RequestFromBackers(RequestFromBackersPhase::new(group.clone()))
|
||||
})
|
||||
.unwrap_or_else(|| {
|
||||
InteractionPhase::RequestChunks(RequestChunksPhase::new(params.validators.len() as _))
|
||||
});
|
||||
|
||||
let interaction = Interaction {
|
||||
sender: ctx.sender().clone(),
|
||||
params,
|
||||
phase,
|
||||
};
|
||||
let interaction = Interaction { sender: ctx.sender().clone(), params, phase };
|
||||
|
||||
let (remote, remote_handle) = interaction.run().remote_handle();
|
||||
|
||||
@@ -694,43 +703,32 @@ where
|
||||
"Error responding with an availability recovery result",
|
||||
);
|
||||
}
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
if let Some(i) = state.interactions.iter_mut().find(|i| i.candidate_hash == candidate_hash) {
|
||||
i.awaiting.push(response_sender);
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
let _span = span.child("not-cached");
|
||||
let session_info = request_session_info(
|
||||
state.live_block.1,
|
||||
session_index,
|
||||
ctx.sender(),
|
||||
).await.await.map_err(error::Error::CanceledSessionInfo)??;
|
||||
let session_info = request_session_info(state.live_block.1, session_index, ctx.sender())
|
||||
.await
|
||||
.await
|
||||
.map_err(error::Error::CanceledSessionInfo)??;
|
||||
|
||||
let _span = span.child("session-info-ctx-received");
|
||||
match session_info {
|
||||
Some(session_info) => {
|
||||
launch_interaction(
|
||||
state,
|
||||
ctx,
|
||||
session_info,
|
||||
receipt,
|
||||
backing_group,
|
||||
response_sender,
|
||||
).await
|
||||
}
|
||||
Some(session_info) =>
|
||||
launch_interaction(state, ctx, session_info, receipt, backing_group, response_sender)
|
||||
.await,
|
||||
None => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
"SessionInfo is `None` at {:?}", state.live_block,
|
||||
);
|
||||
tracing::warn!(target: LOG_TARGET, "SessionInfo is `None` at {:?}", state.live_block,);
|
||||
response_sender
|
||||
.send(Err(RecoveryError::Unavailable))
|
||||
.map_err(|_| error::Error::CanceledResponseSender)?;
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -744,9 +742,8 @@ where
|
||||
Context: overseer::SubsystemContext<Message = AvailabilityRecoveryMessage>,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
ctx.send_message(
|
||||
AvailabilityStoreMessage::QueryAvailableData(candidate_hash, tx),
|
||||
).await;
|
||||
ctx.send_message(AvailabilityStoreMessage::QueryAvailableData(candidate_hash, tx))
|
||||
.await;
|
||||
|
||||
Ok(rx.await.map_err(error::Error::CanceledQueryFullData)?)
|
||||
}
|
||||
@@ -762,10 +759,7 @@ impl AvailabilityRecoverySubsystem {
|
||||
Self { fast_path: false }
|
||||
}
|
||||
|
||||
async fn run<Context>(
|
||||
self,
|
||||
mut ctx: Context,
|
||||
) -> SubsystemResult<()>
|
||||
async fn run<Context>(self, mut ctx: Context) -> SubsystemResult<()>
|
||||
where
|
||||
Context: SubsystemContext<Message = AvailabilityRecoveryMessage>,
|
||||
Context: overseer::SubsystemContext<Message = AvailabilityRecoveryMessage>,
|
||||
|
||||
@@ -14,27 +14,26 @@
|
||||
// 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::time::Duration;
|
||||
use std::sync::Arc;
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
use assert_matches::assert_matches;
|
||||
use futures::{executor, future};
|
||||
use futures_timer::Delay;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
use parity_scale_codec::Encode;
|
||||
|
||||
use super::*;
|
||||
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, PersistedValidationData, HeadData,
|
||||
};
|
||||
use polkadot_node_primitives::{PoV, BlockData};
|
||||
use polkadot_erasure_coding::{branches, obtain_chunks_v1 as obtain_chunks};
|
||||
use polkadot_node_primitives::{BlockData, PoV};
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
use polkadot_primitives::v1::{AuthorityDiscoveryId, HeadData, PersistedValidationData};
|
||||
use polkadot_subsystem::{
|
||||
messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest}, jaeger, ActivatedLeaf, LeafStatus,
|
||||
jaeger,
|
||||
messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest},
|
||||
ActivatedLeaf, LeafStatus,
|
||||
};
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
|
||||
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<AvailabilityRecoveryMessage>;
|
||||
|
||||
@@ -43,10 +42,7 @@ fn test_harness_fast_path<T: Future<Output = VirtualOverseer>>(
|
||||
) {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.filter(
|
||||
Some("polkadot_availability_recovery"),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(Some("polkadot_availability_recovery"), log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
@@ -61,10 +57,15 @@ fn test_harness_fast_path<T: Future<Output = VirtualOverseer>>(
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
}, subsystem)).1.unwrap();
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
},
|
||||
subsystem,
|
||||
))
|
||||
.1
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
fn test_harness_chunks_only<T: Future<Output = VirtualOverseer>>(
|
||||
@@ -72,10 +73,7 @@ fn test_harness_chunks_only<T: Future<Output = VirtualOverseer>>(
|
||||
) {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.filter(
|
||||
Some("polkadot_availability_recovery"),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(Some("polkadot_availability_recovery"), log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
@@ -90,10 +88,15 @@ fn test_harness_chunks_only<T: Future<Output = VirtualOverseer>>(
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
}, subsystem)).1.unwrap();
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
},
|
||||
subsystem,
|
||||
))
|
||||
.1
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(100);
|
||||
@@ -132,16 +135,11 @@ async fn overseer_recv(
|
||||
overseer: &mut test_helpers::TestSubsystemContextHandle<AvailabilityRecoveryMessage>,
|
||||
) -> AllMessages {
|
||||
tracing::trace!("waiting for message ...");
|
||||
let msg = overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("TIMEOUT is enough to recv.");
|
||||
let msg = overseer.recv().timeout(TIMEOUT).await.expect("TIMEOUT is enough to recv.");
|
||||
tracing::trace!(msg = ?msg, "received message");
|
||||
msg
|
||||
}
|
||||
|
||||
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -153,9 +151,7 @@ enum Has {
|
||||
|
||||
impl Has {
|
||||
fn timeout() -> Self {
|
||||
Has::NetworkError(sc_network::RequestFailure::Network(
|
||||
sc_network::OutboundFailure::Timeout
|
||||
))
|
||||
Has::NetworkError(sc_network::RequestFailure::Network(sc_network::OutboundFailure::Timeout))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,10 +179,7 @@ impl TestState {
|
||||
self.validators.len() - self.threshold() + 1
|
||||
}
|
||||
|
||||
async fn test_runtime_api(
|
||||
&self,
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
) {
|
||||
async fn test_runtime_api(&self, virtual_overseer: &mut VirtualOverseer) {
|
||||
assert_matches!(
|
||||
overseer_recv(virtual_overseer).await,
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
@@ -233,7 +226,7 @@ impl TestState {
|
||||
async fn respond_to_query_all_request(
|
||||
&self,
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
send_chunk: impl Fn(usize) -> bool
|
||||
send_chunk: impl Fn(usize) -> bool,
|
||||
) {
|
||||
assert_matches!(
|
||||
overseer_recv(virtual_overseer).await,
|
||||
@@ -344,7 +337,6 @@ impl TestState {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn validator_pubkeys(val_ids: &[Sr25519Keyring]) -> Vec<ValidatorId> {
|
||||
val_ids.iter().map(|v| v.public().into()).collect()
|
||||
}
|
||||
@@ -406,9 +398,7 @@ impl Default for TestState {
|
||||
relay_parent_storage_root: Default::default(),
|
||||
};
|
||||
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![42; 64]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![42; 64]) };
|
||||
|
||||
let available_data = AvailableData {
|
||||
validation_data: persisted_validation_data.clone(),
|
||||
@@ -451,7 +441,8 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -462,8 +453,9 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -472,12 +464,14 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Recovered data should match the original one.
|
||||
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
|
||||
@@ -496,20 +490,23 @@ fn availability_is_recovered_from_chunks_if_no_group_provided() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
new_candidate.hash(),
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
new_candidate.hash(),
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
)
|
||||
.await;
|
||||
|
||||
// A request times out with `Unavailable` error.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
|
||||
@@ -530,7 +527,8 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -541,8 +539,9 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
|
||||
test_state.session_index,
|
||||
Some(GroupIndex(0)),
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -551,12 +550,14 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Recovered data should match the original one.
|
||||
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
|
||||
@@ -575,20 +576,23 @@ fn availability_is_recovered_from_chunks_even_if_backing_group_supplied_if_chunk
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
new_candidate.hash(),
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
new_candidate.hash(),
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
)
|
||||
.await;
|
||||
|
||||
// A request times out with `Unavailable` error.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
|
||||
@@ -609,7 +613,8 @@ fn bad_merkle_path_leads_to_recovery_error() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -620,8 +625,9 @@ fn bad_merkle_path_leads_to_recovery_error() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -637,12 +643,14 @@ fn bad_merkle_path_leads_to_recovery_error() {
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::Yes,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
// A request times out with `Unavailable` error.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
|
||||
@@ -663,7 +671,8 @@ fn wrong_chunk_index_leads_to_recovery_error() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -674,8 +683,9 @@ fn wrong_chunk_index_leads_to_recovery_error() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -690,12 +700,14 @@ fn wrong_chunk_index_leads_to_recovery_error() {
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
)
|
||||
.await;
|
||||
|
||||
// A request times out with `Unavailable` error as there are no good peers.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
|
||||
@@ -708,9 +720,7 @@ fn invalid_erasure_coding_leads_to_invalid_error() {
|
||||
let mut test_state = TestState::default();
|
||||
|
||||
test_harness_fast_path(|mut virtual_overseer| async move {
|
||||
let pov = PoV {
|
||||
block_data: BlockData(vec![69; 64]),
|
||||
};
|
||||
let pov = PoV { block_data: BlockData(vec![69; 64]) };
|
||||
|
||||
let (bad_chunks, bad_erasure_root) = derive_erasure_chunks_with_proofs_and_root(
|
||||
test_state.chunks.len(),
|
||||
@@ -734,7 +744,8 @@ fn invalid_erasure_coding_leads_to_invalid_error() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -745,20 +756,23 @@ fn invalid_erasure_coding_leads_to_invalid_error() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
// f+1 'valid' chunks can't produce correct data.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Invalid);
|
||||
@@ -779,7 +793,8 @@ fn fast_path_backing_group_recovers() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -790,8 +805,9 @@ fn fast_path_backing_group_recovers() {
|
||||
test_state.session_index,
|
||||
Some(GroupIndex(0)),
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -804,11 +820,9 @@ fn fast_path_backing_group_recovers() {
|
||||
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
|
||||
test_state.test_full_data_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
who_has,
|
||||
).await;
|
||||
test_state
|
||||
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
|
||||
.await;
|
||||
|
||||
// Recovered data should match the original one.
|
||||
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
|
||||
@@ -829,7 +843,8 @@ fn no_answers_in_fast_path_causes_chunk_requests() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -840,8 +855,9 @@ fn no_answers_in_fast_path_causes_chunk_requests() {
|
||||
test_state.session_index,
|
||||
Some(GroupIndex(0)),
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -855,20 +871,20 @@ fn no_answers_in_fast_path_causes_chunk_requests() {
|
||||
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
|
||||
test_state.test_full_data_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
who_has,
|
||||
).await;
|
||||
test_state
|
||||
.test_full_data_requests(candidate_hash, &mut virtual_overseer, who_has)
|
||||
.await;
|
||||
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold(),
|
||||
|_| Has::Yes,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Recovered data should match the original one.
|
||||
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
|
||||
@@ -889,7 +905,8 @@ fn task_canceled_when_receivers_dropped() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, _) = oneshot::channel();
|
||||
|
||||
@@ -900,8 +917,9 @@ fn task_canceled_when_receivers_dropped() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -929,7 +947,8 @@ fn chunks_retry_until_all_nodes_respond() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -940,8 +959,9 @@ fn chunks_retry_until_all_nodes_respond() {
|
||||
test_state.session_index,
|
||||
Some(GroupIndex(0)),
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
|
||||
@@ -950,21 +970,25 @@ fn chunks_retry_until_all_nodes_respond() {
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
test_state.respond_to_query_all_request(&mut virtual_overseer, |_| false).await;
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.validators.len(),
|
||||
|_| Has::timeout(),
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.validators.len(),
|
||||
|_| Has::timeout(),
|
||||
)
|
||||
.await;
|
||||
|
||||
// we get to go another round!
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.impossibility_threshold(),
|
||||
|_| Has::No,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Recovered data should match the original one.
|
||||
assert_eq!(rx.await.unwrap().unwrap_err(), RecoveryError::Unavailable);
|
||||
@@ -985,7 +1009,8 @@ fn returns_early_if_we_have_the_data() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -996,8 +1021,9 @@ fn returns_early_if_we_have_the_data() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, true).await;
|
||||
@@ -1020,7 +1046,8 @@ fn does_not_query_local_validator() {
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -1031,8 +1058,9 @@ fn does_not_query_local_validator() {
|
||||
test_state.session_index,
|
||||
None,
|
||||
tx,
|
||||
)
|
||||
).await;
|
||||
),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.test_runtime_api(&mut virtual_overseer).await;
|
||||
test_state.respond_to_available_data_query(&mut virtual_overseer, false).await;
|
||||
@@ -1040,28 +1068,24 @@ fn does_not_query_local_validator() {
|
||||
|
||||
let candidate_hash = test_state.candidate.hash();
|
||||
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.validators.len(),
|
||||
|i| if i == 0 {
|
||||
panic!("requested from local validator")
|
||||
} else {
|
||||
Has::timeout()
|
||||
},
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.validators.len(),
|
||||
|i| if i == 0 { panic!("requested from local validator") } else { Has::timeout() },
|
||||
)
|
||||
.await;
|
||||
|
||||
// second round, make sure it uses the local chunk.
|
||||
test_state.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold() - 1,
|
||||
|i| if i == 0 {
|
||||
panic!("requested from local validator")
|
||||
} else {
|
||||
Has::Yes
|
||||
},
|
||||
).await;
|
||||
test_state
|
||||
.test_chunk_requests(
|
||||
candidate_hash,
|
||||
&mut virtual_overseer,
|
||||
test_state.threshold() - 1,
|
||||
|i| if i == 0 { panic!("requested from local validator") } else { Has::Yes },
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_eq!(rx.await.unwrap().unwrap(), test_state.available_data);
|
||||
virtual_overseer
|
||||
|
||||
@@ -24,19 +24,19 @@
|
||||
|
||||
use futures::{channel::oneshot, FutureExt};
|
||||
|
||||
use polkadot_subsystem::messages::*;
|
||||
use polkadot_subsystem::{
|
||||
PerLeafSpan, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem,
|
||||
SubsystemContext, SubsystemResult, SubsystemError,
|
||||
jaeger,
|
||||
overseer,
|
||||
use polkadot_node_network_protocol::{
|
||||
v1 as protocol_v1, OurView, PeerId, UnifiedReputationChange as Rep, View,
|
||||
};
|
||||
use polkadot_node_subsystem_util::{
|
||||
self as util,
|
||||
metrics::{self, prometheus},
|
||||
self as util, MIN_GOSSIP_PEERS,
|
||||
MIN_GOSSIP_PEERS,
|
||||
};
|
||||
use polkadot_primitives::v1::{Hash, SignedAvailabilityBitfield, SigningContext, ValidatorId};
|
||||
use polkadot_node_network_protocol::{v1 as protocol_v1, PeerId, View, UnifiedReputationChange as Rep, OurView};
|
||||
use polkadot_subsystem::{
|
||||
jaeger, messages::*, overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, PerLeafSpan,
|
||||
SpawnedSubsystem, SubsystemContext, SubsystemError, SubsystemResult,
|
||||
};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -46,8 +46,10 @@ const COST_SIGNATURE_INVALID: Rep = Rep::CostMajor("Bitfield signature invalid")
|
||||
const COST_VALIDATOR_INDEX_INVALID: Rep = Rep::CostMajor("Bitfield validator index invalid");
|
||||
const COST_MISSING_PEER_SESSION_KEY: Rep = Rep::CostMinor("Missing peer session key");
|
||||
const COST_NOT_IN_VIEW: Rep = Rep::CostMinor("Not interested in that parent hash");
|
||||
const COST_PEER_DUPLICATE_MESSAGE: Rep = Rep::CostMinorRepeated("Peer sent the same message multiple times");
|
||||
const BENEFIT_VALID_MESSAGE_FIRST: Rep = Rep::BenefitMinorFirst("Valid message with new information");
|
||||
const COST_PEER_DUPLICATE_MESSAGE: Rep =
|
||||
Rep::CostMinorRepeated("Peer sent the same message multiple times");
|
||||
const BENEFIT_VALID_MESSAGE_FIRST: Rep =
|
||||
Rep::BenefitMinorFirst("Valid message with new information");
|
||||
const BENEFIT_VALID_MESSAGE: Rep = Rep::BenefitMinor("Valid message");
|
||||
|
||||
/// Checked signed availability bitfield that is distributed
|
||||
@@ -62,14 +64,10 @@ struct BitfieldGossipMessage {
|
||||
|
||||
impl BitfieldGossipMessage {
|
||||
fn into_validation_protocol(self) -> protocol_v1::ValidationProtocol {
|
||||
protocol_v1::ValidationProtocol::BitfieldDistribution(
|
||||
self.into_network_message()
|
||||
)
|
||||
protocol_v1::ValidationProtocol::BitfieldDistribution(self.into_network_message())
|
||||
}
|
||||
|
||||
fn into_network_message(self)
|
||||
-> protocol_v1::BitfieldDistributionMessage
|
||||
{
|
||||
fn into_network_message(self) -> protocol_v1::BitfieldDistributionMessage {
|
||||
protocol_v1::BitfieldDistributionMessage::Bitfield(
|
||||
self.relay_parent,
|
||||
self.signed_availability.into(),
|
||||
@@ -124,7 +122,11 @@ struct PerRelayParentData {
|
||||
|
||||
impl PerRelayParentData {
|
||||
/// Create a new instance.
|
||||
fn new(signing_context: SigningContext, validator_set: Vec<ValidatorId>, span: PerLeafSpan) -> Self {
|
||||
fn new(
|
||||
signing_context: SigningContext,
|
||||
validator_set: Vec<ValidatorId>,
|
||||
span: PerLeafSpan,
|
||||
) -> Self {
|
||||
Self {
|
||||
signing_context,
|
||||
validator_set,
|
||||
@@ -141,8 +143,14 @@ impl PerRelayParentData {
|
||||
peer: &PeerId,
|
||||
validator: &ValidatorId,
|
||||
) -> bool {
|
||||
self.message_sent_to_peer.get(peer).map(|v| !v.contains(validator)).unwrap_or(true)
|
||||
&& self.message_received_from_peer.get(peer).map(|v| !v.contains(validator)).unwrap_or(true)
|
||||
self.message_sent_to_peer
|
||||
.get(peer)
|
||||
.map(|v| !v.contains(validator))
|
||||
.unwrap_or(true) &&
|
||||
self.message_received_from_peer
|
||||
.get(peer)
|
||||
.map(|v| !v.contains(validator))
|
||||
.unwrap_or(true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,34 +180,34 @@ impl BitfieldDistribution {
|
||||
Ok(message) => message,
|
||||
Err(e) => {
|
||||
tracing::debug!(target: LOG_TARGET, err = ?e, "Failed to receive a message from Overseer, exiting");
|
||||
return;
|
||||
return
|
||||
},
|
||||
};
|
||||
match message {
|
||||
FromOverseer::Communication {
|
||||
msg: BitfieldDistributionMessage::DistributeBitfield(hash, signed_availability),
|
||||
} => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?hash,
|
||||
"Processing DistributeBitfield"
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?hash, "Processing DistributeBitfield");
|
||||
handle_bitfield_distribution(
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&self.metrics,
|
||||
hash,
|
||||
signed_availability,
|
||||
).await;
|
||||
}
|
||||
)
|
||||
.await;
|
||||
},
|
||||
FromOverseer::Communication {
|
||||
msg: BitfieldDistributionMessage::NetworkBridgeUpdateV1(event),
|
||||
} => {
|
||||
tracing::trace!(target: LOG_TARGET, "Processing NetworkMessage");
|
||||
// a network message was received
|
||||
handle_network_msg(&mut ctx, &mut state, &self.metrics, event).await;
|
||||
}
|
||||
FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate { activated, .. })) => {
|
||||
},
|
||||
FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate {
|
||||
activated,
|
||||
..
|
||||
})) => {
|
||||
let _timer = self.metrics.time_active_leaves_update();
|
||||
|
||||
for activated in activated {
|
||||
@@ -221,41 +229,34 @@ impl BitfieldDistribution {
|
||||
relay_parent,
|
||||
PerRelayParentData::new(signing_context, validator_set, span),
|
||||
);
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
tracing::warn!(target: LOG_TARGET, err = ?e, "query_basics has failed");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
FromOverseer::Signal(OverseerSignal::BlockFinalized(hash, number)) => {
|
||||
tracing::trace!(target: LOG_TARGET, hash = %hash, number = %number, "block finalized");
|
||||
}
|
||||
},
|
||||
FromOverseer::Signal(OverseerSignal::Conclude) => {
|
||||
tracing::trace!(target: LOG_TARGET, "Conclude");
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Modify the reputation of a peer based on its behavior.
|
||||
async fn modify_reputation<Context>(
|
||||
ctx: &mut Context,
|
||||
peer: PeerId,
|
||||
rep: Rep,
|
||||
)
|
||||
async fn modify_reputation<Context>(ctx: &mut Context, peer: PeerId, rep: Rep)
|
||||
where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
tracing::trace!(target: LOG_TARGET, ?rep, peer_id = %peer, "reputation change");
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::ReportPeer(peer, rep),
|
||||
)
|
||||
.await
|
||||
ctx.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await
|
||||
}
|
||||
|
||||
/// Distribute a given valid and signature checked bitfield message.
|
||||
@@ -267,8 +268,7 @@ async fn handle_bitfield_distribution<Context>(
|
||||
metrics: &Metrics,
|
||||
relay_parent: Hash,
|
||||
signed_availability: SignedAvailabilityBitfield,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let _timer = metrics.time_handle_bitfield_distribution();
|
||||
@@ -284,26 +284,27 @@ where
|
||||
"Not supposed to work on relay parent related data",
|
||||
);
|
||||
|
||||
return;
|
||||
return
|
||||
};
|
||||
let validator_set = &job_data.validator_set;
|
||||
if validator_set.is_empty() {
|
||||
tracing::trace!(target: LOG_TARGET, relay_parent = %relay_parent, "validator set is empty");
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
let validator_index = signed_availability.validator_index().0 as usize;
|
||||
let validator = if let Some(validator) = validator_set.get(validator_index) {
|
||||
validator.clone()
|
||||
} else {
|
||||
tracing::trace!(target: LOG_TARGET, "Could not find a validator for index {}", validator_index);
|
||||
return;
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
"Could not find a validator for index {}",
|
||||
validator_index
|
||||
);
|
||||
return
|
||||
};
|
||||
|
||||
let msg = BitfieldGossipMessage {
|
||||
relay_parent,
|
||||
signed_availability,
|
||||
};
|
||||
let msg = BitfieldGossipMessage { relay_parent, signed_availability };
|
||||
|
||||
let gossip_peers = &state.gossip_peers;
|
||||
let peer_views = &mut state.peer_views;
|
||||
@@ -322,23 +323,17 @@ async fn relay_message<Context>(
|
||||
peer_views: &mut HashMap<PeerId, View>,
|
||||
validator: ValidatorId,
|
||||
message: BitfieldGossipMessage,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let span = job_data.span.child("relay-msg");
|
||||
|
||||
let _span = span.child("provisionable");
|
||||
// notify the overseer about a new and valid signed bitfield
|
||||
ctx.send_message(
|
||||
ProvisionerMessage::ProvisionableData(
|
||||
message.relay_parent,
|
||||
ProvisionableData::Bitfield(
|
||||
message.relay_parent,
|
||||
message.signed_availability.clone(),
|
||||
),
|
||||
),
|
||||
)
|
||||
ctx.send_message(ProvisionerMessage::ProvisionableData(
|
||||
message.relay_parent,
|
||||
ProvisionableData::Bitfield(message.relay_parent, message.signed_availability.clone()),
|
||||
))
|
||||
.await;
|
||||
|
||||
drop(_span);
|
||||
@@ -350,7 +345,8 @@ where
|
||||
.filter_map(|(peer, view)| {
|
||||
// check interest in the peer in this message's relay parent
|
||||
if view.contains(&message.relay_parent) {
|
||||
let message_needed = job_data.message_from_validator_needed_by_peer(&peer, &validator);
|
||||
let message_needed =
|
||||
job_data.message_from_validator_needed_by_peer(&peer, &validator);
|
||||
if message_needed {
|
||||
Some(peer.clone())
|
||||
} else {
|
||||
@@ -366,14 +362,14 @@ where
|
||||
interested_peers,
|
||||
MIN_GOSSIP_PEERS,
|
||||
);
|
||||
interested_peers.iter()
|
||||
.for_each(|peer|{
|
||||
// track the message as sent for this peer
|
||||
job_data.message_sent_to_peer
|
||||
.entry(peer.clone())
|
||||
.or_default()
|
||||
.insert(validator.clone());
|
||||
});
|
||||
interested_peers.iter().for_each(|peer| {
|
||||
// track the message as sent for this peer
|
||||
job_data
|
||||
.message_sent_to_peer
|
||||
.entry(peer.clone())
|
||||
.or_default()
|
||||
.insert(validator.clone());
|
||||
});
|
||||
|
||||
drop(_span);
|
||||
|
||||
@@ -385,12 +381,10 @@ where
|
||||
);
|
||||
} else {
|
||||
let _span = span.child("gossip");
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendValidationMessage(
|
||||
interested_peers,
|
||||
message.into_validation_protocol(),
|
||||
),
|
||||
)
|
||||
ctx.send_message(NetworkBridgeMessage::SendValidationMessage(
|
||||
interested_peers,
|
||||
message.into_validation_protocol(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
@@ -402,8 +396,7 @@ async fn process_incoming_peer_message<Context>(
|
||||
metrics: &Metrics,
|
||||
origin: PeerId,
|
||||
message: protocol_v1::BitfieldDistributionMessage,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let protocol_v1::BitfieldDistributionMessage::Bitfield(relay_parent, bitfield) = message;
|
||||
@@ -416,7 +409,7 @@ where
|
||||
// we don't care about this, not part of our view.
|
||||
if !state.view.contains(&relay_parent) {
|
||||
modify_reputation(ctx, origin, COST_NOT_IN_VIEW).await;
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Ignore anything the overseer did not tell this subsystem to work on.
|
||||
@@ -425,16 +418,17 @@ where
|
||||
job_data
|
||||
} else {
|
||||
modify_reputation(ctx, origin, COST_NOT_IN_VIEW).await;
|
||||
return;
|
||||
return
|
||||
};
|
||||
|
||||
let validator_index = bitfield.unchecked_validator_index();
|
||||
|
||||
let mut _span = job_data.span
|
||||
.child("msg-received")
|
||||
.with_peer_id(&origin)
|
||||
.with_claimed_validator_index(validator_index)
|
||||
.with_stage(jaeger::Stage::BitfieldDistribution);
|
||||
let mut _span = job_data
|
||||
.span
|
||||
.child("msg-received")
|
||||
.with_peer_id(&origin)
|
||||
.with_claimed_validator_index(validator_index)
|
||||
.with_stage(jaeger::Stage::BitfieldDistribution);
|
||||
|
||||
let validator_set = &job_data.validator_set;
|
||||
if validator_set.is_empty() {
|
||||
@@ -445,7 +439,7 @@ where
|
||||
"Validator set is empty",
|
||||
);
|
||||
modify_reputation(ctx, origin, COST_MISSING_PEER_SESSION_KEY).await;
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
// Use the (untrusted) validator index provided by the signed payload
|
||||
@@ -455,28 +449,20 @@ where
|
||||
validator.clone()
|
||||
} else {
|
||||
modify_reputation(ctx, origin, COST_VALIDATOR_INDEX_INVALID).await;
|
||||
return;
|
||||
return
|
||||
};
|
||||
|
||||
// Check if the peer already sent us a message for the validator denoted in the message earlier.
|
||||
// Must be done after validator index verification, in order to avoid storing an unbounded
|
||||
// number of set entries.
|
||||
let received_set = job_data
|
||||
.message_received_from_peer
|
||||
.entry(origin.clone())
|
||||
.or_default();
|
||||
let received_set = job_data.message_received_from_peer.entry(origin.clone()).or_default();
|
||||
|
||||
if !received_set.contains(&validator) {
|
||||
received_set.insert(validator.clone());
|
||||
} else {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?validator_index,
|
||||
?origin,
|
||||
"Duplicate message",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?validator_index, ?origin, "Duplicate message",);
|
||||
modify_reputation(ctx, origin, COST_PEER_DUPLICATE_MESSAGE).await;
|
||||
return;
|
||||
return
|
||||
};
|
||||
|
||||
let one_per_validator = &mut (job_data.one_per_validator);
|
||||
@@ -491,25 +477,23 @@ where
|
||||
if old_message.signed_availability.as_unchecked() == &bitfield {
|
||||
modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE).await;
|
||||
}
|
||||
return;
|
||||
return
|
||||
}
|
||||
let signed_availability = match bitfield.try_into_checked(&signing_context, &validator) {
|
||||
Err(_) => {
|
||||
modify_reputation(ctx, origin, COST_SIGNATURE_INVALID).await;
|
||||
return;
|
||||
return
|
||||
},
|
||||
Ok(bitfield) => bitfield,
|
||||
};
|
||||
|
||||
let message = BitfieldGossipMessage {
|
||||
relay_parent,
|
||||
signed_availability,
|
||||
};
|
||||
let message = BitfieldGossipMessage { relay_parent, signed_availability };
|
||||
|
||||
metrics.on_bitfield_received();
|
||||
one_per_validator.insert(validator.clone(), message.clone());
|
||||
|
||||
relay_message(ctx, job_data, &state.gossip_peers, &mut state.peer_views, validator, message).await;
|
||||
relay_message(ctx, job_data, &state.gossip_peers, &mut state.peer_views, validator, message)
|
||||
.await;
|
||||
|
||||
modify_reputation(ctx, origin, BENEFIT_VALID_MESSAGE_FIRST).await
|
||||
}
|
||||
@@ -521,32 +505,22 @@ async fn handle_network_msg<Context>(
|
||||
state: &mut ProtocolState,
|
||||
metrics: &Metrics,
|
||||
bridge_message: NetworkBridgeEvent<protocol_v1::BitfieldDistributionMessage>,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let _timer = metrics.time_handle_network_msg();
|
||||
|
||||
match bridge_message {
|
||||
NetworkBridgeEvent::PeerConnected(peerid, role, _) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peerid,
|
||||
?role,
|
||||
"Peer connected",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peerid, ?role, "Peer connected",);
|
||||
// insert if none already present
|
||||
state.peer_views.entry(peerid).or_default();
|
||||
}
|
||||
},
|
||||
NetworkBridgeEvent::PeerDisconnected(peerid) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peerid,
|
||||
"Peer disconnected",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peerid, "Peer disconnected",);
|
||||
// get rid of superfluous data
|
||||
state.peer_views.remove(&peerid);
|
||||
}
|
||||
},
|
||||
NetworkBridgeEvent::NewGossipTopology(peers) => {
|
||||
let newly_added: Vec<PeerId> = peers.difference(&state.gossip_peers).cloned().collect();
|
||||
state.gossip_peers = peers;
|
||||
@@ -555,24 +529,15 @@ where
|
||||
handle_peer_view_change(ctx, state, peer, view).await;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
NetworkBridgeEvent::PeerViewChange(peerid, view) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peerid,
|
||||
?view,
|
||||
"Peer view change",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peerid, ?view, "Peer view change",);
|
||||
handle_peer_view_change(ctx, state, peerid, view).await;
|
||||
}
|
||||
},
|
||||
NetworkBridgeEvent::OurViewChange(view) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?view,
|
||||
"Our view change",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?view, "Our view change",);
|
||||
handle_our_view_change(state, view);
|
||||
}
|
||||
},
|
||||
NetworkBridgeEvent::PeerMessage(remote, message) =>
|
||||
process_incoming_peer_message(ctx, state, metrics, remote, message).await,
|
||||
}
|
||||
@@ -598,7 +563,6 @@ fn handle_our_view_change(state: &mut ProtocolState, view: OurView) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Send the difference between two views which were not sent
|
||||
// to that particular peer.
|
||||
async fn handle_peer_view_change<Context>(
|
||||
@@ -606,25 +570,27 @@ async fn handle_peer_view_change<Context>(
|
||||
state: &mut ProtocolState,
|
||||
origin: PeerId,
|
||||
view: View,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let added = state.peer_views.entry(origin.clone()).or_default().replace_difference(view).cloned().collect::<Vec<_>>();
|
||||
let added = state
|
||||
.peer_views
|
||||
.entry(origin.clone())
|
||||
.or_default()
|
||||
.replace_difference(view)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let is_gossip_peer = state.gossip_peers.contains(&origin);
|
||||
let lucky = is_gossip_peer || util::gen_ratio(
|
||||
util::MIN_GOSSIP_PEERS.saturating_sub(state.gossip_peers.len()),
|
||||
util::MIN_GOSSIP_PEERS,
|
||||
);
|
||||
let lucky = is_gossip_peer ||
|
||||
util::gen_ratio(
|
||||
util::MIN_GOSSIP_PEERS.saturating_sub(state.gossip_peers.len()),
|
||||
util::MIN_GOSSIP_PEERS,
|
||||
);
|
||||
|
||||
if !lucky {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?origin,
|
||||
"Peer view change is ignored",
|
||||
);
|
||||
return;
|
||||
tracing::trace!(target: LOG_TARGET, ?origin, "Peer view change is ignored",);
|
||||
return
|
||||
}
|
||||
|
||||
// Send all messages we've seen before and the peer is now interested
|
||||
@@ -637,14 +603,10 @@ where
|
||||
// to the peer `origin`...
|
||||
let one_per_validator = job_data.one_per_validator.clone();
|
||||
let origin = origin.clone();
|
||||
Some(
|
||||
one_per_validator
|
||||
.into_iter()
|
||||
.filter(move |(validator, _message)| {
|
||||
// ..except for the ones the peer already has.
|
||||
job_data.message_from_validator_needed_by_peer(&origin, validator)
|
||||
}),
|
||||
)
|
||||
Some(one_per_validator.into_iter().filter(move |(validator, _message)| {
|
||||
// ..except for the ones the peer already has.
|
||||
job_data.message_from_validator_needed_by_peer(&origin, validator)
|
||||
}))
|
||||
} else {
|
||||
// A relay parent is in the peers view, which is not in ours, ignore those.
|
||||
None
|
||||
@@ -665,14 +627,13 @@ async fn send_tracked_gossip_message<Context>(
|
||||
dest: PeerId,
|
||||
validator: ValidatorId,
|
||||
message: BitfieldGossipMessage,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
let job_data = if let Some(job_data) = state.per_relay_parent.get_mut(&message.relay_parent) {
|
||||
job_data
|
||||
} else {
|
||||
return;
|
||||
return
|
||||
};
|
||||
|
||||
let _span = job_data.span.child("gossip");
|
||||
@@ -684,17 +645,17 @@ where
|
||||
"Sending gossip message"
|
||||
);
|
||||
|
||||
job_data.message_sent_to_peer
|
||||
job_data
|
||||
.message_sent_to_peer
|
||||
.entry(dest.clone())
|
||||
.or_default()
|
||||
.insert(validator.clone());
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendValidationMessage(
|
||||
vec![dest],
|
||||
message.into_validation_protocol(),
|
||||
),
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::SendValidationMessage(
|
||||
vec![dest],
|
||||
message.into_validation_protocol(),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
impl<Context> overseer::Subsystem<Context, SubsystemError> for BitfieldDistribution
|
||||
@@ -703,14 +664,9 @@ where
|
||||
Context: overseer::SubsystemContext<Message = BitfieldDistributionMessage>,
|
||||
{
|
||||
fn start(self, ctx: Context) -> SpawnedSubsystem {
|
||||
let future = self.run(ctx)
|
||||
.map(|_| Ok(()))
|
||||
.boxed();
|
||||
let future = self.run(ctx).map(|_| Ok(())).boxed();
|
||||
|
||||
SpawnedSubsystem {
|
||||
name: "bitfield-distribution-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "bitfield-distribution-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,23 +685,23 @@ where
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
relay_parent.clone(),
|
||||
RuntimeApiRequest::Validators(validators_tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
// query signing context
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
relay_parent.clone(),
|
||||
RuntimeApiRequest::SessionIndexForChild(session_tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
match (validators_rx.await?, session_rx.await?) {
|
||||
(Ok(v), Ok(s)) => Ok(Some((
|
||||
v,
|
||||
SigningContext { parent_hash: relay_parent, session_index: s },
|
||||
))),
|
||||
(Ok(v), Ok(s)) =>
|
||||
Ok(Some((v, SigningContext { parent_hash: relay_parent, session_index: s }))),
|
||||
(Err(e), _) | (_, Err(e)) => {
|
||||
tracing::warn!(target: LOG_TARGET, err = ?e, "Failed to fetch basics from runtime API");
|
||||
Ok(None)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -781,8 +737,12 @@ impl Metrics {
|
||||
}
|
||||
|
||||
/// Provide a timer for `handle_bitfield_distribution` which observes on drop.
|
||||
fn time_handle_bitfield_distribution(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
|
||||
self.0.as_ref().map(|metrics| metrics.handle_bitfield_distribution.start_timer())
|
||||
fn time_handle_bitfield_distribution(
|
||||
&self,
|
||||
) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
|
||||
self.0
|
||||
.as_ref()
|
||||
.map(|metrics| metrics.handle_bitfield_distribution.start_timer())
|
||||
}
|
||||
|
||||
/// Provide a timer for `handle_network_msg` which observes on drop.
|
||||
@@ -797,42 +757,36 @@ impl metrics::Metrics for Metrics {
|
||||
gossipped_own_availability_bitfields: prometheus::register(
|
||||
prometheus::Counter::new(
|
||||
"parachain_gossipped_own_availabilty_bitfields_total",
|
||||
"Number of own availability bitfields sent to other peers."
|
||||
"Number of own availability bitfields sent to other peers.",
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
received_availability_bitfields: prometheus::register(
|
||||
prometheus::Counter::new(
|
||||
"parachain_received_availabilty_bitfields_total",
|
||||
"Number of valid availability bitfields received from other peers."
|
||||
"Number of valid availability bitfields received from other peers.",
|
||||
)?,
|
||||
registry,
|
||||
)?,
|
||||
active_leaves_update: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_active_leaves_update",
|
||||
"Time spent within `bitfield_distribution::active_leaves_update`",
|
||||
)
|
||||
)?,
|
||||
prometheus::Histogram::with_opts(prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_active_leaves_update",
|
||||
"Time spent within `bitfield_distribution::active_leaves_update`",
|
||||
))?,
|
||||
registry,
|
||||
)?,
|
||||
handle_bitfield_distribution: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_handle_bitfield_distribution",
|
||||
"Time spent within `bitfield_distribution::handle_bitfield_distribution`",
|
||||
)
|
||||
)?,
|
||||
prometheus::Histogram::with_opts(prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_handle_bitfield_distribution",
|
||||
"Time spent within `bitfield_distribution::handle_bitfield_distribution`",
|
||||
))?,
|
||||
registry,
|
||||
)?,
|
||||
handle_network_msg: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_handle_network_msg",
|
||||
"Time spent within `bitfield_distribution::handle_network_msg`",
|
||||
)
|
||||
)?,
|
||||
prometheus::Histogram::with_opts(prometheus::HistogramOpts::new(
|
||||
"parachain_bitfield_distribution_handle_network_msg",
|
||||
"Time spent within `bitfield_distribution::handle_network_msg`",
|
||||
))?,
|
||||
registry,
|
||||
)?,
|
||||
};
|
||||
|
||||
@@ -15,28 +15,24 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use assert_matches::assert_matches;
|
||||
use bitvec::bitvec;
|
||||
use futures::executor;
|
||||
use maplit::hashmap;
|
||||
use polkadot_primitives::v1::{Signed, AvailabilityBitfield, ValidatorIndex};
|
||||
use polkadot_node_network_protocol::{our_view, view, ObservedRole};
|
||||
use polkadot_node_subsystem_test_helpers::make_subsystem_context;
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use sp_keystore::{SyncCryptoStorePtr, SyncCryptoStore};
|
||||
use sp_application_crypto::AppKey;
|
||||
use sp_keystore::testing::KeyStore;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use std::iter::FromIterator as _;
|
||||
use assert_matches::assert_matches;
|
||||
use polkadot_node_network_protocol::{view, ObservedRole, our_view};
|
||||
use polkadot_primitives::v1::{AvailabilityBitfield, Signed, ValidatorIndex};
|
||||
use polkadot_subsystem::jaeger;
|
||||
use sp_application_crypto::AppKey;
|
||||
use sp_keystore::{testing::KeyStore, SyncCryptoStore, SyncCryptoStorePtr};
|
||||
use std::{iter::FromIterator as _, sync::Arc, time::Duration};
|
||||
|
||||
macro_rules! launch {
|
||||
($fut:expr) => {
|
||||
$fut
|
||||
.timeout(Duration::from_millis(10))
|
||||
.await
|
||||
.expect("10ms is more than enough for sending messages.")
|
||||
$fut.timeout(Duration::from_millis(10))
|
||||
.await
|
||||
.expect("10ms is more than enough for sending messages.")
|
||||
};
|
||||
}
|
||||
|
||||
@@ -64,11 +60,7 @@ fn prewarmed_state(
|
||||
span: PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"),
|
||||
},
|
||||
},
|
||||
peer_views: peers
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(|peer| (peer, view!(relay_parent)))
|
||||
.collect(),
|
||||
peer_views: peers.iter().cloned().map(|peer| (peer, view!(relay_parent))).collect(),
|
||||
gossip_peers: peers.into_iter().collect(),
|
||||
view: our_view!(relay_parent),
|
||||
}
|
||||
@@ -80,26 +72,28 @@ fn state_with_view(
|
||||
) -> (ProtocolState, SigningContext, SyncCryptoStorePtr, ValidatorId) {
|
||||
let mut state = ProtocolState::default();
|
||||
|
||||
let signing_context = SigningContext {
|
||||
session_index: 1,
|
||||
parent_hash: relay_parent.clone(),
|
||||
};
|
||||
let signing_context = SigningContext { session_index: 1, parent_hash: relay_parent.clone() };
|
||||
|
||||
let keystore : SyncCryptoStorePtr = Arc::new(KeyStore::new());
|
||||
let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
|
||||
let validator = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
|
||||
.expect("generating sr25519 key not to fail");
|
||||
|
||||
state.per_relay_parent = view.iter().map(|relay_parent| {(
|
||||
relay_parent.clone(),
|
||||
PerRelayParentData {
|
||||
signing_context: signing_context.clone(),
|
||||
validator_set: vec![validator.clone().into()],
|
||||
one_per_validator: hashmap!{},
|
||||
message_received_from_peer: hashmap!{},
|
||||
message_sent_to_peer: hashmap!{},
|
||||
span: PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"),
|
||||
})
|
||||
}).collect();
|
||||
state.per_relay_parent = view
|
||||
.iter()
|
||||
.map(|relay_parent| {
|
||||
(
|
||||
relay_parent.clone(),
|
||||
PerRelayParentData {
|
||||
signing_context: signing_context.clone(),
|
||||
validator_set: vec![validator.clone().into()],
|
||||
one_per_validator: hashmap! {},
|
||||
message_received_from_peer: hashmap! {},
|
||||
message_sent_to_peer: hashmap! {},
|
||||
span: PerLeafSpan::new(Arc::new(jaeger::Span::Disabled), "test"),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
state.view = view;
|
||||
|
||||
@@ -119,13 +113,10 @@ fn receive_invalid_signature() {
|
||||
let peer_b = PeerId::random();
|
||||
assert_ne!(peer_a, peer_b);
|
||||
|
||||
let signing_context = SigningContext {
|
||||
session_index: 1,
|
||||
parent_hash: hash_a.clone(),
|
||||
};
|
||||
let signing_context = SigningContext { session_index: 1, parent_hash: hash_a.clone() };
|
||||
|
||||
// another validator not part of the validatorset
|
||||
let keystore : SyncCryptoStorePtr = Arc::new(KeyStore::new());
|
||||
let keystore: SyncCryptoStorePtr = Arc::new(KeyStore::new());
|
||||
let malicious = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
|
||||
.expect("Malicious key created");
|
||||
let validator_0 = SyncCryptoStore::sr25519_generate_new(&*keystore, ValidatorId::ID, None)
|
||||
@@ -140,14 +131,20 @@ fn receive_invalid_signature() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&malicious.into(),
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
let invalid_signed_2 = executor::block_on(Signed::<AvailabilityBitfield>::sign(
|
||||
&keystore,
|
||||
payload.clone(),
|
||||
&signing_context,
|
||||
ValidatorIndex(1),
|
||||
&malicious.into(),
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let valid_signed = executor::block_on(Signed::<AvailabilityBitfield>::sign(
|
||||
&keystore,
|
||||
@@ -155,7 +152,10 @@ fn receive_invalid_signature() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator_0.into(),
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let invalid_msg = BitfieldGossipMessage {
|
||||
relay_parent: hash_a.clone(),
|
||||
@@ -171,8 +171,7 @@ fn receive_invalid_signature() {
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
let mut state = prewarmed_state(
|
||||
validator_0.into(),
|
||||
@@ -180,7 +179,9 @@ fn receive_invalid_signature() {
|
||||
valid_msg,
|
||||
vec![peer_b.clone()],
|
||||
);
|
||||
state.per_relay_parent.get_mut(&hash_a)
|
||||
state
|
||||
.per_relay_parent
|
||||
.get_mut(&hash_a)
|
||||
.unwrap()
|
||||
.validator_set
|
||||
.push(validator_1.into());
|
||||
@@ -230,7 +231,8 @@ fn receive_invalid_validator_index() {
|
||||
assert_ne!(peer_a, peer_b);
|
||||
|
||||
// validator 0 key pair
|
||||
let (mut state, signing_context, keystore, validator) = state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
let (mut state, signing_context, keystore, validator) =
|
||||
state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
|
||||
state.peer_views.insert(peer_b.clone(), view![hash_a]);
|
||||
|
||||
@@ -241,16 +243,16 @@ fn receive_invalid_validator_index() {
|
||||
&signing_context,
|
||||
ValidatorIndex(42),
|
||||
&validator,
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let msg = BitfieldGossipMessage {
|
||||
relay_parent: hash_a.clone(),
|
||||
signed_availability: signed.clone(),
|
||||
};
|
||||
let msg =
|
||||
BitfieldGossipMessage { relay_parent: hash_a.clone(), signed_availability: signed.clone() };
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
executor::block_on(async move {
|
||||
launch!(handle_network_msg(
|
||||
@@ -288,7 +290,8 @@ fn receive_duplicate_messages() {
|
||||
assert_ne!(peer_a, peer_b);
|
||||
|
||||
// validator 0 key pair
|
||||
let (mut state, signing_context, keystore, validator) = state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
let (mut state, signing_context, keystore, validator) =
|
||||
state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
|
||||
// create a signed message by validator 0
|
||||
let payload = AvailabilityBitfield(bitvec![bitvec::order::Lsb0, u8; 1u8; 32]);
|
||||
@@ -298,7 +301,10 @@ fn receive_duplicate_messages() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator,
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let msg = BitfieldGossipMessage {
|
||||
relay_parent: hash_a.clone(),
|
||||
@@ -306,8 +312,7 @@ fn receive_duplicate_messages() {
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
executor::block_on(async move {
|
||||
// send a first message
|
||||
@@ -315,10 +320,7 @@ fn receive_duplicate_messages() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
// none of our peers has any interest in any messages
|
||||
@@ -350,10 +352,7 @@ fn receive_duplicate_messages() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_a.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_a.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -371,10 +370,7 @@ fn receive_duplicate_messages() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -403,7 +399,8 @@ fn do_not_relay_message_twice() {
|
||||
assert_ne!(peer_a, peer_b);
|
||||
|
||||
// validator 0 key pair
|
||||
let (mut state, signing_context, keystore, validator) = state_with_view(our_view![hash], hash.clone());
|
||||
let (mut state, signing_context, keystore, validator) =
|
||||
state_with_view(our_view![hash], hash.clone());
|
||||
|
||||
// create a signed message by validator 0
|
||||
let payload = AvailabilityBitfield(bitvec![bitvec::order::Lsb0, u8; 1u8; 32]);
|
||||
@@ -413,7 +410,10 @@ fn do_not_relay_message_twice() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator,
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
state.peer_views.insert(peer_b.clone(), view![hash]);
|
||||
state.peer_views.insert(peer_a.clone(), view![hash]);
|
||||
@@ -424,13 +424,10 @@ fn do_not_relay_message_twice() {
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
executor::block_on(async move {
|
||||
let gossip_peers = HashSet::from_iter(vec![
|
||||
peer_a.clone(), peer_b.clone(),
|
||||
].into_iter());
|
||||
let gossip_peers = HashSet::from_iter(vec![peer_a.clone(), peer_b.clone()].into_iter());
|
||||
relay_message(
|
||||
&mut ctx,
|
||||
state.per_relay_parent.get_mut(&hash).unwrap(),
|
||||
@@ -438,7 +435,8 @@ fn do_not_relay_message_twice() {
|
||||
&mut state.peer_views,
|
||||
validator.clone(),
|
||||
msg.clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
handle.recv().await,
|
||||
@@ -471,7 +469,8 @@ fn do_not_relay_message_twice() {
|
||||
&mut state.peer_views,
|
||||
validator.clone(),
|
||||
msg.clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
handle.recv().await,
|
||||
@@ -504,7 +503,8 @@ fn changing_view() {
|
||||
assert_ne!(peer_a, peer_b);
|
||||
|
||||
// validator 0 key pair
|
||||
let (mut state, signing_context, keystore, validator) = state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
let (mut state, signing_context, keystore, validator) =
|
||||
state_with_view(our_view![hash_a, hash_b], hash_a.clone());
|
||||
|
||||
// create a signed message by validator 0
|
||||
let payload = AvailabilityBitfield(bitvec![bitvec::order::Lsb0, u8; 1u8; 32]);
|
||||
@@ -514,7 +514,10 @@ fn changing_view() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator,
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
let msg = BitfieldGossipMessage {
|
||||
relay_parent: hash_a.clone(),
|
||||
@@ -522,8 +525,7 @@ fn changing_view() {
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
executor::block_on(async move {
|
||||
launch!(handle_network_msg(
|
||||
@@ -548,10 +550,7 @@ fn changing_view() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
// gossip to the overseer
|
||||
@@ -585,10 +584,7 @@ fn changing_view() {
|
||||
));
|
||||
|
||||
assert!(state.peer_views.contains_key(&peer_b));
|
||||
assert_eq!(
|
||||
state.peer_views.get(&peer_b).expect("Must contain value for peer B"),
|
||||
&view![]
|
||||
);
|
||||
assert_eq!(state.peer_views.get(&peer_b).expect("Must contain value for peer B"), &view![]);
|
||||
|
||||
// on rx of the same message, since we are not interested,
|
||||
// should give penalty
|
||||
@@ -596,10 +592,7 @@ fn changing_view() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
// reputation change for peer B
|
||||
@@ -629,10 +622,7 @@ fn changing_view() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_a.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_a.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
// reputation change for peer B
|
||||
@@ -645,7 +635,6 @@ fn changing_view() {
|
||||
assert_eq!(rep, COST_NOT_IN_VIEW)
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@@ -673,7 +662,10 @@ fn do_not_send_message_back_to_origin() {
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator,
|
||||
)).ok().flatten().expect("should be signed");
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
state.peer_views.insert(peer_b.clone(), view![hash]);
|
||||
state.peer_views.insert(peer_a.clone(), view![hash]);
|
||||
@@ -684,8 +676,7 @@ fn do_not_send_message_back_to_origin() {
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) =
|
||||
make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
|
||||
executor::block_on(async move {
|
||||
// send a first message
|
||||
@@ -693,10 +684,7 @@ fn do_not_send_message_back_to_origin() {
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
msg.clone().into_network_message(),
|
||||
),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
|
||||
@@ -19,48 +19,39 @@
|
||||
#![deny(unused_crate_dependencies)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use futures::{prelude::*, stream::BoxStream};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
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;
|
||||
|
||||
use polkadot_overseer::gen::{
|
||||
Subsystem,
|
||||
OverseerError,
|
||||
};
|
||||
use polkadot_subsystem::{
|
||||
overseer,
|
||||
OverseerSignal,
|
||||
FromOverseer,
|
||||
SpawnedSubsystem,
|
||||
SubsystemContext,
|
||||
SubsystemSender,
|
||||
errors::{SubsystemError, SubsystemResult},
|
||||
ActivatedLeaf, ActiveLeavesUpdate,
|
||||
messages::{
|
||||
AllMessages, StatementDistributionMessage,
|
||||
NetworkBridgeMessage, CollatorProtocolMessage, NetworkBridgeEvent,
|
||||
},
|
||||
};
|
||||
use polkadot_primitives::v1::{Hash, BlockNumber};
|
||||
use polkadot_node_network_protocol::{
|
||||
PeerId, peer_set::PeerSet, View, v1 as protocol_v1, OurView, UnifiedReputationChange as Rep,
|
||||
ObservedRole,
|
||||
peer_set::PeerSet, v1 as protocol_v1, ObservedRole, OurView, PeerId,
|
||||
UnifiedReputationChange as Rep, View,
|
||||
};
|
||||
use polkadot_node_subsystem_util::metrics::{self, prometheus};
|
||||
use polkadot_overseer::gen::{OverseerError, Subsystem};
|
||||
use polkadot_primitives::v1::{BlockNumber, Hash};
|
||||
use polkadot_subsystem::{
|
||||
errors::{SubsystemError, SubsystemResult},
|
||||
messages::{
|
||||
AllMessages, CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage,
|
||||
StatementDistributionMessage,
|
||||
},
|
||||
overseer, ActivatedLeaf, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem,
|
||||
SubsystemContext, SubsystemSender,
|
||||
};
|
||||
|
||||
/// Peer set info for network initialization.
|
||||
///
|
||||
/// To be added to [`NetworkConfiguration::extra_sets`].
|
||||
pub use polkadot_node_network_protocol::peer_set::{peer_sets_info, IsAuthority};
|
||||
|
||||
use std::collections::HashSet;
|
||||
use std::collections::{HashMap, hash_map};
|
||||
use std::sync::Arc;
|
||||
use std::{
|
||||
collections::{hash_map, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
mod validator_discovery;
|
||||
|
||||
@@ -68,7 +59,7 @@ mod validator_discovery;
|
||||
///
|
||||
/// Defines the `Network` trait with an implementation for an `Arc<NetworkService>`.
|
||||
mod network;
|
||||
use network::{Network, send_message};
|
||||
use network::{send_message, Network};
|
||||
|
||||
/// Request multiplexer for combining the multiple request sources into a single `Stream` of `AllMessages`.
|
||||
mod multiplexer;
|
||||
@@ -84,7 +75,6 @@ mod tests;
|
||||
/// We use the same limit to compute the view sent to peers locally.
|
||||
const MAX_VIEW_HEADS: usize = 5;
|
||||
|
||||
|
||||
const MALFORMED_MESSAGE_COST: Rep = Rep::CostMajor("Malformed Network-bridge message");
|
||||
const UNCONNECTED_PEERSET_COST: Rep = Rep::CostMinor("Message sent to un-connected peer-set");
|
||||
const MALFORMED_VIEW_COST: Rep = Rep::CostMajor("Malformed view");
|
||||
@@ -99,36 +89,41 @@ pub struct Metrics(Option<MetricsInner>);
|
||||
|
||||
impl Metrics {
|
||||
fn on_peer_connected(&self, peer_set: PeerSet) {
|
||||
self.0.as_ref().map(|metrics| metrics
|
||||
.connected_events
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc()
|
||||
);
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.connected_events
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc()
|
||||
});
|
||||
}
|
||||
|
||||
fn on_peer_disconnected(&self, peer_set: PeerSet) {
|
||||
self.0.as_ref().map(|metrics| metrics
|
||||
.disconnected_events
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc()
|
||||
);
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.disconnected_events
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc()
|
||||
});
|
||||
}
|
||||
|
||||
fn note_peer_count(&self, peer_set: PeerSet, count: usize) {
|
||||
self.0.as_ref().map(|metrics| metrics
|
||||
.peer_count
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.set(count as u64)
|
||||
);
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.peer_count
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.set(count as u64)
|
||||
});
|
||||
}
|
||||
|
||||
fn on_notification_received(&self, peer_set: PeerSet, size: usize) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics.notifications_received
|
||||
metrics
|
||||
.notifications_received
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc();
|
||||
|
||||
metrics.bytes_received
|
||||
metrics
|
||||
.bytes_received
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc_by(size as u64);
|
||||
}
|
||||
@@ -136,22 +131,25 @@ impl Metrics {
|
||||
|
||||
fn on_notification_sent(&self, peer_set: PeerSet, size: usize, to_peers: usize) {
|
||||
if let Some(metrics) = self.0.as_ref() {
|
||||
metrics.notifications_sent
|
||||
metrics
|
||||
.notifications_sent
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc_by(to_peers as u64);
|
||||
|
||||
metrics.bytes_sent
|
||||
metrics
|
||||
.bytes_sent
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.inc_by((size * to_peers) as u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn note_desired_peer_count(&self, peer_set: PeerSet, size: usize) {
|
||||
self.0.as_ref().map(|metrics| metrics
|
||||
.desired_peer_count
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.set(size as u64)
|
||||
);
|
||||
self.0.as_ref().map(|metrics| {
|
||||
metrics
|
||||
.desired_peer_count
|
||||
.with_label_values(&[peer_set.get_protocol_name_static()])
|
||||
.set(size as u64)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +168,9 @@ struct MetricsInner {
|
||||
}
|
||||
|
||||
impl metrics::Metrics for Metrics {
|
||||
fn try_register(registry: &prometheus::Registry)
|
||||
-> std::result::Result<Self, prometheus::PrometheusError>
|
||||
{
|
||||
fn try_register(
|
||||
registry: &prometheus::Registry,
|
||||
) -> std::result::Result<Self, prometheus::PrometheusError> {
|
||||
let metrics = MetricsInner {
|
||||
peer_count: prometheus::register(
|
||||
prometheus::GaugeVec::new(
|
||||
@@ -306,10 +304,11 @@ 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 + Clone,
|
||||
Context: SubsystemContext<Message = NetworkBridgeMessage> + overseer::SubsystemContext<Message = NetworkBridgeMessage>,
|
||||
where
|
||||
Net: Network + Sync,
|
||||
AD: validator_discovery::AuthorityDiscovery + Clone,
|
||||
Context: SubsystemContext<Message = NetworkBridgeMessage>
|
||||
+ overseer::SubsystemContext<Message = NetworkBridgeMessage>,
|
||||
{
|
||||
fn start(mut self, ctx: Context) -> SpawnedSubsystem {
|
||||
// The stream of networking events has to be created at initialization, otherwise the
|
||||
@@ -319,14 +318,9 @@ impl<Net, AD, Context> Subsystem<Context, SubsystemError> for NetworkBridge<Net,
|
||||
// Swallow error because failure is fatal to the node and we log with more precision
|
||||
// within `run_network`.
|
||||
let future = run_network(self, ctx, network_stream)
|
||||
.map_err(|e| {
|
||||
SubsystemError::with_origin("network-bridge", e)
|
||||
})
|
||||
.map_err(|e| SubsystemError::with_origin("network-bridge", e))
|
||||
.boxed();
|
||||
SpawnedSubsystem {
|
||||
name: "network-bridge-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "network-bridge-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,7 +876,8 @@ async fn run_network<N, AD, Context>(
|
||||
where
|
||||
N: Network,
|
||||
AD: validator_discovery::AuthorityDiscovery + Clone,
|
||||
Context: SubsystemContext<Message=NetworkBridgeMessage> + overseer::SubsystemContext<Message=NetworkBridgeMessage>,
|
||||
Context: SubsystemContext<Message = NetworkBridgeMessage>
|
||||
+ overseer::SubsystemContext<Message = NetworkBridgeMessage>,
|
||||
{
|
||||
let shared = Shared::default();
|
||||
|
||||
@@ -902,7 +897,7 @@ where
|
||||
.get_dispute_sending()
|
||||
.expect("Gets initialized, must be `Some` on startup. qed.");
|
||||
|
||||
let (remote, network_event_handler) = handle_network_messages::<>(
|
||||
let (remote, network_event_handler) = handle_network_messages(
|
||||
ctx.sender().clone(),
|
||||
network_service.clone(),
|
||||
network_stream,
|
||||
@@ -910,16 +905,15 @@ where
|
||||
request_multiplexer,
|
||||
metrics.clone(),
|
||||
shared.clone(),
|
||||
).remote_handle();
|
||||
)
|
||||
.remote_handle();
|
||||
|
||||
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;
|
||||
ctx.send_message(DisputeDistributionMessage::DisputeSendingReceiver(dispute_receiver))
|
||||
.await;
|
||||
ctx.send_message(StatementDistributionMessage::StatementFetchingReceiver(statement_receiver))
|
||||
.await;
|
||||
|
||||
let subsystem_event_handler = handle_subsystem_messages(
|
||||
ctx,
|
||||
@@ -949,38 +943,34 @@ where
|
||||
"Received SubsystemError from overseer: {:?}",
|
||||
err
|
||||
)))
|
||||
}
|
||||
},
|
||||
Err(UnexpectedAbort::EventStreamConcluded) => {
|
||||
tracing::info!(
|
||||
target: LOG_TARGET,
|
||||
"Shutting down Network Bridge: underlying request stream concluded"
|
||||
);
|
||||
Err(SubsystemError::Context(
|
||||
"Incoming network event stream concluded.".to_string(),
|
||||
))
|
||||
}
|
||||
Err(SubsystemError::Context("Incoming network event stream concluded.".to_string()))
|
||||
},
|
||||
Err(UnexpectedAbort::RequestStreamConcluded) => {
|
||||
tracing::info!(
|
||||
target: LOG_TARGET,
|
||||
"Shutting down Network Bridge: underlying request stream concluded"
|
||||
);
|
||||
Err(SubsystemError::Context(
|
||||
"Incoming network request stream concluded".to_string(),
|
||||
))
|
||||
}
|
||||
Err(SubsystemError::Context("Incoming network request stream concluded".to_string()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_view(live_heads: impl DoubleEndedIterator<Item = Hash>, finalized_number: BlockNumber) -> View {
|
||||
View::new(
|
||||
live_heads.take(MAX_VIEW_HEADS),
|
||||
finalized_number,
|
||||
)
|
||||
fn construct_view(
|
||||
live_heads: impl DoubleEndedIterator<Item = Hash>,
|
||||
finalized_number: BlockNumber,
|
||||
) -> View {
|
||||
View::new(live_heads.take(MAX_VIEW_HEADS), finalized_number)
|
||||
}
|
||||
|
||||
fn update_our_view(
|
||||
net: &mut impl Network,
|
||||
ctx: &mut impl SubsystemContext<Message=NetworkBridgeMessage, AllMessages=AllMessages>,
|
||||
ctx: &mut impl SubsystemContext<Message = NetworkBridgeMessage, AllMessages = AllMessages>,
|
||||
live_heads: &[ActivatedLeaf],
|
||||
shared: &Shared,
|
||||
finalized_number: BlockNumber,
|
||||
@@ -997,17 +987,14 @@ fn update_our_view(
|
||||
// If this is the first view update since becoming active, but our view is empty,
|
||||
// there is no need to send anything.
|
||||
match shared.local_view {
|
||||
Some(ref v) if v.check_heads_eq(&new_view) => {
|
||||
return;
|
||||
}
|
||||
Some(ref v) if v.check_heads_eq(&new_view) => return,
|
||||
None if live_heads.is_empty() => {
|
||||
shared.local_view = Some(new_view);
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
_ => {
|
||||
shared.local_view = Some(new_view.clone());
|
||||
}
|
||||
|
||||
},
|
||||
}
|
||||
|
||||
(
|
||||
@@ -1023,12 +1010,7 @@ fn update_our_view(
|
||||
metrics,
|
||||
);
|
||||
|
||||
send_collation_message(
|
||||
net,
|
||||
collation_peers,
|
||||
WireMessage::ViewUpdate(new_view),
|
||||
metrics,
|
||||
);
|
||||
send_collation_message(net, collation_peers, WireMessage::ViewUpdate(new_view), metrics);
|
||||
|
||||
let our_view = OurView::new(
|
||||
live_heads.iter().take(MAX_VIEW_HEADS).cloned().map(|a| (a.hash, a.span)),
|
||||
@@ -1056,9 +1038,7 @@ fn handle_peer_messages<M>(
|
||||
metrics: &Metrics,
|
||||
) -> (Vec<NetworkBridgeEvent<M>>, Vec<Rep>) {
|
||||
let peer_data = match peers.get_mut(&peer) {
|
||||
None => {
|
||||
return (Vec::new(), vec![UNCONNECTED_PEERSET_COST]);
|
||||
},
|
||||
None => return (Vec::new(), vec![UNCONNECTED_PEERSET_COST]),
|
||||
Some(d) => d,
|
||||
};
|
||||
|
||||
@@ -1083,15 +1063,11 @@ fn handle_peer_messages<M>(
|
||||
} else {
|
||||
peer_data.view = new_view;
|
||||
|
||||
NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
peer_data.view.clone(),
|
||||
)
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), peer_data.view.clone())
|
||||
}
|
||||
}
|
||||
WireMessage::ProtocolMessage(message) => {
|
||||
NetworkBridgeEvent::PeerMessage(peer.clone(), message)
|
||||
}
|
||||
},
|
||||
WireMessage::ProtocolMessage(message) =>
|
||||
NetworkBridgeEvent::PeerMessage(peer.clone(), message),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1116,24 +1092,23 @@ fn send_collation_message(
|
||||
send_message(net, peers, PeerSet::Collation, message, metrics)
|
||||
}
|
||||
|
||||
|
||||
async fn dispatch_validation_event_to_all(
|
||||
event: NetworkBridgeEvent<protocol_v1::ValidationProtocol>,
|
||||
ctx: &mut impl SubsystemSender
|
||||
ctx: &mut impl SubsystemSender,
|
||||
) {
|
||||
dispatch_validation_events_to_all(std::iter::once(event), ctx).await
|
||||
}
|
||||
|
||||
async fn dispatch_collation_event_to_all(
|
||||
event: NetworkBridgeEvent<protocol_v1::CollationProtocol>,
|
||||
ctx: &mut impl SubsystemSender
|
||||
ctx: &mut impl SubsystemSender,
|
||||
) {
|
||||
dispatch_collation_events_to_all(std::iter::once(event), ctx).await
|
||||
}
|
||||
|
||||
fn dispatch_validation_event_to_all_unbounded(
|
||||
event: NetworkBridgeEvent<protocol_v1::ValidationProtocol>,
|
||||
ctx: &mut impl SubsystemSender
|
||||
ctx: &mut impl SubsystemSender,
|
||||
) {
|
||||
for msg in AllMessages::dispatch_iter(event) {
|
||||
ctx.send_unbounded_message(msg);
|
||||
@@ -1142,36 +1117,30 @@ fn dispatch_validation_event_to_all_unbounded(
|
||||
|
||||
fn dispatch_collation_event_to_all_unbounded(
|
||||
event: NetworkBridgeEvent<protocol_v1::CollationProtocol>,
|
||||
ctx: &mut impl SubsystemSender
|
||||
ctx: &mut impl SubsystemSender,
|
||||
) {
|
||||
if let Some(msg) = event.focus().ok().map(CollatorProtocolMessage::NetworkBridgeUpdateV1) {
|
||||
ctx.send_unbounded_message(msg.into());
|
||||
}
|
||||
}
|
||||
|
||||
async fn dispatch_validation_events_to_all<I>(
|
||||
events: I,
|
||||
ctx: &mut impl SubsystemSender
|
||||
)
|
||||
where
|
||||
I: IntoIterator<Item = NetworkBridgeEvent<protocol_v1::ValidationProtocol>>,
|
||||
I::IntoIter: Send,
|
||||
async fn dispatch_validation_events_to_all<I>(events: I, ctx: &mut impl SubsystemSender)
|
||||
where
|
||||
I: IntoIterator<Item = NetworkBridgeEvent<protocol_v1::ValidationProtocol>>,
|
||||
I::IntoIter: Send,
|
||||
{
|
||||
ctx.send_messages(events.into_iter().flat_map(AllMessages::dispatch_iter)).await
|
||||
}
|
||||
|
||||
async fn dispatch_collation_events_to_all<I>(
|
||||
events: I,
|
||||
ctx: &mut impl SubsystemSender
|
||||
)
|
||||
where
|
||||
I: IntoIterator<Item = NetworkBridgeEvent<protocol_v1::CollationProtocol>>,
|
||||
I::IntoIter: Send,
|
||||
async fn dispatch_collation_events_to_all<I>(events: I, ctx: &mut impl SubsystemSender)
|
||||
where
|
||||
I: IntoIterator<Item = NetworkBridgeEvent<protocol_v1::CollationProtocol>>,
|
||||
I::IntoIter: Send,
|
||||
{
|
||||
let messages_for = |event: NetworkBridgeEvent<protocol_v1::CollationProtocol>| {
|
||||
event.focus().ok().map(|m| AllMessages::CollatorProtocol(
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(m)
|
||||
))
|
||||
event.focus().ok().map(|m| {
|
||||
AllMessages::CollatorProtocol(CollatorProtocolMessage::NetworkBridgeUpdateV1(m))
|
||||
})
|
||||
};
|
||||
|
||||
ctx.send_messages(events.into_iter().flat_map(messages_for)).await
|
||||
|
||||
@@ -14,18 +14,18 @@
|
||||
// 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 std::unreachable;
|
||||
use std::{pin::Pin, unreachable};
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use futures::stream::{FusedStream, Stream};
|
||||
use futures::task::{Context, Poll};
|
||||
use futures::{
|
||||
channel::mpsc,
|
||||
stream::{FusedStream, Stream},
|
||||
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 sc_network::{config as network, PeerId};
|
||||
|
||||
use polkadot_node_network_protocol::request_response::{
|
||||
request::IncomingRequest, v1, Protocol, RequestResponseConfig,
|
||||
@@ -72,33 +72,23 @@ impl RequestMultiplexer {
|
||||
|
||||
// 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)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
).expect("Statement fetching must be registered. qed.");
|
||||
let index = receivers
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(
|
||||
|(i, (p, _))| if let Protocol::StatementFetching = p { Some(i) } else { None },
|
||||
)
|
||||
.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 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,
|
||||
)
|
||||
(Self { receivers, statement_fetching, dispute_sending, next_poll: 0 }, cfgs)
|
||||
}
|
||||
|
||||
/// Get the receiver for handling statement fetching requests.
|
||||
@@ -132,7 +122,7 @@ impl Stream for RequestMultiplexer {
|
||||
// Avoid panic:
|
||||
if rx.is_terminated() {
|
||||
// Early return, we don't want to update next_poll.
|
||||
return Poll::Ready(None);
|
||||
return Poll::Ready(None)
|
||||
}
|
||||
i += 1;
|
||||
count -= 1;
|
||||
@@ -142,8 +132,8 @@ impl Stream for RequestMultiplexer {
|
||||
Poll::Ready(None) => return Poll::Ready(None),
|
||||
Poll::Ready(Some(v)) => {
|
||||
result = Poll::Ready(Some(multiplex_single(*p, v)));
|
||||
break;
|
||||
}
|
||||
break
|
||||
},
|
||||
}
|
||||
}
|
||||
self.next_poll = i;
|
||||
@@ -155,7 +145,7 @@ impl FusedStream for RequestMultiplexer {
|
||||
fn is_terminated(&self) -> bool {
|
||||
let len = self.receivers.len();
|
||||
if len == 0 {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
let (_, rx) = &self.receivers[self.next_poll % len];
|
||||
rx.is_terminated()
|
||||
@@ -165,11 +155,7 @@ impl FusedStream for RequestMultiplexer {
|
||||
/// Convert a single raw incoming request into a `MultiplexMessage`.
|
||||
fn multiplex_single(
|
||||
p: Protocol,
|
||||
network::IncomingRequest {
|
||||
payload,
|
||||
peer,
|
||||
pending_response,
|
||||
}: network::IncomingRequest,
|
||||
network::IncomingRequest { payload, peer, pending_response }: network::IncomingRequest,
|
||||
) -> Result<AllMessages, RequestMultiplexError> {
|
||||
let r = match p {
|
||||
Protocol::ChunkFetching => AllMessages::from(IncomingRequest::new(
|
||||
@@ -194,10 +180,10 @@ 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)
|
||||
}
|
||||
@@ -211,8 +197,7 @@ fn decode_with_peer<Req: Decode>(
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use futures::prelude::*;
|
||||
use futures::stream::FusedStream;
|
||||
use futures::{prelude::*, stream::FusedStream};
|
||||
|
||||
use super::RequestMultiplexer;
|
||||
#[test]
|
||||
|
||||
@@ -14,23 +14,21 @@
|
||||
// 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::borrow::Cow;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
use std::{borrow::Cow, collections::HashSet, sync::Arc};
|
||||
|
||||
use async_trait::async_trait;
|
||||
use futures::prelude::*;
|
||||
use futures::stream::BoxStream;
|
||||
use futures::{prelude::*, stream::BoxStream};
|
||||
|
||||
use parity_scale_codec::Encode;
|
||||
|
||||
use sc_network::Event as NetworkEvent;
|
||||
use sc_network::{IfDisconnected, NetworkService, OutboundFailure, RequestFailure};
|
||||
use sc_network::{config::parse_addr, multiaddr::Multiaddr};
|
||||
use sc_network::{
|
||||
config::parse_addr, multiaddr::Multiaddr, Event as NetworkEvent, IfDisconnected,
|
||||
NetworkService, OutboundFailure, RequestFailure,
|
||||
};
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
peer_set::PeerSet,
|
||||
request_response::{OutgoingRequest, Requests, Recipient},
|
||||
request_response::{OutgoingRequest, Recipient, Requests},
|
||||
PeerId, UnifiedReputationChange as Rep,
|
||||
};
|
||||
use polkadot_primitives::v1::{AuthorityDiscoveryId, Block, Hash};
|
||||
@@ -50,8 +48,7 @@ pub(crate) fn send_message<M>(
|
||||
peer_set: PeerSet,
|
||||
message: M,
|
||||
metrics: &super::Metrics,
|
||||
)
|
||||
where
|
||||
) where
|
||||
M: Encode + Clone,
|
||||
{
|
||||
let message = {
|
||||
@@ -112,12 +109,7 @@ pub trait Network: Clone + Send + 'static {
|
||||
fn disconnect_peer(&self, who: PeerId, peer_set: PeerSet);
|
||||
|
||||
/// Write a notification to a peer on the given peer-set's protocol.
|
||||
fn write_notification(
|
||||
&self,
|
||||
who: PeerId,
|
||||
peer_set: PeerSet,
|
||||
message: Vec<u8>,
|
||||
);
|
||||
fn write_notification(&self, who: PeerId, peer_set: PeerSet, message: Vec<u8>);
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@@ -170,38 +162,29 @@ impl Network for Arc<NetworkService<Block, Hash>> {
|
||||
req: Requests,
|
||||
if_disconnected: IfDisconnected,
|
||||
) {
|
||||
let (
|
||||
protocol,
|
||||
OutgoingRequest {
|
||||
peer,
|
||||
payload,
|
||||
pending_response,
|
||||
},
|
||||
) = req.encode_request();
|
||||
let (protocol, OutgoingRequest { peer, payload, pending_response }) = req.encode_request();
|
||||
|
||||
let peer_id = match peer {
|
||||
Recipient::Peer(peer_id) => Some(peer_id),
|
||||
Recipient::Peer(peer_id) => Some(peer_id),
|
||||
Recipient::Authority(authority) => {
|
||||
let mut found_peer_id = None;
|
||||
// Note: `get_addresses_by_authority_id` searched in a cache, and it thus expected
|
||||
// to be very quick.
|
||||
for addr in authority_discovery
|
||||
.get_addresses_by_authority_id(authority).await
|
||||
.into_iter().flat_map(|list| list.into_iter())
|
||||
.get_addresses_by_authority_id(authority)
|
||||
.await
|
||||
.into_iter()
|
||||
.flat_map(|list| list.into_iter())
|
||||
{
|
||||
let (peer_id, addr) = match parse_addr(addr) {
|
||||
Ok(v) => v,
|
||||
Err(_) => continue,
|
||||
};
|
||||
NetworkService::add_known_address(
|
||||
&*self,
|
||||
peer_id.clone(),
|
||||
addr,
|
||||
);
|
||||
NetworkService::add_known_address(&*self, peer_id.clone(), addr);
|
||||
found_peer_id = Some(peer_id);
|
||||
}
|
||||
found_peer_id
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let peer_id = match peer_id {
|
||||
@@ -214,10 +197,10 @@ impl Network for Arc<NetworkService<Block, Hash>> {
|
||||
target: LOG_TARGET,
|
||||
"Sending failed request response failed."
|
||||
),
|
||||
Ok(_) => {}
|
||||
Ok(_) => {},
|
||||
}
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
Some(peer_id) => peer_id,
|
||||
};
|
||||
|
||||
@@ -240,7 +223,8 @@ pub async fn get_peer_id_by_authority_id<AD: AuthorityDiscovery>(
|
||||
// Note: `get_addresses_by_authority_id` searched in a cache, and it thus expected
|
||||
// to be very quick.
|
||||
authority_discovery
|
||||
.get_addresses_by_authority_id(authority).await
|
||||
.get_addresses_by_authority_id(authority)
|
||||
.await
|
||||
.into_iter()
|
||||
.flat_map(|list| list.into_iter())
|
||||
.find_map(|addr| parse_addr(addr).ok().map(|(p, _)| p))
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -25,9 +25,9 @@ use futures::channel::oneshot;
|
||||
|
||||
use sc_network::multiaddr::Multiaddr;
|
||||
|
||||
use polkadot_primitives::v1::AuthorityDiscoveryId;
|
||||
use polkadot_node_network_protocol::peer_set::{PeerSet, PerPeerSet};
|
||||
pub use polkadot_node_network_protocol::authority_discovery::AuthorityDiscovery;
|
||||
use polkadot_node_network_protocol::peer_set::{PeerSet, PerPeerSet};
|
||||
use polkadot_primitives::v1::AuthorityDiscoveryId;
|
||||
|
||||
const LOG_TARGET: &str = "parachain::validator-discovery";
|
||||
|
||||
@@ -44,10 +44,7 @@ struct StatePerPeerSet {
|
||||
|
||||
impl<N: Network, AD: AuthorityDiscovery> Service<N, AD> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Default::default(),
|
||||
_phantom: PhantomData,
|
||||
}
|
||||
Self { state: Default::default(), _phantom: PhantomData }
|
||||
}
|
||||
|
||||
/// On a new connection request, a peer set update will be issued.
|
||||
@@ -70,24 +67,27 @@ impl<N: Network, AD: AuthorityDiscovery> Service<N, AD> {
|
||||
let mut newly_requested = HashSet::new();
|
||||
let requested = validator_ids.len();
|
||||
for authority in validator_ids.into_iter() {
|
||||
let result = authority_discovery_service.get_addresses_by_authority_id(authority.clone()).await;
|
||||
let result = authority_discovery_service
|
||||
.get_addresses_by_authority_id(authority.clone())
|
||||
.await;
|
||||
if let Some(addresses) = result {
|
||||
newly_requested.extend(addresses);
|
||||
} else {
|
||||
failed_to_resolve += 1;
|
||||
tracing::debug!(target: LOG_TARGET, "Authority Discovery couldn't resolve {:?}", authority);
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Authority Discovery couldn't resolve {:?}",
|
||||
authority
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
let state = &mut self.state[peer_set];
|
||||
// clean up revoked requests
|
||||
let multiaddr_to_remove: HashSet<_> = state.previously_requested
|
||||
.difference(&newly_requested)
|
||||
.cloned()
|
||||
.collect();
|
||||
let multiaddr_to_add: HashSet<_> = newly_requested.difference(&state.previously_requested)
|
||||
.cloned()
|
||||
.collect();
|
||||
let multiaddr_to_remove: HashSet<_> =
|
||||
state.previously_requested.difference(&newly_requested).cloned().collect();
|
||||
let multiaddr_to_add: HashSet<_> =
|
||||
newly_requested.difference(&state.previously_requested).cloned().collect();
|
||||
state.previously_requested = newly_requested;
|
||||
|
||||
tracing::debug!(
|
||||
@@ -101,17 +101,16 @@ impl<N: Network, AD: AuthorityDiscovery> Service<N, AD> {
|
||||
);
|
||||
// ask the network to connect to these nodes and not disconnect
|
||||
// from them until removed from the set
|
||||
if let Err(e) = network_service.add_to_peers_set(
|
||||
peer_set.into_protocol_name(),
|
||||
multiaddr_to_add,
|
||||
).await {
|
||||
if let Err(e) = network_service
|
||||
.add_to_peers_set(peer_set.into_protocol_name(), multiaddr_to_add)
|
||||
.await
|
||||
{
|
||||
tracing::warn!(target: LOG_TARGET, err = ?e, "AuthorityDiscoveryService returned an invalid multiaddress");
|
||||
}
|
||||
// the addresses are known to be valid
|
||||
let _ = network_service.remove_from_peers_set(
|
||||
peer_set.into_protocol_name(),
|
||||
multiaddr_to_remove
|
||||
).await;
|
||||
let _ = network_service
|
||||
.remove_from_peers_set(peer_set.into_protocol_name(), multiaddr_to_remove)
|
||||
.await;
|
||||
|
||||
let _ = failed.send(failed_to_resolve);
|
||||
|
||||
@@ -124,12 +123,12 @@ mod tests {
|
||||
use super::*;
|
||||
use crate::network::Network;
|
||||
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
use futures::stream::BoxStream;
|
||||
use async_trait::async_trait;
|
||||
use futures::stream::BoxStream;
|
||||
use polkadot_node_network_protocol::{request_response::request::Requests, PeerId};
|
||||
use sc_network::{Event as NetworkEvent, IfDisconnected};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use polkadot_node_network_protocol::{PeerId, request_response::request::Requests};
|
||||
use std::{borrow::Cow, collections::HashMap};
|
||||
|
||||
fn new_service() -> Service<TestNetwork, TestAuthorityDiscovery> {
|
||||
Service::new()
|
||||
@@ -156,13 +155,8 @@ mod tests {
|
||||
let authorities = known_authorities();
|
||||
let multiaddr = known_multiaddr();
|
||||
Self {
|
||||
by_authority_id: authorities.iter()
|
||||
.cloned()
|
||||
.zip(multiaddr.into_iter())
|
||||
.collect(),
|
||||
by_peer_id: peer_ids.into_iter()
|
||||
.zip(authorities.into_iter())
|
||||
.collect(),
|
||||
by_authority_id: authorities.iter().cloned().zip(multiaddr.into_iter()).collect(),
|
||||
by_peer_id: peer_ids.into_iter().zip(authorities.into_iter()).collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -173,17 +167,30 @@ mod tests {
|
||||
panic!()
|
||||
}
|
||||
|
||||
async fn add_to_peers_set(&mut self, _protocol: Cow<'static, str>, multiaddresses: HashSet<Multiaddr>) -> Result<(), String> {
|
||||
async fn add_to_peers_set(
|
||||
&mut self,
|
||||
_protocol: Cow<'static, str>,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
self.peers_set.extend(multiaddresses.into_iter());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn remove_from_peers_set(&mut self, _protocol: Cow<'static, str>, multiaddresses: HashSet<Multiaddr>) -> Result<(), String> {
|
||||
async fn remove_from_peers_set(
|
||||
&mut self,
|
||||
_protocol: Cow<'static, str>,
|
||||
multiaddresses: HashSet<Multiaddr>,
|
||||
) -> Result<(), String> {
|
||||
self.peers_set.retain(|elem| !multiaddresses.contains(elem));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn start_request<AD: AuthorityDiscovery>(&self, _: &mut AD, _: Requests, _: IfDisconnected) {
|
||||
async fn start_request<AD: AuthorityDiscovery>(
|
||||
&self,
|
||||
_: &mut AD,
|
||||
_: Requests,
|
||||
_: IfDisconnected,
|
||||
) {
|
||||
}
|
||||
|
||||
fn report_peer(&self, _: PeerId, _: crate::Rep) {
|
||||
@@ -194,33 +201,33 @@ mod tests {
|
||||
panic!()
|
||||
}
|
||||
|
||||
fn write_notification(
|
||||
&self,
|
||||
_: PeerId,
|
||||
_: PeerSet,
|
||||
_: Vec<u8>,
|
||||
) {
|
||||
fn write_notification(&self, _: PeerId, _: PeerSet, _: Vec<u8>) {
|
||||
panic!()
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl AuthorityDiscovery for TestAuthorityDiscovery {
|
||||
async fn get_addresses_by_authority_id(&mut self, authority: AuthorityDiscoveryId) -> Option<Vec<Multiaddr>> {
|
||||
async fn get_addresses_by_authority_id(
|
||||
&mut self,
|
||||
authority: AuthorityDiscoveryId,
|
||||
) -> Option<Vec<Multiaddr>> {
|
||||
self.by_authority_id.get(&authority).cloned().map(|addr| vec![addr])
|
||||
}
|
||||
|
||||
async fn get_authority_id_by_peer_id(&mut self, peer_id: PeerId) -> Option<AuthorityDiscoveryId> {
|
||||
async fn get_authority_id_by_peer_id(
|
||||
&mut self,
|
||||
peer_id: PeerId,
|
||||
) -> Option<AuthorityDiscoveryId> {
|
||||
self.by_peer_id.get(&peer_id).cloned()
|
||||
}
|
||||
}
|
||||
|
||||
fn known_authorities() -> Vec<AuthorityDiscoveryId> {
|
||||
[
|
||||
Sr25519Keyring::Alice,
|
||||
Sr25519Keyring::Bob,
|
||||
Sr25519Keyring::Charlie,
|
||||
].iter().map(|k| k.public().into()).collect()
|
||||
[Sr25519Keyring::Alice, Sr25519Keyring::Bob, Sr25519Keyring::Charlie]
|
||||
.iter()
|
||||
.map(|k| k.public().into())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn known_peer_ids() -> Vec<PeerId> {
|
||||
@@ -245,26 +252,20 @@ mod tests {
|
||||
|
||||
futures::executor::block_on(async move {
|
||||
let (failed, _) = oneshot::channel();
|
||||
let (ns, ads) = service.on_request(
|
||||
vec![authority_ids[0].clone()],
|
||||
PeerSet::Validation,
|
||||
failed,
|
||||
ns,
|
||||
ads,
|
||||
).await;
|
||||
let (ns, ads) = service
|
||||
.on_request(vec![authority_ids[0].clone()], PeerSet::Validation, failed, ns, ads)
|
||||
.await;
|
||||
|
||||
let (failed, _) = oneshot::channel();
|
||||
let (_, ads) = service.on_request(
|
||||
vec![authority_ids[1].clone()],
|
||||
PeerSet::Validation,
|
||||
failed,
|
||||
ns,
|
||||
ads,
|
||||
).await;
|
||||
let (_, ads) = service
|
||||
.on_request(vec![authority_ids[1].clone()], PeerSet::Validation, failed, ns, ads)
|
||||
.await;
|
||||
|
||||
let state = &service.state[PeerSet::Validation];
|
||||
assert_eq!(state.previously_requested.len(), 1);
|
||||
assert!(state.previously_requested.contains(ads.by_authority_id.get(&authority_ids[1]).unwrap()));
|
||||
assert!(state
|
||||
.previously_requested
|
||||
.contains(ads.by_authority_id.get(&authority_ids[1]).unwrap()));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -279,17 +280,21 @@ mod tests {
|
||||
futures::executor::block_on(async move {
|
||||
let (failed, failed_rx) = oneshot::channel();
|
||||
let unknown = Sr25519Keyring::Ferdie.public().into();
|
||||
let (_, ads) = service.on_request(
|
||||
vec![authority_ids[0].clone(), unknown],
|
||||
PeerSet::Validation,
|
||||
failed,
|
||||
ns,
|
||||
ads,
|
||||
).await;
|
||||
let (_, ads) = service
|
||||
.on_request(
|
||||
vec![authority_ids[0].clone(), unknown],
|
||||
PeerSet::Validation,
|
||||
failed,
|
||||
ns,
|
||||
ads,
|
||||
)
|
||||
.await;
|
||||
|
||||
let state = &service.state[PeerSet::Validation];
|
||||
assert_eq!(state.previously_requested.len(), 1);
|
||||
assert!(state.previously_requested.contains(ads.by_authority_id.get(&authority_ids[0]).unwrap()));
|
||||
assert!(state
|
||||
.previously_requested
|
||||
.contains(ads.by_authority_id.get(&authority_ids[0]).unwrap()));
|
||||
|
||||
let failed = failed_rx.await.unwrap();
|
||||
assert_eq!(failed, 1);
|
||||
|
||||
@@ -14,44 +14,49 @@
|
||||
// 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, VecDeque}, pin::Pin, time::Duration};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet, VecDeque},
|
||||
pin::Pin,
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use futures::{FutureExt, StreamExt, channel::oneshot, stream::FuturesUnordered, select, Future};
|
||||
use futures::{channel::oneshot, select, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
||||
use sp_core::Pair;
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
peer_set::PeerSet,
|
||||
request_response::{
|
||||
request::OutgoingResponse,
|
||||
v1::{CollationFetchingRequest, CollationFetchingResponse},
|
||||
IncomingRequest,
|
||||
},
|
||||
v1 as protocol_v1, OurView, PeerId, UnifiedReputationChange as Rep, View,
|
||||
};
|
||||
use polkadot_node_primitives::{PoV, SignedFullStatement, Statement};
|
||||
use polkadot_node_subsystem_util::{
|
||||
metrics::{self, prometheus},
|
||||
runtime::{get_availability_cores, get_group_rotation_info, RuntimeInfo},
|
||||
TimeoutExt,
|
||||
};
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, CandidateHash, CandidateReceipt, CollatorPair, CoreIndex, CoreState, GroupIndex, Hash,
|
||||
Id as ParaId,
|
||||
AuthorityDiscoveryId, CandidateHash, CandidateReceipt, CollatorPair, CoreIndex, CoreState,
|
||||
GroupIndex, Hash, Id as ParaId,
|
||||
};
|
||||
use polkadot_subsystem::{
|
||||
overseer,
|
||||
FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, jaeger,
|
||||
messages::{
|
||||
CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage,
|
||||
},
|
||||
jaeger,
|
||||
messages::{CollatorProtocolMessage, NetworkBridgeEvent, NetworkBridgeMessage},
|
||||
overseer, FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext,
|
||||
};
|
||||
use polkadot_node_network_protocol::{
|
||||
OurView, PeerId, UnifiedReputationChange as Rep, View, peer_set::PeerSet,
|
||||
request_response::{
|
||||
IncomingRequest, request::OutgoingResponse, v1::{CollationFetchingRequest, CollationFetchingResponse}
|
||||
},
|
||||
v1 as protocol_v1,
|
||||
};
|
||||
use polkadot_node_subsystem_util::{
|
||||
TimeoutExt,
|
||||
metrics::{self, prometheus},
|
||||
runtime::{RuntimeInfo, get_availability_cores, get_group_rotation_info}
|
||||
};
|
||||
use polkadot_node_primitives::{SignedFullStatement, Statement, PoV};
|
||||
|
||||
use crate::error::{Fatal, NonFatal, log_error};
|
||||
use super::{LOG_TARGET, Result};
|
||||
use super::{Result, LOG_TARGET};
|
||||
use crate::error::{log_error, Fatal, NonFatal};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
const COST_UNEXPECTED_MESSAGE: Rep = Rep::CostMinor("An unexpected message");
|
||||
const COST_APPARENT_FLOOD: Rep = Rep::CostMinor("Message received when previous one was still being processed");
|
||||
const COST_APPARENT_FLOOD: Rep =
|
||||
Rep::CostMinor("Message received when previous one was still being processed");
|
||||
|
||||
/// Time after starting an upload to a validator we will start another one to the next validator,
|
||||
/// even if the upload was not finished yet.
|
||||
@@ -102,9 +107,9 @@ struct MetricsInner {
|
||||
}
|
||||
|
||||
impl metrics::Metrics for Metrics {
|
||||
fn try_register(registry: &prometheus::Registry)
|
||||
-> std::result::Result<Self, prometheus::PrometheusError>
|
||||
{
|
||||
fn try_register(
|
||||
registry: &prometheus::Registry,
|
||||
) -> std::result::Result<Self, prometheus::PrometheusError> {
|
||||
let metrics = MetricsInner {
|
||||
advertisements_made: prometheus::register(
|
||||
prometheus::Counter::new(
|
||||
@@ -128,12 +133,10 @@ impl metrics::Metrics for Metrics {
|
||||
registry,
|
||||
)?,
|
||||
process_msg: prometheus::register(
|
||||
prometheus::Histogram::with_opts(
|
||||
prometheus::HistogramOpts::new(
|
||||
"parachain_collator_protocol_collator_process_msg",
|
||||
"Time spent within `collator_protocol_collator::process_msg`",
|
||||
)
|
||||
)?,
|
||||
prometheus::Histogram::with_opts(prometheus::HistogramOpts::new(
|
||||
"parachain_collator_protocol_collator_process_msg",
|
||||
"Time spent within `collator_protocol_collator::process_msg`",
|
||||
))?,
|
||||
registry,
|
||||
)?,
|
||||
};
|
||||
@@ -157,8 +160,11 @@ struct ValidatorGroup {
|
||||
|
||||
impl ValidatorGroup {
|
||||
/// Returns `true` if we should advertise our collation to the given peer.
|
||||
fn should_advertise_to(&self, peer_ids: &HashMap<PeerId, AuthorityDiscoveryId>, peer: &PeerId)
|
||||
-> bool {
|
||||
fn should_advertise_to(
|
||||
&self,
|
||||
peer_ids: &HashMap<PeerId, AuthorityDiscoveryId>,
|
||||
peer: &PeerId,
|
||||
) -> bool {
|
||||
match peer_ids.get(peer) {
|
||||
Some(discovery_id) => !self.advertised_to.contains(discovery_id),
|
||||
None => false,
|
||||
@@ -166,7 +172,11 @@ impl ValidatorGroup {
|
||||
}
|
||||
|
||||
/// Should be called after we advertised our collation to the given `peer` to keep track of it.
|
||||
fn advertised_to_peer(&mut self, peer_ids: &HashMap<PeerId, AuthorityDiscoveryId>, peer: &PeerId) {
|
||||
fn advertised_to_peer(
|
||||
&mut self,
|
||||
peer_ids: &HashMap<PeerId, AuthorityDiscoveryId>,
|
||||
peer: &PeerId,
|
||||
) {
|
||||
if let Some(validator_id) = peer_ids.get(peer) {
|
||||
self.advertised_to.insert(validator_id.clone());
|
||||
}
|
||||
@@ -175,10 +185,7 @@ impl ValidatorGroup {
|
||||
|
||||
impl From<HashSet<AuthorityDiscoveryId>> for ValidatorGroup {
|
||||
fn from(discovery_ids: HashSet<AuthorityDiscoveryId>) -> Self {
|
||||
Self {
|
||||
discovery_ids,
|
||||
advertised_to: HashSet::new(),
|
||||
}
|
||||
Self { discovery_ids, advertised_to: HashSet::new() }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +236,8 @@ struct WaitingCollationFetches {
|
||||
waiting_peers: HashSet<PeerId>,
|
||||
}
|
||||
|
||||
type ActiveCollationFetches = FuturesUnordered<Pin<Box<dyn Future<Output = (Hash, PeerId)> + Send + 'static>>>;
|
||||
type ActiveCollationFetches =
|
||||
FuturesUnordered<Pin<Box<dyn Future<Output = (Hash, PeerId)> + Send + 'static>>>;
|
||||
|
||||
struct State {
|
||||
/// Our network peer id.
|
||||
@@ -344,12 +352,12 @@ where
|
||||
"distribute collation message parent is outside of our view",
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// We have already seen collation for this relay parent.
|
||||
if state.collations.contains_key(&relay_parent) {
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
// Determine which core the para collated-on is assigned to.
|
||||
@@ -365,12 +373,12 @@ where
|
||||
);
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
// Determine the group on that core and the next group on that core.
|
||||
let (current_validators, next_validators) =
|
||||
determine_our_validators(ctx, runtime, our_core, num_cores, relay_parent,).await?;
|
||||
determine_our_validators(ctx, runtime, our_core, num_cores, relay_parent).await?;
|
||||
|
||||
if current_validators.validators.is_empty() && next_validators.validators.is_empty() {
|
||||
tracing::warn!(
|
||||
@@ -379,7 +387,7 @@ where
|
||||
"there are no validators assigned to core",
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
tracing::debug!(
|
||||
@@ -394,16 +402,19 @@ where
|
||||
"Accepted collation, connecting to validators."
|
||||
);
|
||||
|
||||
let validator_group: HashSet<_> = current_validators.validators.iter().map(Clone::clone).collect();
|
||||
let validator_group: HashSet<_> =
|
||||
current_validators.validators.iter().map(Clone::clone).collect();
|
||||
|
||||
// Issue a discovery request for the validators of the current group and the next group:
|
||||
connect_to_validators(
|
||||
ctx,
|
||||
current_validators.validators
|
||||
current_validators
|
||||
.validators
|
||||
.into_iter()
|
||||
.chain(next_validators.validators.into_iter())
|
||||
.collect(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
state.our_validators_groups.insert(relay_parent, validator_group.into());
|
||||
|
||||
@@ -411,7 +422,9 @@ where
|
||||
state.collation_result_senders.insert(receipt.hash(), result_sender);
|
||||
}
|
||||
|
||||
state.collations.insert(relay_parent, Collation { receipt, pov, status: CollationStatus::Created });
|
||||
state
|
||||
.collations
|
||||
.insert(relay_parent, Collation { receipt, pov, status: CollationStatus::Created });
|
||||
|
||||
let interested = state.peers_interested_in_leaf(&relay_parent);
|
||||
// Make sure already connected peers get collations:
|
||||
@@ -438,7 +451,7 @@ where
|
||||
for (idx, core) in cores.iter().enumerate() {
|
||||
if let CoreState::Scheduled(occupied) = core {
|
||||
if occupied.para_id == para_id {
|
||||
return Ok(Some(((idx as u32).into(), cores.len())));
|
||||
return Ok(Some(((idx as u32).into(), cores.len())))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -470,7 +483,8 @@ where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
let session_index = runtime.get_session_index(ctx.sender(), relay_parent).await?;
|
||||
let info = &runtime.get_session_info_by_index(ctx.sender(), relay_parent, session_index)
|
||||
let info = &runtime
|
||||
.get_session_info_by_index(ctx.sender(), relay_parent, session_index)
|
||||
.await?
|
||||
.session_info;
|
||||
tracing::debug!(target: LOG_TARGET, ?session_index, "Received session info");
|
||||
@@ -478,34 +492,31 @@ where
|
||||
let rotation_info = get_group_rotation_info(ctx, relay_parent).await?;
|
||||
|
||||
let current_group_index = rotation_info.group_for_core(core_index, cores);
|
||||
let current_validators = groups.get(current_group_index.0 as usize).map(|v| v.as_slice()).unwrap_or_default();
|
||||
let current_validators = groups
|
||||
.get(current_group_index.0 as usize)
|
||||
.map(|v| v.as_slice())
|
||||
.unwrap_or_default();
|
||||
|
||||
let next_group_idx = (current_group_index.0 as usize + 1) % groups.len();
|
||||
let next_validators = groups.get(next_group_idx).map(|v| v.as_slice()).unwrap_or_default();
|
||||
|
||||
let validators = &info.discovery_keys;
|
||||
|
||||
let current_validators = current_validators.iter().map(|i| validators[i.0 as usize].clone()).collect();
|
||||
let next_validators = next_validators.iter().map(|i| validators[i.0 as usize].clone()).collect();
|
||||
let current_validators =
|
||||
current_validators.iter().map(|i| validators[i.0 as usize].clone()).collect();
|
||||
let next_validators =
|
||||
next_validators.iter().map(|i| validators[i.0 as usize].clone()).collect();
|
||||
|
||||
let current_validators = GroupValidators {
|
||||
group: current_group_index,
|
||||
validators: current_validators,
|
||||
};
|
||||
let next_validators = GroupValidators {
|
||||
group: GroupIndex(next_group_idx as u32),
|
||||
validators: next_validators,
|
||||
};
|
||||
let current_validators =
|
||||
GroupValidators { group: current_group_index, validators: current_validators };
|
||||
let next_validators =
|
||||
GroupValidators { group: GroupIndex(next_group_idx as u32), validators: next_validators };
|
||||
|
||||
Ok((current_validators, next_validators))
|
||||
}
|
||||
|
||||
/// Issue a `Declare` collation message to the given `peer`.
|
||||
async fn declare<Context>(
|
||||
ctx: &mut Context,
|
||||
state: &mut State,
|
||||
peer: PeerId,
|
||||
)
|
||||
async fn declare<Context>(ctx: &mut Context, state: &mut State, peer: PeerId)
|
||||
where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
@@ -519,21 +530,17 @@ where
|
||||
state.collator_pair.sign(&declare_signature_payload),
|
||||
);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
/// Issue a connection request to a set of validators and
|
||||
/// revoke the previous connection request.
|
||||
async fn connect_to_validators<Context>(
|
||||
ctx: &mut Context,
|
||||
validator_ids: Vec<AuthorityDiscoveryId>,
|
||||
)
|
||||
async fn connect_to_validators<Context>(ctx: &mut Context, validator_ids: Vec<AuthorityDiscoveryId>)
|
||||
where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
@@ -542,8 +549,11 @@ where
|
||||
// will reissue a new request on new collation
|
||||
let (failed, _) = oneshot::channel();
|
||||
ctx.send_message(NetworkBridgeMessage::ConnectToValidators {
|
||||
validator_ids, peer_set: PeerSet::Collation, failed,
|
||||
}).await;
|
||||
validator_ids,
|
||||
peer_set: PeerSet::Collation,
|
||||
failed,
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Advertise collation to the given `peer`.
|
||||
@@ -555,12 +565,12 @@ async fn advertise_collation<Context>(
|
||||
state: &mut State,
|
||||
relay_parent: Hash,
|
||||
peer: PeerId,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
let should_advertise = state.our_validators_groups
|
||||
let should_advertise = state
|
||||
.our_validators_groups
|
||||
.get(&relay_parent)
|
||||
.map(|g| g.should_advertise_to(&state.peer_ids, &peer))
|
||||
.unwrap_or(false);
|
||||
@@ -583,7 +593,7 @@ where
|
||||
"Not advertising collation as we already advertised it to this validator.",
|
||||
);
|
||||
return
|
||||
}
|
||||
},
|
||||
(Some(collation), true) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -595,16 +605,13 @@ where
|
||||
},
|
||||
}
|
||||
|
||||
let wire_message = protocol_v1::CollatorProtocolMessage::AdvertiseCollation(
|
||||
relay_parent,
|
||||
);
|
||||
let wire_message = protocol_v1::CollatorProtocolMessage::AdvertiseCollation(relay_parent);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer.clone()],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer.clone()],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
))
|
||||
.await;
|
||||
|
||||
if let Some(validators) = state.our_validators_groups.get_mut(&relay_parent) {
|
||||
validators.advertised_to_peer(&state.peer_ids, &peer);
|
||||
@@ -631,10 +638,12 @@ where
|
||||
match msg {
|
||||
CollateOn(id) => {
|
||||
state.collating_on = Some(id);
|
||||
}
|
||||
},
|
||||
DistributeCollation(receipt, pov, result_sender) => {
|
||||
let _span1 = state.span_per_relay_parent
|
||||
.get(&receipt.descriptor.relay_parent).map(|s| s.child("distributing-collation"));
|
||||
let _span1 = state
|
||||
.span_per_relay_parent
|
||||
.get(&receipt.descriptor.relay_parent)
|
||||
.map(|s| s.child("distributing-collation"));
|
||||
let _span2 = jaeger::Span::new(&pov, "distributing-collation");
|
||||
match state.collating_on {
|
||||
Some(id) if receipt.descriptor.para_id != id => {
|
||||
@@ -646,32 +655,28 @@ where
|
||||
collating_on = %id,
|
||||
"DistributeCollation for unexpected para_id",
|
||||
);
|
||||
}
|
||||
},
|
||||
Some(id) => {
|
||||
distribute_collation(ctx, runtime, state, id, receipt, pov, result_sender).await?;
|
||||
}
|
||||
distribute_collation(ctx, runtime, state, id, receipt, pov, result_sender)
|
||||
.await?;
|
||||
},
|
||||
None => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
para_id = %receipt.descriptor.para_id,
|
||||
"DistributeCollation message while not collating on any",
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
ReportCollator(_) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
"ReportCollator message is not expected on the collator side of the protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
NetworkBridgeUpdateV1(event) => {
|
||||
if let Err(e) = handle_network_msg(
|
||||
ctx,
|
||||
runtime,
|
||||
state,
|
||||
event,
|
||||
).await {
|
||||
if let Err(e) = handle_network_msg(ctx, runtime, state, event).await {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
err = ?e,
|
||||
@@ -680,11 +685,16 @@ where
|
||||
}
|
||||
},
|
||||
CollationFetchingRequest(incoming) => {
|
||||
let _span = state.span_per_relay_parent.get(&incoming.payload.relay_parent).map(|s| s.child("request-collation"));
|
||||
let _span = state
|
||||
.span_per_relay_parent
|
||||
.get(&incoming.payload.relay_parent)
|
||||
.map(|s| s.child("request-collation"));
|
||||
match state.collating_on {
|
||||
Some(our_para_id) => {
|
||||
Some(our_para_id) =>
|
||||
if our_para_id == incoming.payload.para_id {
|
||||
let (receipt, pov) = if let Some(collation) = state.collations.get_mut(&incoming.payload.relay_parent) {
|
||||
let (receipt, pov) = if let Some(collation) =
|
||||
state.collations.get_mut(&incoming.payload.relay_parent)
|
||||
{
|
||||
collation.status.advance_to_requested();
|
||||
(collation.receipt.clone(), collation.pov.clone())
|
||||
} else {
|
||||
@@ -694,23 +704,28 @@ where
|
||||
"received a `RequestCollation` for a relay parent we don't have collation stored.",
|
||||
);
|
||||
|
||||
return Ok(());
|
||||
return Ok(())
|
||||
};
|
||||
|
||||
state.metrics.on_collation_sent_requested();
|
||||
|
||||
let _span = _span.as_ref().map(|s| s.child("sending"));
|
||||
|
||||
let waiting = state.waiting_collation_fetches.entry(incoming.payload.relay_parent).or_default();
|
||||
let waiting = state
|
||||
.waiting_collation_fetches
|
||||
.entry(incoming.payload.relay_parent)
|
||||
.or_default();
|
||||
|
||||
if !waiting.waiting_peers.insert(incoming.peer) {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Dropping incoming request as peer has a request in flight already."
|
||||
);
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::ReportPeer(incoming.peer, COST_APPARENT_FLOOD)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::ReportPeer(
|
||||
incoming.peer,
|
||||
COST_APPARENT_FLOOD,
|
||||
))
|
||||
.await;
|
||||
return Ok(())
|
||||
}
|
||||
|
||||
@@ -727,17 +742,16 @@ where
|
||||
our_para_id = %our_para_id,
|
||||
"received a `CollationFetchingRequest` for unexpected para_id",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
None => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
for_para_id = %incoming.payload.para_id,
|
||||
"received a `RequestCollation` while not collating on any para",
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
@@ -763,24 +777,24 @@ async fn send_collation(
|
||||
};
|
||||
|
||||
if let Err(_) = request.send_outgoing_response(response) {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
"Sending collation response failed",
|
||||
);
|
||||
tracing::warn!(target: LOG_TARGET, "Sending collation response failed",);
|
||||
}
|
||||
|
||||
state.active_collation_fetches.push(async move {
|
||||
let r = rx.timeout(MAX_UNSHARED_UPLOAD_TIME).await;
|
||||
if r.is_none() {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?relay_parent,
|
||||
?peer_id,
|
||||
"Sending collation to validator timed out, carrying on with next validator."
|
||||
);
|
||||
state.active_collation_fetches.push(
|
||||
async move {
|
||||
let r = rx.timeout(MAX_UNSHARED_UPLOAD_TIME).await;
|
||||
if r.is_none() {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?relay_parent,
|
||||
?peer_id,
|
||||
"Sending collation to validator timed out, carrying on with next validator."
|
||||
);
|
||||
}
|
||||
(relay_parent, peer_id)
|
||||
}
|
||||
(relay_parent, peer_id)
|
||||
}.boxed());
|
||||
.boxed(),
|
||||
);
|
||||
|
||||
state.metrics.on_collation_sent();
|
||||
}
|
||||
@@ -808,10 +822,9 @@ where
|
||||
);
|
||||
|
||||
// If we are declared to, this is another collator, and we should disconnect.
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::DisconnectPeer(origin, PeerSet::Collation)
|
||||
).await;
|
||||
}
|
||||
ctx.send_message(NetworkBridgeMessage::DisconnectPeer(origin, PeerSet::Collation))
|
||||
.await;
|
||||
},
|
||||
AdvertiseCollation(_) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
@@ -819,15 +832,16 @@ where
|
||||
"AdvertiseCollation message is not expected on the collator side of the protocol",
|
||||
);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::ReportPeer(origin.clone(), COST_UNEXPECTED_MESSAGE)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::ReportPeer(
|
||||
origin.clone(),
|
||||
COST_UNEXPECTED_MESSAGE,
|
||||
))
|
||||
.await;
|
||||
|
||||
// If we are advertised to, this is another collator, and we should disconnect.
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::DisconnectPeer(origin, PeerSet::Collation)
|
||||
).await;
|
||||
}
|
||||
ctx.send_message(NetworkBridgeMessage::DisconnectPeer(origin, PeerSet::Collation))
|
||||
.await;
|
||||
},
|
||||
CollationSeconded(relay_parent, statement) => {
|
||||
if !matches!(statement.unchecked_payload(), Statement::Seconded(_)) {
|
||||
tracing::warn!(
|
||||
@@ -837,12 +851,13 @@ where
|
||||
"Collation seconded message received with none-seconded statement.",
|
||||
);
|
||||
} else {
|
||||
let statement = runtime.check_signature(ctx.sender(), relay_parent, statement)
|
||||
let statement = runtime
|
||||
.check_signature(ctx.sender(), relay_parent, statement)
|
||||
.await?
|
||||
.map_err(NonFatal::InvalidStatementSignature)?;
|
||||
|
||||
let removed = state.collation_result_senders
|
||||
.remove(&statement.payload().candidate_hash());
|
||||
let removed =
|
||||
state.collation_result_senders.remove(&statement.payload().candidate_hash());
|
||||
|
||||
if let Some(sender) = removed {
|
||||
tracing::trace!(
|
||||
@@ -854,7 +869,7 @@ where
|
||||
let _ = sender.send(statement);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -866,8 +881,7 @@ async fn handle_peer_view_change<Context>(
|
||||
state: &mut State,
|
||||
peer_id: PeerId,
|
||||
view: View,
|
||||
)
|
||||
where
|
||||
) where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
@@ -899,12 +913,7 @@ where
|
||||
PeerConnected(peer_id, observed_role, maybe_authority) => {
|
||||
// If it is possible that a disconnected validator would attempt a reconnect
|
||||
// it should be handled here.
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peer_id,
|
||||
?observed_role,
|
||||
"Peer connected",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peer_id, ?observed_role, "Peer connected",);
|
||||
if let Some(authority) = maybe_authority {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
@@ -916,49 +925,33 @@ where
|
||||
|
||||
declare(ctx, state, peer_id).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
PeerViewChange(peer_id, view) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peer_id,
|
||||
?view,
|
||||
"Peer view change",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peer_id, ?view, "Peer view change",);
|
||||
handle_peer_view_change(ctx, state, peer_id, view).await;
|
||||
}
|
||||
},
|
||||
PeerDisconnected(peer_id) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peer_id,
|
||||
"Peer disconnected",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peer_id, "Peer disconnected",);
|
||||
state.peer_views.remove(&peer_id);
|
||||
state.peer_ids.remove(&peer_id);
|
||||
}
|
||||
},
|
||||
OurViewChange(view) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?view,
|
||||
"Own view change",
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?view, "Own view change",);
|
||||
handle_our_view_change(state, view).await?;
|
||||
}
|
||||
},
|
||||
PeerMessage(remote, msg) => {
|
||||
handle_incoming_peer_message(ctx, runtime, state, remote, msg).await?;
|
||||
}
|
||||
},
|
||||
NewGossipTopology(..) => {
|
||||
// impossibru!
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handles our view changes.
|
||||
async fn handle_our_view_change(
|
||||
state: &mut State,
|
||||
view: OurView,
|
||||
) -> Result<()> {
|
||||
async fn handle_our_view_change(state: &mut State, view: OurView) -> Result<()> {
|
||||
for removed in state.view.difference(&view) {
|
||||
tracing::debug!(target: LOG_TARGET, relay_parent = ?removed, "Removing relay parent because our view changed.");
|
||||
|
||||
@@ -983,7 +976,7 @@ async fn handle_our_view_change(
|
||||
candidate_hash = ?collation.receipt.hash(),
|
||||
pov_hash = ?collation.pov.hash(),
|
||||
"Collation was requested.",
|
||||
)
|
||||
),
|
||||
}
|
||||
}
|
||||
state.our_validators_groups.remove(removed);
|
||||
@@ -1005,7 +998,7 @@ pub(crate) async fn run<Context>(
|
||||
) -> Result<()>
|
||||
where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
use OverseerSignal::*;
|
||||
|
||||
|
||||
@@ -26,21 +26,17 @@ use sp_core::{crypto::Pair, Decode};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use sp_runtime::traits::AppVerify;
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
our_view,
|
||||
view,
|
||||
request_response::request::IncomingRequest,
|
||||
};
|
||||
use polkadot_node_network_protocol::{our_view, request_response::request::IncomingRequest, view};
|
||||
use polkadot_node_primitives::BlockData;
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_primitives::v1::{
|
||||
AuthorityDiscoveryId, CandidateDescriptor, CollatorPair, GroupRotationInfo, ScheduledCore, SessionIndex,
|
||||
SessionInfo, ValidatorId, ValidatorIndex,
|
||||
AuthorityDiscoveryId, CandidateDescriptor, CollatorPair, GroupRotationInfo, ScheduledCore,
|
||||
SessionIndex, SessionInfo, ValidatorId, ValidatorIndex,
|
||||
};
|
||||
use polkadot_node_primitives::BlockData;
|
||||
use polkadot_subsystem::{
|
||||
jaeger,
|
||||
messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest},
|
||||
ActiveLeavesUpdate, ActivatedLeaf, LeafStatus,
|
||||
ActivatedLeaf, ActiveLeavesUpdate, LeafStatus,
|
||||
};
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
|
||||
@@ -103,22 +99,17 @@ impl Default for TestState {
|
||||
let validator_public = validator_pubkeys(&validators);
|
||||
let discovery_keys = validator_authority_id(&validators);
|
||||
|
||||
let validator_peer_id = std::iter::repeat_with(|| PeerId::random())
|
||||
.take(discovery_keys.len())
|
||||
.collect();
|
||||
let validator_peer_id =
|
||||
std::iter::repeat_with(|| PeerId::random()).take(discovery_keys.len()).collect();
|
||||
|
||||
let validator_groups = vec![vec![2, 0, 4], vec![3, 2, 4]]
|
||||
.into_iter().map(|g| g.into_iter().map(ValidatorIndex).collect()).collect();
|
||||
let group_rotation_info = GroupRotationInfo {
|
||||
session_start_block: 0,
|
||||
group_rotation_frequency: 100,
|
||||
now: 1,
|
||||
};
|
||||
.into_iter()
|
||||
.map(|g| g.into_iter().map(ValidatorIndex).collect())
|
||||
.collect();
|
||||
let group_rotation_info =
|
||||
GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 100, now: 1 };
|
||||
|
||||
let availability_core = CoreState::Scheduled(ScheduledCore {
|
||||
para_id,
|
||||
collator: None,
|
||||
});
|
||||
let availability_core = CoreState::Scheduled(ScheduledCore { para_id, collator: None });
|
||||
|
||||
let relay_parent = Hash::random();
|
||||
|
||||
@@ -155,7 +146,10 @@ impl TestState {
|
||||
}
|
||||
|
||||
fn current_group_validator_peer_ids(&self) -> Vec<PeerId> {
|
||||
self.current_group_validator_indices().iter().map(|i| self.validator_peer_id[i.0 as usize].clone()).collect()
|
||||
self.current_group_validator_indices()
|
||||
.iter()
|
||||
.map(|i| self.validator_peer_id[i.0 as usize].clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn current_group_validator_authority_ids(&self) -> Vec<AuthorityDiscoveryId> {
|
||||
@@ -169,7 +163,11 @@ impl TestState {
|
||||
///
|
||||
/// If `merge_views == true` it means the subsystem will be informed that we are working on the old `relay_parent`
|
||||
/// and the new one.
|
||||
async fn advance_to_new_round(&mut self, virtual_overseer: &mut VirtualOverseer, merge_views: bool) {
|
||||
async fn advance_to_new_round(
|
||||
&mut self,
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
merge_views: bool,
|
||||
) {
|
||||
let old_relay_parent = self.relay_parent;
|
||||
|
||||
while self.relay_parent == old_relay_parent {
|
||||
@@ -184,8 +182,11 @@ impl TestState {
|
||||
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(our_view)),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,14 +203,8 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
) {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.filter(
|
||||
Some("polkadot_collator_protocol"),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(
|
||||
Some(LOG_TARGET),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(Some("polkadot_collator_protocol"), log::LevelFilter::Trace)
|
||||
.filter(Some(LOG_TARGET), log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
@@ -223,18 +218,20 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
}, subsystem)).1.unwrap();
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
},
|
||||
subsystem,
|
||||
))
|
||||
.1
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
async fn overseer_send(
|
||||
overseer: &mut VirtualOverseer,
|
||||
msg: CollatorProtocolMessage,
|
||||
) {
|
||||
async fn overseer_send(overseer: &mut VirtualOverseer, msg: CollatorProtocolMessage) {
|
||||
tracing::trace!(?msg, "sending message");
|
||||
overseer
|
||||
.send(FromOverseer::Communication { msg })
|
||||
@@ -243,9 +240,7 @@ async fn overseer_send(
|
||||
.expect(&format!("{:?} is more than enough for sending messages.", TIMEOUT));
|
||||
}
|
||||
|
||||
async fn overseer_recv(
|
||||
overseer: &mut VirtualOverseer,
|
||||
) -> AllMessages {
|
||||
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
||||
let msg = overseer_recv_with_timeout(overseer, TIMEOUT)
|
||||
.await
|
||||
.expect(&format!("{:?} is more than enough to receive messages", TIMEOUT));
|
||||
@@ -260,16 +255,10 @@ async fn overseer_recv_with_timeout(
|
||||
timeout: Duration,
|
||||
) -> Option<AllMessages> {
|
||||
tracing::trace!("waiting for message...");
|
||||
overseer
|
||||
.recv()
|
||||
.timeout(timeout)
|
||||
.await
|
||||
overseer.recv().timeout(timeout).await
|
||||
}
|
||||
|
||||
async fn overseer_signal(
|
||||
overseer: &mut VirtualOverseer,
|
||||
signal: OverseerSignal,
|
||||
) {
|
||||
async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) {
|
||||
overseer
|
||||
.send(FromOverseer::Signal(signal))
|
||||
.timeout(TIMEOUT)
|
||||
@@ -279,10 +268,7 @@ async fn overseer_signal(
|
||||
|
||||
// Setup the system by sending the `CollateOn`, `ActiveLeaves` and `OurViewChange` messages.
|
||||
async fn setup_system(virtual_overseer: &mut VirtualOverseer, test_state: &TestState) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::CollateOn(test_state.para_id),
|
||||
).await;
|
||||
overseer_send(virtual_overseer, CollatorProtocolMessage::CollateOn(test_state.para_id)).await;
|
||||
|
||||
overseer_signal(
|
||||
virtual_overseer,
|
||||
@@ -292,14 +278,16 @@ async fn setup_system(virtual_overseer: &mut VirtualOverseer, test_state: &TestS
|
||||
status: LeafStatus::Fresh,
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
})),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent]),
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Result of [`distribute_collation`]
|
||||
@@ -316,9 +304,7 @@ async fn distribute_collation(
|
||||
should_connect: bool,
|
||||
) -> DistributeCollation {
|
||||
// Now we want to distribute a PoVBlock
|
||||
let pov_block = PoV {
|
||||
block_data: BlockData(vec![42, 43, 44]),
|
||||
};
|
||||
let pov_block = PoV { block_data: BlockData(vec![42, 43, 44]) };
|
||||
|
||||
let pov_hash = pov_block.hash();
|
||||
|
||||
@@ -327,12 +313,14 @@ async fn distribute_collation(
|
||||
relay_parent: test_state.relay_parent,
|
||||
pov_hash,
|
||||
..Default::default()
|
||||
}.build();
|
||||
}
|
||||
.build();
|
||||
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::DistributeCollation(candidate.clone(), pov_block.clone(), None),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
// obtain the availability cores.
|
||||
assert_matches!(
|
||||
@@ -355,7 +343,7 @@ async fn distribute_collation(
|
||||
)) => {
|
||||
assert_eq!(relay_parent, test_state.relay_parent);
|
||||
tx.send(Ok(test_state.current_session_index())).unwrap();
|
||||
}
|
||||
},
|
||||
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
relay_parent,
|
||||
@@ -365,22 +353,22 @@ async fn distribute_collation(
|
||||
assert_eq!(index, test_state.current_session_index());
|
||||
|
||||
tx.send(Ok(Some(test_state.session_info.clone()))).unwrap();
|
||||
}
|
||||
},
|
||||
|
||||
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
|
||||
relay_parent,
|
||||
RuntimeApiRequest::ValidatorGroups(tx)
|
||||
RuntimeApiRequest::ValidatorGroups(tx),
|
||||
)) => {
|
||||
assert_eq!(relay_parent, test_state.relay_parent);
|
||||
tx.send(Ok((
|
||||
test_state.session_info.validator_groups.clone(),
|
||||
test_state.group_rotation_info.clone(),
|
||||
))).unwrap();
|
||||
)))
|
||||
.unwrap();
|
||||
// This call is mandatory - we are done:
|
||||
break;
|
||||
}
|
||||
other =>
|
||||
panic!("Unexpected message received: {:?}", other),
|
||||
break
|
||||
},
|
||||
other => panic!("Unexpected message received: {:?}", other),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,35 +383,33 @@ async fn distribute_collation(
|
||||
);
|
||||
}
|
||||
|
||||
DistributeCollation {
|
||||
candidate,
|
||||
pov_block,
|
||||
}
|
||||
DistributeCollation { candidate, pov_block }
|
||||
}
|
||||
|
||||
/// Connect a peer
|
||||
async fn connect_peer(
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
peer: PeerId,
|
||||
authority_id: Option<AuthorityDiscoveryId>
|
||||
authority_id: Option<AuthorityDiscoveryId>,
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(
|
||||
peer.clone(),
|
||||
polkadot_node_network_protocol::ObservedRole::Authority,
|
||||
authority_id,
|
||||
),
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer.clone(),
|
||||
polkadot_node_network_protocol::ObservedRole::Authority,
|
||||
authority_id,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer, view![]),
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer,
|
||||
view![],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Disconnect a peer
|
||||
@@ -431,7 +417,8 @@ async fn disconnect_peer(virtual_overseer: &mut VirtualOverseer, peer: PeerId) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerDisconnected(peer)),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Check that the next received message is a `Declare` message.
|
||||
@@ -496,13 +483,19 @@ async fn expect_advertise_collation_msg(
|
||||
}
|
||||
|
||||
/// Send a message that the given peer's view changed.
|
||||
async fn send_peer_view_change(virtual_overseer: &mut VirtualOverseer, peer: &PeerId, hashes: Vec<Hash>) {
|
||||
async fn send_peer_view_change(
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
peer: &PeerId,
|
||||
hashes: Vec<Hash>,
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(peer.clone(), View::new(hashes, 0)),
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
View::new(hashes, 0),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -519,7 +512,8 @@ fn advertise_and_send_collation() {
|
||||
let DistributeCollation { candidate, pov_block } =
|
||||
distribute_collation(&mut virtual_overseer, &test_state, true).await;
|
||||
|
||||
for (val, peer) in test_state.current_group_validator_authority_ids()
|
||||
for (val, peer) in test_state
|
||||
.current_group_validator_authority_ids()
|
||||
.into_iter()
|
||||
.zip(test_state.current_group_validator_peer_ids())
|
||||
{
|
||||
@@ -546,33 +540,31 @@ fn advertise_and_send_collation() {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(
|
||||
IncomingRequest::new(
|
||||
CollatorProtocolMessage::CollationFetchingRequest(IncomingRequest::new(
|
||||
peer,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
// Second request by same validator should get dropped and peer reported:
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(IncomingRequest::new(
|
||||
peer,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
)),
|
||||
)
|
||||
).await;
|
||||
// Second request by same validator should get dropped and peer reported:
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(
|
||||
IncomingRequest::new(
|
||||
peer,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
.await;
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::ReportPeer(bad_peer, _)) => {
|
||||
@@ -609,17 +601,16 @@ fn advertise_and_send_collation() {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(
|
||||
IncomingRequest::new(
|
||||
peer,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: old_relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::CollationFetchingRequest(IncomingRequest::new(
|
||||
peer,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: old_relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
// Re-requesting collation should fail:
|
||||
rx.await.unwrap_err();
|
||||
|
||||
@@ -630,13 +621,12 @@ fn advertise_and_send_collation() {
|
||||
// Send info about peer's view.
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
view![test_state.relay_parent],
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
|
||||
peer.clone(),
|
||||
view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &peer, test_state.relay_parent).await;
|
||||
virtual_overseer
|
||||
@@ -646,29 +636,26 @@ fn advertise_and_send_collation() {
|
||||
#[test]
|
||||
fn send_only_one_collation_per_relay_parent_at_a_time() {
|
||||
test_validator_send_sequence(|mut second_response_receiver, feedback_first_tx| async move {
|
||||
Delay::new(Duration::from_millis(100)).await;
|
||||
assert!(
|
||||
second_response_receiver.try_recv().unwrap().is_none(),
|
||||
"We should not have send the collation yet to the second validator",
|
||||
);
|
||||
Delay::new(Duration::from_millis(100)).await;
|
||||
assert!(
|
||||
second_response_receiver.try_recv().unwrap().is_none(),
|
||||
"We should not have send the collation yet to the second validator",
|
||||
);
|
||||
|
||||
// Signal that the collation fetch is finished
|
||||
feedback_first_tx.send(()).expect("Sending collation fetch finished");
|
||||
second_response_receiver
|
||||
}
|
||||
);
|
||||
// Signal that the collation fetch is finished
|
||||
feedback_first_tx.send(()).expect("Sending collation fetch finished");
|
||||
second_response_receiver
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn send_next_collation_after_max_unshared_upload_time() {
|
||||
test_validator_send_sequence(|second_response_receiver, _| async move {
|
||||
Delay::new(MAX_UNSHARED_UPLOAD_TIME + Duration::from_millis(50)).await;
|
||||
second_response_receiver
|
||||
}
|
||||
);
|
||||
Delay::new(MAX_UNSHARED_UPLOAD_TIME + Duration::from_millis(50)).await;
|
||||
second_response_receiver
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn collators_declare_to_connected_peers() {
|
||||
let test_state = TestState::default();
|
||||
@@ -721,7 +708,8 @@ fn collations_are_only_advertised_to_validators_with_correct_view() {
|
||||
|
||||
distribute_collation(&mut virtual_overseer, &test_state, true).await;
|
||||
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &peer2, test_state.relay_parent).await;
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &peer2, test_state.relay_parent)
|
||||
.await;
|
||||
|
||||
// The other validator announces that it changed its view.
|
||||
send_peer_view_change(&mut virtual_overseer, &peer, vec![test_state.relay_parent]).await;
|
||||
@@ -772,7 +760,8 @@ fn collate_on_two_different_relay_chain_blocks() {
|
||||
|
||||
send_peer_view_change(&mut virtual_overseer, &peer2, vec![test_state.relay_parent]).await;
|
||||
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &peer2, test_state.relay_parent).await;
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &peer2, test_state.relay_parent)
|
||||
.await;
|
||||
virtual_overseer
|
||||
})
|
||||
}
|
||||
@@ -833,17 +822,16 @@ fn collators_reject_declare_messages() {
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
collator_pair2.public(),
|
||||
ParaId::from(5),
|
||||
collator_pair2.sign(b"garbage"),
|
||||
),
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
|
||||
peer.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
collator_pair2.public(),
|
||||
ParaId::from(5),
|
||||
collator_pair2.sign(b"garbage"),
|
||||
),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
@@ -868,7 +856,7 @@ fn collators_reject_declare_messages() {
|
||||
fn test_validator_send_sequence<T, F>(handle_first_response: T)
|
||||
where
|
||||
T: FnOnce(oneshot::Receiver<sc_network::config::OutgoingResponse>, oneshot::Sender<()>) -> F,
|
||||
F: Future<Output=oneshot::Receiver<sc_network::config::OutgoingResponse>>
|
||||
F: Future<Output = oneshot::Receiver<sc_network::config::OutgoingResponse>>,
|
||||
{
|
||||
let test_state = TestState::default();
|
||||
let local_peer_id = test_state.local_peer_id.clone();
|
||||
@@ -882,7 +870,8 @@ where
|
||||
let DistributeCollation { candidate, pov_block } =
|
||||
distribute_collation(&mut virtual_overseer, &test_state, true).await;
|
||||
|
||||
for (val, peer) in test_state.current_group_validator_authority_ids()
|
||||
for (val, peer) in test_state
|
||||
.current_group_validator_authority_ids()
|
||||
.into_iter()
|
||||
.zip(test_state.current_group_validator_peer_ids())
|
||||
{
|
||||
@@ -900,29 +889,40 @@ where
|
||||
let validator_1 = test_state.current_group_validator_peer_ids()[1].clone();
|
||||
|
||||
// Send info about peer's view.
|
||||
send_peer_view_change(&mut virtual_overseer, &validator_0, vec![test_state.relay_parent]).await;
|
||||
send_peer_view_change(&mut virtual_overseer, &validator_1, vec![test_state.relay_parent]).await;
|
||||
send_peer_view_change(&mut virtual_overseer, &validator_0, vec![test_state.relay_parent])
|
||||
.await;
|
||||
send_peer_view_change(&mut virtual_overseer, &validator_1, vec![test_state.relay_parent])
|
||||
.await;
|
||||
|
||||
// The peer is interested in a leaf that we have a collation for;
|
||||
// advertise it.
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &validator_0, test_state.relay_parent).await;
|
||||
expect_advertise_collation_msg(&mut virtual_overseer, &validator_1, test_state.relay_parent).await;
|
||||
expect_advertise_collation_msg(
|
||||
&mut virtual_overseer,
|
||||
&validator_0,
|
||||
test_state.relay_parent,
|
||||
)
|
||||
.await;
|
||||
expect_advertise_collation_msg(
|
||||
&mut virtual_overseer,
|
||||
&validator_1,
|
||||
test_state.relay_parent,
|
||||
)
|
||||
.await;
|
||||
|
||||
// Request a collation.
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(
|
||||
IncomingRequest::new(
|
||||
validator_0,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::CollationFetchingRequest(IncomingRequest::new(
|
||||
validator_0,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
// Keep the feedback channel alive because we need to use it to inform about the finished transfer.
|
||||
let feedback_tx = assert_matches!(
|
||||
@@ -945,17 +945,16 @@ where
|
||||
let (tx, rx) = oneshot::channel();
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::CollationFetchingRequest(
|
||||
IncomingRequest::new(
|
||||
validator_1,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::CollationFetchingRequest(IncomingRequest::new(
|
||||
validator_1,
|
||||
CollationFetchingRequest {
|
||||
relay_parent: test_state.relay_parent,
|
||||
para_id: test_state.para_id,
|
||||
},
|
||||
tx,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
let rx = handle_first_response(rx, feedback_tx).await;
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ use polkadot_node_primitives::UncheckedSignedFullStatement;
|
||||
use polkadot_subsystem::errors::SubsystemError;
|
||||
use thiserror::Error;
|
||||
|
||||
use polkadot_node_subsystem_util::{Fault, runtime, unwrap_non_fatal};
|
||||
use polkadot_node_subsystem_util::{runtime, unwrap_non_fatal, Fault};
|
||||
|
||||
use crate::LOG_TARGET;
|
||||
|
||||
@@ -82,9 +82,7 @@ pub enum 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<()>, ctx: &'static str)
|
||||
-> FatalResult<()>
|
||||
{
|
||||
pub fn log_error(result: Result<()>, ctx: &'static str) -> FatalResult<()> {
|
||||
if let Some(error) = unwrap_non_fatal(result.map_err(|e| e.0))? {
|
||||
tracing::warn!(target: LOG_TARGET, error = ?error, ctx)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//! This subsystem implements both sides of the collator protocol.
|
||||
|
||||
#![deny(missing_docs, unused_crate_dependencies)]
|
||||
#![recursion_limit="256"]
|
||||
#![recursion_limit = "256"]
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
@@ -30,14 +30,9 @@ use polkadot_node_network_protocol::{PeerId, UnifiedReputationChange as Rep};
|
||||
use polkadot_primitives::v1::CollatorPair;
|
||||
|
||||
use polkadot_subsystem::{
|
||||
SpawnedSubsystem,
|
||||
SubsystemContext,
|
||||
SubsystemSender,
|
||||
overseer,
|
||||
messages::{
|
||||
CollatorProtocolMessage, NetworkBridgeMessage,
|
||||
},
|
||||
errors::SubsystemError,
|
||||
messages::{CollatorProtocolMessage, NetworkBridgeMessage},
|
||||
overseer, SpawnedSubsystem, SubsystemContext, SubsystemSender,
|
||||
};
|
||||
|
||||
mod error;
|
||||
@@ -92,29 +87,19 @@ impl CollatorProtocolSubsystem {
|
||||
/// If `id` is `None` this is a validator side of the protocol.
|
||||
/// Caller must provide a registry for prometheus metrics.
|
||||
pub fn new(protocol_side: ProtocolSide) -> Self {
|
||||
Self {
|
||||
protocol_side,
|
||||
}
|
||||
Self { protocol_side }
|
||||
}
|
||||
|
||||
async fn run<Context>(self, ctx: Context) -> Result<()>
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
match self.protocol_side {
|
||||
ProtocolSide::Validator { keystore, eviction_policy, metrics } => validator_side::run(
|
||||
ctx,
|
||||
keystore,
|
||||
eviction_policy,
|
||||
metrics,
|
||||
).await,
|
||||
ProtocolSide::Collator(local_peer_id, collator_pair, metrics) => collator_side::run(
|
||||
ctx,
|
||||
local_peer_id,
|
||||
collator_pair,
|
||||
metrics,
|
||||
).await,
|
||||
ProtocolSide::Validator { keystore, eviction_policy, metrics } =>
|
||||
validator_side::run(ctx, keystore, eviction_policy, metrics).await,
|
||||
ProtocolSide::Collator(local_peer_id, collator_pair, metrics) =>
|
||||
collator_side::run(ctx, local_peer_id, collator_pair, metrics).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,10 +116,7 @@ where
|
||||
.map_err(|e| SubsystemError::with_origin("collator-protocol", e))
|
||||
.boxed();
|
||||
|
||||
SpawnedSubsystem {
|
||||
name: "collator-protocol-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "collator-protocol-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +132,5 @@ where
|
||||
"reputation change for peer",
|
||||
);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::ReportPeer(peer, rep),
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::ReportPeer(peer, rep)).await;
|
||||
}
|
||||
|
||||
@@ -14,39 +14,44 @@
|
||||
// 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}, sync::Arc, task::Poll};
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::time::{Duration, Instant};
|
||||
use always_assert::never;
|
||||
use futures::{
|
||||
channel::oneshot, future::{BoxFuture, Fuse, FusedFuture}, FutureExt, StreamExt,
|
||||
stream::FuturesUnordered, select,
|
||||
channel::oneshot,
|
||||
future::{BoxFuture, Fuse, FusedFuture},
|
||||
select,
|
||||
stream::FuturesUnordered,
|
||||
FutureExt, StreamExt,
|
||||
};
|
||||
use futures_timer::Delay;
|
||||
use std::{
|
||||
collections::{hash_map::Entry, HashMap, HashSet},
|
||||
sync::Arc,
|
||||
task::Poll,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use sp_keystore::SyncCryptoStorePtr;
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
request_response as req_res, v1 as protocol_v1,
|
||||
peer_set::PeerSet,
|
||||
request_response as req_res,
|
||||
request_response::{
|
||||
request::{Recipient, RequestError},
|
||||
v1::{CollationFetchingRequest, CollationFetchingResponse},
|
||||
OutgoingRequest, Requests,
|
||||
},
|
||||
OurView, PeerId, UnifiedReputationChange as Rep, View,
|
||||
v1 as protocol_v1, OurView, PeerId, UnifiedReputationChange as Rep, View,
|
||||
};
|
||||
use polkadot_node_primitives::{SignedFullStatement, PoV};
|
||||
use polkadot_node_primitives::{PoV, SignedFullStatement};
|
||||
use polkadot_node_subsystem_util::metrics::{self, prometheus};
|
||||
use polkadot_primitives::v1::{CandidateReceipt, CollatorId, Hash, Id as ParaId};
|
||||
use polkadot_subsystem::{
|
||||
overseer,
|
||||
jaeger,
|
||||
messages::{
|
||||
CollatorProtocolMessage, IfDisconnected,
|
||||
NetworkBridgeEvent, NetworkBridgeMessage, CandidateBackingMessage,
|
||||
CandidateBackingMessage, CollatorProtocolMessage, IfDisconnected, NetworkBridgeEvent,
|
||||
NetworkBridgeMessage,
|
||||
},
|
||||
FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, SubsystemSender,
|
||||
overseer, FromOverseer, OverseerSignal, PerLeafSpan, SubsystemContext, SubsystemSender,
|
||||
};
|
||||
|
||||
use super::{modify_reputation, Result, LOG_TARGET};
|
||||
@@ -64,7 +69,8 @@ const COST_INVALID_SIGNATURE: Rep = Rep::Malicious("Invalid network message sign
|
||||
const COST_REPORT_BAD: Rep = Rep::Malicious("A collator was reported by another subsystem");
|
||||
const COST_WRONG_PARA: Rep = Rep::Malicious("A collator provided a collation for the wrong para");
|
||||
const COST_UNNEEDED_COLLATOR: Rep = Rep::CostMinor("An unneeded collator connected");
|
||||
const BENEFIT_NOTIFY_GOOD: Rep = Rep::BenefitMinor("A collator was noted good by another subsystem");
|
||||
const BENEFIT_NOTIFY_GOOD: Rep =
|
||||
Rep::BenefitMinor("A collator was noted good by another subsystem");
|
||||
|
||||
/// Time after starting a collation download from a collator we will start another one from the
|
||||
/// next collator even if the upload was not finished yet.
|
||||
@@ -105,13 +111,19 @@ impl Metrics {
|
||||
}
|
||||
|
||||
/// Provide a timer for `handle_collation_request_result` which observes on drop.
|
||||
fn time_handle_collation_request_result(&self) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
|
||||
self.0.as_ref().map(|metrics| metrics.handle_collation_request_result.start_timer())
|
||||
fn time_handle_collation_request_result(
|
||||
&self,
|
||||
) -> Option<metrics::prometheus::prometheus::HistogramTimer> {
|
||||
self.0
|
||||
.as_ref()
|
||||
.map(|metrics| metrics.handle_collation_request_result.start_timer())
|
||||
}
|
||||
|
||||
/// Note the current number of collator peers.
|
||||
fn note_collator_peer_count(&self, collator_peers: usize) {
|
||||
self.0.as_ref().map(|metrics| metrics.collator_peer_count.set(collator_peers as u64));
|
||||
self.0
|
||||
.as_ref()
|
||||
.map(|metrics| metrics.collator_peer_count.set(collator_peers as u64));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,9 +136,9 @@ struct MetricsInner {
|
||||
}
|
||||
|
||||
impl metrics::Metrics for Metrics {
|
||||
fn try_register(registry: &prometheus::Registry)
|
||||
-> std::result::Result<Self, prometheus::PrometheusError>
|
||||
{
|
||||
fn try_register(
|
||||
registry: &prometheus::Registry,
|
||||
) -> std::result::Result<Self, prometheus::PrometheusError> {
|
||||
let metrics = MetricsInner {
|
||||
collation_requests: prometheus::register(
|
||||
prometheus::CounterVec::new(
|
||||
@@ -210,10 +222,7 @@ struct PeerData {
|
||||
|
||||
impl PeerData {
|
||||
fn new(view: View) -> Self {
|
||||
PeerData {
|
||||
view,
|
||||
state: PeerState::Connected(Instant::now()),
|
||||
}
|
||||
PeerData { view, state: PeerState::Connected(Instant::now()) }
|
||||
}
|
||||
|
||||
/// Update the view, clearing all advertisements that are no longer in the
|
||||
@@ -241,20 +250,17 @@ impl PeerData {
|
||||
&mut self,
|
||||
on_relay_parent: Hash,
|
||||
our_view: &View,
|
||||
)
|
||||
-> std::result::Result<(CollatorId, ParaId), AdvertisementError>
|
||||
{
|
||||
) -> std::result::Result<(CollatorId, ParaId), AdvertisementError> {
|
||||
match self.state {
|
||||
PeerState::Connected(_) => Err(AdvertisementError::UndeclaredCollator),
|
||||
_ if !our_view.contains(&on_relay_parent) => Err(AdvertisementError::OutOfOurView),
|
||||
PeerState::Collating(ref mut state) => {
|
||||
PeerState::Collating(ref mut state) =>
|
||||
if state.advertisements.insert(on_relay_parent) {
|
||||
state.last_active = Instant::now();
|
||||
Ok((state.collator_id.clone(), state.para_id.clone()))
|
||||
} else {
|
||||
Err(AdvertisementError::Duplicate)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,7 +311,8 @@ impl PeerData {
|
||||
fn is_inactive(&self, policy: &crate::CollatorEvictionPolicy) -> bool {
|
||||
match self.state {
|
||||
PeerState::Connected(connected_at) => connected_at.elapsed() >= policy.undeclared,
|
||||
PeerState::Collating(ref state) => state.last_active.elapsed() >= policy.inactive_collator,
|
||||
PeerState::Collating(ref state) =>
|
||||
state.last_active.elapsed() >= policy.inactive_collator,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -325,7 +332,7 @@ struct GroupAssignments {
|
||||
struct ActiveParas {
|
||||
relay_parent_assignments: HashMap<Hash, GroupAssignments>,
|
||||
current_assignments: HashMap<ParaId, usize>,
|
||||
next_assignments: HashMap<ParaId, usize>
|
||||
next_assignments: HashMap<ParaId, usize>,
|
||||
}
|
||||
|
||||
impl ActiveParas {
|
||||
@@ -350,7 +357,6 @@ impl ActiveParas {
|
||||
.map(|x| x.ok())
|
||||
.flatten();
|
||||
|
||||
|
||||
let mc = polkadot_node_subsystem_util::request_availability_cores(relay_parent, sender)
|
||||
.await
|
||||
.await
|
||||
@@ -368,38 +374,32 @@ impl ActiveParas {
|
||||
);
|
||||
|
||||
continue
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let (para_now, para_next) = match polkadot_node_subsystem_util
|
||||
::signing_key_and_index(&validators, keystore)
|
||||
.await
|
||||
.and_then(|(_, index)| polkadot_node_subsystem_util::find_validator_group(
|
||||
&groups,
|
||||
index,
|
||||
))
|
||||
{
|
||||
Some(group) => {
|
||||
let next_rotation_info = rotation_info.bump_rotation();
|
||||
let (para_now, para_next) =
|
||||
match polkadot_node_subsystem_util::signing_key_and_index(&validators, keystore)
|
||||
.await
|
||||
.and_then(|(_, index)| {
|
||||
polkadot_node_subsystem_util::find_validator_group(&groups, index)
|
||||
}) {
|
||||
Some(group) => {
|
||||
let next_rotation_info = rotation_info.bump_rotation();
|
||||
|
||||
let core_now = rotation_info.core_for_group(group, cores.len());
|
||||
let core_next = next_rotation_info.core_for_group(group, cores.len());
|
||||
let core_now = rotation_info.core_for_group(group, cores.len());
|
||||
let core_next = next_rotation_info.core_for_group(group, cores.len());
|
||||
|
||||
(
|
||||
cores.get(core_now.0 as usize).and_then(|c| c.para_id()),
|
||||
cores.get(core_next.0 as usize).and_then(|c| c.para_id()),
|
||||
)
|
||||
}
|
||||
None => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?relay_parent,
|
||||
"Not a validator",
|
||||
);
|
||||
(
|
||||
cores.get(core_now.0 as usize).and_then(|c| c.para_id()),
|
||||
cores.get(core_next.0 as usize).and_then(|c| c.para_id()),
|
||||
)
|
||||
},
|
||||
None => {
|
||||
tracing::trace!(target: LOG_TARGET, ?relay_parent, "Not a validator",);
|
||||
|
||||
continue
|
||||
}
|
||||
};
|
||||
continue
|
||||
},
|
||||
};
|
||||
|
||||
// This code won't work well, if at all for parathreads. For parathreads we'll
|
||||
// have to be aware of which core the parathread claim is going to be multiplexed
|
||||
@@ -426,17 +426,12 @@ impl ActiveParas {
|
||||
*self.next_assignments.entry(para_next).or_default() += 1;
|
||||
}
|
||||
|
||||
self.relay_parent_assignments.insert(
|
||||
relay_parent,
|
||||
GroupAssignments { current: para_now, next: para_next },
|
||||
);
|
||||
self.relay_parent_assignments
|
||||
.insert(relay_parent, GroupAssignments { current: para_now, next: para_next });
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_outgoing(
|
||||
&mut self,
|
||||
old_relay_parents: impl IntoIterator<Item = Hash>,
|
||||
) {
|
||||
fn remove_outgoing(&mut self, old_relay_parents: impl IntoIterator<Item = Hash>) {
|
||||
for old_relay_parent in old_relay_parents {
|
||||
if let Some(assignments) = self.relay_parent_assignments.remove(&old_relay_parent) {
|
||||
let GroupAssignments { current, next } = assignments;
|
||||
@@ -482,16 +477,19 @@ struct PendingCollation {
|
||||
|
||||
impl PendingCollation {
|
||||
fn new(relay_parent: Hash, para_id: &ParaId, peer_id: &PeerId) -> Self {
|
||||
Self { relay_parent, para_id: para_id.clone(), peer_id: peer_id.clone(), commitments_hash: None }
|
||||
Self {
|
||||
relay_parent,
|
||||
para_id: para_id.clone(),
|
||||
peer_id: peer_id.clone(),
|
||||
commitments_hash: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type CollationEvent = (CollatorId, PendingCollation);
|
||||
|
||||
type PendingCollationFetch = (
|
||||
CollationEvent,
|
||||
std::result::Result<(CandidateReceipt, PoV), oneshot::Canceled>,
|
||||
);
|
||||
type PendingCollationFetch =
|
||||
(CollationEvent, std::result::Result<(CandidateReceipt, PoV), oneshot::Canceled>);
|
||||
|
||||
/// The status of the collations in [`CollationsPerRelayParent`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
@@ -567,7 +565,7 @@ impl CollationsPerRelayParent {
|
||||
let next = self.unfetched_collations.pop();
|
||||
self.waiting_collation = next.as_ref().map(|(_, collator_id)| collator_id.clone());
|
||||
next
|
||||
}
|
||||
},
|
||||
CollationStatus::WaitingOnValidation | CollationStatus::Fetching =>
|
||||
unreachable!("We have reset the status above!"),
|
||||
}
|
||||
@@ -622,20 +620,18 @@ fn collator_peer_id(
|
||||
peer_data: &HashMap<PeerId, PeerData>,
|
||||
collator_id: &CollatorId,
|
||||
) -> Option<PeerId> {
|
||||
peer_data.iter()
|
||||
.find_map(|(peer, data)|
|
||||
data.collator_id().filter(|c| c == &collator_id).map(|_| peer.clone())
|
||||
)
|
||||
peer_data.iter().find_map(|(peer, data)| {
|
||||
data.collator_id().filter(|c| c == &collator_id).map(|_| peer.clone())
|
||||
})
|
||||
}
|
||||
|
||||
async fn disconnect_peer<Context>(ctx: &mut Context, peer_id: PeerId)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::DisconnectPeer(peer_id, PeerSet::Collation)
|
||||
).await
|
||||
ctx.send_message(NetworkBridgeMessage::DisconnectPeer(peer_id, PeerSet::Collation))
|
||||
.await
|
||||
}
|
||||
|
||||
/// Another subsystem has requested to fetch collations on a particular leaf for some para.
|
||||
@@ -644,10 +640,9 @@ async fn fetch_collation<Context>(
|
||||
state: &mut State,
|
||||
pc: PendingCollation,
|
||||
id: CollatorId,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
@@ -657,7 +652,9 @@ where
|
||||
Delay::new(MAX_UNSHARED_DOWNLOAD_TIME).await;
|
||||
(collator_id, relay_parent)
|
||||
};
|
||||
state.collation_fetch_timeouts.push(timeout(id.clone(), relay_parent.clone()).boxed());
|
||||
state
|
||||
.collation_fetch_timeouts
|
||||
.push(timeout(id.clone(), relay_parent.clone()).boxed());
|
||||
|
||||
if state.peer_data.get(&peer_id).map_or(false, |d| d.has_advertised(&relay_parent)) {
|
||||
request_collation(ctx, state, relay_parent, para_id, peer_id, tx).await;
|
||||
@@ -671,9 +668,8 @@ async fn report_collator<Context>(
|
||||
ctx: &mut Context,
|
||||
peer_data: &HashMap<PeerId, PeerData>,
|
||||
id: CollatorId,
|
||||
)
|
||||
where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>
|
||||
) where
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
if let Some(peer_id) = collator_peer_id(peer_data, &id) {
|
||||
modify_reputation(ctx, peer_id, COST_REPORT_BAD).await;
|
||||
@@ -685,10 +681,9 @@ async fn note_good_collation<Context>(
|
||||
ctx: &mut Context,
|
||||
peer_data: &HashMap<PeerId, PeerData>,
|
||||
id: CollatorId,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
if let Some(peer_id) = collator_peer_id(peer_data, &id) {
|
||||
modify_reputation(ctx, peer_id, BENEFIT_NOTIFY_GOOD).await;
|
||||
@@ -701,18 +696,17 @@ async fn notify_collation_seconded<Context>(
|
||||
peer_id: PeerId,
|
||||
relay_parent: Hash,
|
||||
statement: SignedFullStatement,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
let wire_message = protocol_v1::CollatorProtocolMessage::CollationSeconded(relay_parent, statement.into());
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer_id],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
)
|
||||
).await;
|
||||
let wire_message =
|
||||
protocol_v1::CollatorProtocolMessage::CollationSeconded(relay_parent, statement.into());
|
||||
ctx.send_message(NetworkBridgeMessage::SendCollationMessage(
|
||||
vec![peer_id],
|
||||
protocol_v1::CollationProtocol::CollatorProtocol(wire_message),
|
||||
))
|
||||
.await;
|
||||
|
||||
modify_reputation(ctx, peer_id, BENEFIT_NOTIFY_GOOD).await;
|
||||
}
|
||||
@@ -720,15 +714,12 @@ where
|
||||
/// A peer's view has changed. A number of things should be done:
|
||||
/// - Ongoing collation requests have to be canceled.
|
||||
/// - Advertisements by this peer that are no longer relevant have to be removed.
|
||||
async fn handle_peer_view_change(
|
||||
state: &mut State,
|
||||
peer_id: PeerId,
|
||||
view: View,
|
||||
) -> Result<()> {
|
||||
async fn handle_peer_view_change(state: &mut State, peer_id: PeerId, view: View) -> Result<()> {
|
||||
let peer_data = state.peer_data.entry(peer_id.clone()).or_default();
|
||||
|
||||
peer_data.update_view(view);
|
||||
state.requested_collations
|
||||
state
|
||||
.requested_collations
|
||||
.retain(|pc, _| pc.peer_id != peer_id || !peer_data.has_advertised(&pc.relay_parent));
|
||||
|
||||
Ok(())
|
||||
@@ -747,10 +738,9 @@ async fn request_collation<Context>(
|
||||
para_id: ParaId,
|
||||
peer_id: PeerId,
|
||||
result: oneshot::Sender<(CandidateReceipt, PoV)>,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
if !state.view.contains(&relay_parent) {
|
||||
tracing::debug!(
|
||||
@@ -760,7 +750,7 @@ where
|
||||
relay_parent = %relay_parent,
|
||||
"collation is no longer in view",
|
||||
);
|
||||
return;
|
||||
return
|
||||
}
|
||||
let pending_collation = PendingCollation::new(relay_parent, ¶_id, &peer_id);
|
||||
if state.requested_collations.contains_key(&pending_collation) {
|
||||
@@ -771,29 +761,27 @@ where
|
||||
?pending_collation.relay_parent,
|
||||
"collation has already been requested",
|
||||
);
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
let (full_request, response_recv) =
|
||||
OutgoingRequest::new(Recipient::Peer(peer_id), CollationFetchingRequest {
|
||||
relay_parent,
|
||||
para_id,
|
||||
});
|
||||
let (full_request, response_recv) = OutgoingRequest::new(
|
||||
Recipient::Peer(peer_id),
|
||||
CollationFetchingRequest { relay_parent, para_id },
|
||||
);
|
||||
let requests = Requests::CollationFetching(full_request);
|
||||
|
||||
let per_request = PerRequest {
|
||||
from_collator: response_recv.boxed().fuse(),
|
||||
to_requester: result,
|
||||
span: state.span_per_relay_parent.get(&relay_parent).map(|s| {
|
||||
s.child("collation-request")
|
||||
.with_para_id(para_id)
|
||||
}),
|
||||
span: state
|
||||
.span_per_relay_parent
|
||||
.get(&relay_parent)
|
||||
.map(|s| s.child("collation-request").with_para_id(para_id)),
|
||||
};
|
||||
|
||||
state.requested_collations.insert(
|
||||
PendingCollation::new(relay_parent, ¶_id, &peer_id),
|
||||
per_request
|
||||
);
|
||||
state
|
||||
.requested_collations
|
||||
.insert(PendingCollation::new(relay_parent, ¶_id, &peer_id), per_request);
|
||||
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -803,9 +791,11 @@ where
|
||||
"Requesting collation",
|
||||
);
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::SendRequests(vec![requests], IfDisconnected::ImmediateError)
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::SendRequests(
|
||||
vec![requests],
|
||||
IfDisconnected::ImmediateError,
|
||||
))
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Networking message has been received.
|
||||
@@ -814,10 +804,9 @@ async fn process_incoming_peer_message<Context>(
|
||||
state: &mut State,
|
||||
origin: PeerId,
|
||||
msg: protocol_v1::CollatorProtocolMessage,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
use protocol_v1::CollatorProtocolMessage::*;
|
||||
use sp_runtime::traits::AppVerify;
|
||||
@@ -833,7 +822,7 @@ where
|
||||
None => {
|
||||
modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await;
|
||||
return
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
if peer_data.is_collating() {
|
||||
@@ -869,9 +858,12 @@ where
|
||||
tracing::trace!(target: LOG_TARGET, "Disconnecting unneeded collator");
|
||||
disconnect_peer(ctx, origin).await;
|
||||
}
|
||||
}
|
||||
},
|
||||
AdvertiseCollation(relay_parent) => {
|
||||
let _span = state.span_per_relay_parent.get(&relay_parent).map(|s| s.child("advertise-collation"));
|
||||
let _span = state
|
||||
.span_per_relay_parent
|
||||
.get(&relay_parent)
|
||||
.map(|s| s.child("advertise-collation"));
|
||||
if !state.view.contains(&relay_parent) {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -881,14 +873,14 @@ where
|
||||
);
|
||||
|
||||
modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await;
|
||||
return;
|
||||
return
|
||||
}
|
||||
|
||||
let peer_data = match state.peer_data.get_mut(&origin) {
|
||||
None => {
|
||||
modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await;
|
||||
return;
|
||||
}
|
||||
return
|
||||
},
|
||||
Some(p) => p,
|
||||
};
|
||||
|
||||
@@ -902,13 +894,10 @@ where
|
||||
"Received advertise collation",
|
||||
);
|
||||
|
||||
let pending_collation = PendingCollation::new(
|
||||
relay_parent,
|
||||
¶_id,
|
||||
&origin,
|
||||
);
|
||||
let pending_collation = PendingCollation::new(relay_parent, ¶_id, &origin);
|
||||
|
||||
let collations = state.collations_per_relay_parent.entry(relay_parent).or_default();
|
||||
let collations =
|
||||
state.collations_per_relay_parent.entry(relay_parent).or_default();
|
||||
|
||||
match collations.status {
|
||||
CollationStatus::Fetching | CollationStatus::WaitingOnValidation =>
|
||||
@@ -921,7 +910,7 @@ where
|
||||
},
|
||||
CollationStatus::Seconded => {},
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(error) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -932,33 +921,26 @@ where
|
||||
);
|
||||
|
||||
modify_reputation(ctx, origin, COST_UNEXPECTED_MESSAGE).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
CollationSeconded(_, _) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
peer_id = ?origin,
|
||||
"Unexpected `CollationSeconded` message, decreasing reputation",
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// A leaf has become inactive so we want to
|
||||
/// - Cancel all ongoing collation requests that are on top of that leaf.
|
||||
/// - Remove all stored collations relevant to that leaf.
|
||||
async fn remove_relay_parent(
|
||||
state: &mut State,
|
||||
relay_parent: Hash,
|
||||
) -> Result<()> {
|
||||
state.requested_collations.retain(|k, _| {
|
||||
k.relay_parent != relay_parent
|
||||
});
|
||||
async fn remove_relay_parent(state: &mut State, relay_parent: Hash) -> Result<()> {
|
||||
state.requested_collations.retain(|k, _| k.relay_parent != relay_parent);
|
||||
|
||||
state.pending_candidates.retain(|k, _| {
|
||||
k != &relay_parent
|
||||
});
|
||||
state.pending_candidates.retain(|k, _| k != &relay_parent);
|
||||
|
||||
state.collations_per_relay_parent.remove(&relay_parent);
|
||||
Ok(())
|
||||
@@ -972,12 +954,13 @@ async fn handle_our_view_change<Context>(
|
||||
view: OurView,
|
||||
) -> Result<()>
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
let old_view = std::mem::replace(&mut state.view, view);
|
||||
|
||||
let added: HashMap<Hash, Arc<jaeger::Span>> = state.view
|
||||
let added: HashMap<Hash, Arc<jaeger::Span>> = state
|
||||
.view
|
||||
.span_per_head()
|
||||
.iter()
|
||||
.filter(|v| !old_view.contains(&v.0))
|
||||
@@ -989,10 +972,7 @@ where
|
||||
});
|
||||
|
||||
let added = state.view.difference(&old_view).cloned().collect::<Vec<_>>();
|
||||
let removed = old_view
|
||||
.difference(&state.view)
|
||||
.cloned()
|
||||
.collect::<Vec<_>>();
|
||||
let removed = old_view.difference(&state.view).cloned().collect::<Vec<_>>();
|
||||
|
||||
for removed in removed.iter().cloned() {
|
||||
remove_relay_parent(state, removed).await?;
|
||||
@@ -1028,8 +1008,8 @@ async fn handle_network_msg<Context>(
|
||||
bridge_message: NetworkBridgeEvent<protocol_v1::CollatorProtocolMessage>,
|
||||
) -> Result<()>
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
use NetworkBridgeEvent::*;
|
||||
|
||||
@@ -1044,7 +1024,7 @@ where
|
||||
},
|
||||
NewGossipTopology(..) => {
|
||||
// impossibru!
|
||||
}
|
||||
},
|
||||
PeerViewChange(peer_id, view) => {
|
||||
handle_peer_view_change(state, peer_id, view).await?;
|
||||
},
|
||||
@@ -1053,7 +1033,7 @@ where
|
||||
},
|
||||
PeerMessage(remote, msg) => {
|
||||
process_incoming_peer_message(ctx, state, remote, msg).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -1065,10 +1045,9 @@ async fn process_msg<Context>(
|
||||
keystore: &SyncCryptoStorePtr,
|
||||
msg: CollatorProtocolMessage,
|
||||
state: &mut State,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
use CollatorProtocolMessage::*;
|
||||
|
||||
@@ -1081,36 +1060,31 @@ where
|
||||
para_id = %id,
|
||||
"CollateOn message is not expected on the validator side of the protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
DistributeCollation(_, _, _) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
"DistributeCollation message is not expected on the validator side of the protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
ReportCollator(id) => {
|
||||
report_collator(ctx, &state.peer_data, id).await;
|
||||
}
|
||||
},
|
||||
NetworkBridgeUpdateV1(event) => {
|
||||
if let Err(e) = handle_network_msg(
|
||||
ctx,
|
||||
state,
|
||||
keystore,
|
||||
event,
|
||||
).await {
|
||||
if let Err(e) = handle_network_msg(ctx, state, keystore, event).await {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
err = ?e,
|
||||
"Failed to handle incoming network message",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
CollationFetchingRequest(_) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
"CollationFetchingRequest message is not expected on the validator side of the protocol",
|
||||
);
|
||||
}
|
||||
},
|
||||
Seconded(parent, stmt) => {
|
||||
if let Some(collation_event) = state.pending_candidates.remove(&parent) {
|
||||
let (collator_id, pending_collation) = collation_event;
|
||||
@@ -1128,11 +1102,13 @@ where
|
||||
"Collation has been seconded, but the relay parent is deactivated",
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
Invalid(parent, candidate_receipt) => {
|
||||
let id = match state.pending_candidates.entry(parent) {
|
||||
Entry::Occupied(entry)
|
||||
if entry.get().1.commitments_hash == Some(candidate_receipt.commitments_hash) => entry.remove().0,
|
||||
if entry.get().1.commitments_hash ==
|
||||
Some(candidate_receipt.commitments_hash) =>
|
||||
entry.remove().0,
|
||||
Entry::Occupied(_) => {
|
||||
tracing::error!(
|
||||
target: LOG_TARGET,
|
||||
@@ -1141,14 +1117,14 @@ where
|
||||
"Reported invalid candidate for unknown `pending_candidate`!",
|
||||
);
|
||||
return
|
||||
}
|
||||
},
|
||||
Entry::Vacant(_) => return,
|
||||
};
|
||||
|
||||
report_collator(ctx, &state.peer_data, id.clone()).await;
|
||||
|
||||
dequeue_next_collation_and_fetch(ctx, state, parent, id).await;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1172,20 +1148,18 @@ pub(crate) async fn run<Context>(
|
||||
metrics: Metrics,
|
||||
) -> Result<()>
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
use OverseerSignal::*;
|
||||
|
||||
let mut state = State {
|
||||
metrics,
|
||||
..Default::default()
|
||||
};
|
||||
let mut state = State { metrics, ..Default::default() };
|
||||
|
||||
let next_inactivity_stream = futures::stream::unfold(
|
||||
Instant::now() + ACTIVITY_POLL,
|
||||
|next_check| async move { Some(((), wait_until_next_check(next_check).await)) }
|
||||
).fuse();
|
||||
let next_inactivity_stream =
|
||||
futures::stream::unfold(Instant::now() + ACTIVITY_POLL, |next_check| async move {
|
||||
Some(((), wait_until_next_check(next_check).await))
|
||||
})
|
||||
.fuse();
|
||||
|
||||
futures::pin_mut!(next_inactivity_stream);
|
||||
|
||||
@@ -1227,8 +1201,13 @@ where
|
||||
for (pending_collation, per_req) in state.requested_collations.iter_mut() {
|
||||
// Despite the await, this won't block on the response itself.
|
||||
let finished = poll_collation_response(
|
||||
&mut ctx, &state.metrics, &state.span_per_relay_parent, pending_collation, per_req,
|
||||
).await;
|
||||
&mut ctx,
|
||||
&state.metrics,
|
||||
&state.span_per_relay_parent,
|
||||
pending_collation,
|
||||
per_req,
|
||||
)
|
||||
.await;
|
||||
if !finished {
|
||||
retained_requested.insert(pending_collation.clone());
|
||||
}
|
||||
@@ -1240,13 +1219,15 @@ where
|
||||
|
||||
/// Dequeue another collation and fetch.
|
||||
async fn dequeue_next_collation_and_fetch(
|
||||
ctx: &mut (impl SubsystemContext<Message = CollatorProtocolMessage> + overseer::SubsystemContext<Message = CollatorProtocolMessage>),
|
||||
ctx: &mut (impl SubsystemContext<Message = CollatorProtocolMessage>
|
||||
+ overseer::SubsystemContext<Message = CollatorProtocolMessage>),
|
||||
state: &mut State,
|
||||
relay_parent: Hash,
|
||||
// The collator we tried to fetch from last.
|
||||
previous_fetch: CollatorId,
|
||||
) {
|
||||
if let Some((next, id)) = state.collations_per_relay_parent
|
||||
if let Some((next, id)) = state
|
||||
.collations_per_relay_parent
|
||||
.get_mut(&relay_parent)
|
||||
.and_then(|c| c.get_next_collation_to_fetch(Some(previous_fetch)))
|
||||
{
|
||||
@@ -1259,10 +1240,9 @@ async fn handle_collation_fetched_result<Context>(
|
||||
ctx: &mut Context,
|
||||
state: &mut State,
|
||||
(mut collation_event, res): PendingCollationFetch,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
// If no prior collation for this relay parent has been seconded, then
|
||||
// memoize the collation_event for that relay_parent, such that we may
|
||||
@@ -1301,13 +1281,12 @@ where
|
||||
|
||||
if let Entry::Vacant(entry) = state.pending_candidates.entry(relay_parent) {
|
||||
collation_event.1.commitments_hash = Some(candidate_receipt.commitments_hash);
|
||||
ctx.send_message(
|
||||
CandidateBackingMessage::Second(
|
||||
relay_parent.clone(),
|
||||
candidate_receipt,
|
||||
pov,
|
||||
)
|
||||
).await;
|
||||
ctx.send_message(CandidateBackingMessage::Second(
|
||||
relay_parent.clone(),
|
||||
candidate_receipt,
|
||||
pov,
|
||||
))
|
||||
.await;
|
||||
|
||||
entry.insert(collation_event);
|
||||
} else {
|
||||
@@ -1327,10 +1306,9 @@ async fn disconnect_inactive_peers<Context>(
|
||||
ctx: &mut Context,
|
||||
eviction_policy: &crate::CollatorEvictionPolicy,
|
||||
peers: &HashMap<PeerId, PeerData>,
|
||||
)
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
) where
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
{
|
||||
for (peer, peer_data) in peers {
|
||||
if peer_data.is_inactive(&eviction_policy) {
|
||||
@@ -1352,10 +1330,9 @@ async fn poll_collation_response<Context>(
|
||||
spans: &HashMap<Hash, PerLeafSpan>,
|
||||
pending_collation: &PendingCollation,
|
||||
per_req: &mut PerRequest,
|
||||
)
|
||||
-> bool
|
||||
) -> bool
|
||||
where
|
||||
Context: overseer::SubsystemContext<Message=CollatorProtocolMessage>,
|
||||
Context: overseer::SubsystemContext<Message = CollatorProtocolMessage>,
|
||||
Context: SubsystemContext,
|
||||
{
|
||||
if never!(per_req.from_collator.is_terminated()) {
|
||||
@@ -1367,8 +1344,9 @@ where
|
||||
}
|
||||
|
||||
if let Poll::Ready(response) = futures::poll!(&mut per_req.from_collator) {
|
||||
let _span = spans.get(&pending_collation.relay_parent)
|
||||
.map(|s| s.child("received-collation"));
|
||||
let _span = spans
|
||||
.get(&pending_collation.relay_parent)
|
||||
.map(|s| s.child("received-collation"));
|
||||
let _timer = metrics.time_handle_collation_request_result();
|
||||
|
||||
let mut metrics_result = Err(());
|
||||
@@ -1384,12 +1362,9 @@ where
|
||||
err = ?err,
|
||||
"Collator provided response that could not be decoded"
|
||||
);
|
||||
modify_reputation(
|
||||
ctx,
|
||||
pending_collation.peer_id.clone(),
|
||||
COST_CORRUPTED_MESSAGE
|
||||
).await;
|
||||
}
|
||||
modify_reputation(ctx, pending_collation.peer_id.clone(), COST_CORRUPTED_MESSAGE)
|
||||
.await;
|
||||
},
|
||||
Err(RequestError::NetworkError(err)) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
@@ -1404,7 +1379,7 @@ where
|
||||
// which would result in reduced reputation for proper nodes, but the
|
||||
// same can happen for penalties on timeouts, which we also have.
|
||||
modify_reputation(ctx, pending_collation.peer_id.clone(), COST_NETWORK_ERROR).await;
|
||||
}
|
||||
},
|
||||
Err(RequestError::Canceled(_)) => {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
@@ -1417,8 +1392,9 @@ where
|
||||
// sensible. In theory this could be exploited, by DoSing this node,
|
||||
// which would result in reduced reputation for proper nodes, but the
|
||||
// same can happen for penalties on timeouts, which we also have.
|
||||
modify_reputation(ctx, pending_collation.peer_id.clone(), COST_REQUEST_TIMED_OUT).await;
|
||||
}
|
||||
modify_reputation(ctx, pending_collation.peer_id.clone(), COST_REQUEST_TIMED_OUT)
|
||||
.await;
|
||||
},
|
||||
Ok(CollationFetchingResponse::Collation(receipt, _))
|
||||
if receipt.descriptor().para_id != pending_collation.para_id =>
|
||||
{
|
||||
@@ -1446,7 +1422,7 @@ where
|
||||
std::mem::swap(&mut tx, &mut (per_req.to_requester));
|
||||
let result = tx.send((receipt, pov));
|
||||
|
||||
if let Err(_) = result {
|
||||
if let Err(_) = result {
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
hash = ?pending_collation.relay_parent,
|
||||
@@ -1458,7 +1434,7 @@ where
|
||||
metrics_result = Ok(());
|
||||
success = "true";
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
metrics.on_request(metrics_result);
|
||||
per_req.span.as_mut().map(|s| s.add_string_tag("success", success));
|
||||
|
||||
@@ -15,26 +15,26 @@
|
||||
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use super::*;
|
||||
use std::{iter, time::Duration};
|
||||
use std::sync::Arc;
|
||||
use assert_matches::assert_matches;
|
||||
use futures::{executor, future, Future};
|
||||
use sp_core::{crypto::Pair, Encode};
|
||||
use sp_keystore::SyncCryptoStore;
|
||||
use sp_keystore::testing::KeyStore as TestKeyStore;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use assert_matches::assert_matches;
|
||||
use sp_keystore::{testing::KeyStore as TestKeyStore, SyncCryptoStore};
|
||||
use std::{iter, sync::Arc, time::Duration};
|
||||
|
||||
use polkadot_primitives::v1::{
|
||||
CollatorPair, ValidatorId, ValidatorIndex, CoreState, CandidateDescriptor,
|
||||
GroupRotationInfo, ScheduledCore, OccupiedCore, GroupIndex,
|
||||
use polkadot_node_network_protocol::{
|
||||
our_view,
|
||||
request_response::{Requests, ResponseSender},
|
||||
ObservedRole,
|
||||
};
|
||||
use polkadot_node_primitives::BlockData;
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
use polkadot_subsystem::messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest};
|
||||
use polkadot_node_network_protocol::{
|
||||
our_view, ObservedRole, request_response::{Requests, ResponseSender},
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateDescriptor, CollatorPair, CoreState, GroupIndex, GroupRotationInfo, OccupiedCore,
|
||||
ScheduledCore, ValidatorId, ValidatorIndex,
|
||||
};
|
||||
use polkadot_subsystem::messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest};
|
||||
use polkadot_subsystem_testhelpers as test_helpers;
|
||||
|
||||
const ACTIVITY_TIMEOUT: Duration = Duration::from_millis(500);
|
||||
const DECLARE_TIMEOUT: Duration = Duration::from_millis(25);
|
||||
@@ -58,10 +58,7 @@ impl Default for TestState {
|
||||
|
||||
let chain_ids = vec![chain_a, chain_b];
|
||||
let relay_parent = Hash::repeat_byte(0x05);
|
||||
let collators = iter::repeat(())
|
||||
.map(|_| CollatorPair::generate().0)
|
||||
.take(5)
|
||||
.collect();
|
||||
let collators = iter::repeat(()).map(|_| CollatorPair::generate().0).take(5).collect();
|
||||
|
||||
let validators = vec![
|
||||
Sr25519Keyring::Alice,
|
||||
@@ -78,17 +75,11 @@ impl Default for TestState {
|
||||
vec![ValidatorIndex(4)],
|
||||
];
|
||||
|
||||
let group_rotation_info = GroupRotationInfo {
|
||||
session_start_block: 0,
|
||||
group_rotation_frequency: 1,
|
||||
now: 0,
|
||||
};
|
||||
let group_rotation_info =
|
||||
GroupRotationInfo { session_start_block: 0, group_rotation_frequency: 1, now: 0 };
|
||||
|
||||
let cores = vec![
|
||||
CoreState::Scheduled(ScheduledCore {
|
||||
para_id: chain_ids[0],
|
||||
collator: None,
|
||||
}),
|
||||
CoreState::Scheduled(ScheduledCore { para_id: chain_ids[0], collator: None }),
|
||||
CoreState::Free,
|
||||
CoreState::Occupied(OccupiedCore {
|
||||
next_up_on_available: None,
|
||||
@@ -129,14 +120,8 @@ struct TestHarness {
|
||||
fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarness) -> T) {
|
||||
let _ = env_logger::builder()
|
||||
.is_test(true)
|
||||
.filter(
|
||||
Some("polkadot_collator_protocol"),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(
|
||||
Some(LOG_TARGET),
|
||||
log::LevelFilter::Trace,
|
||||
)
|
||||
.filter(Some("polkadot_collator_protocol"), log::LevelFilter::Trace)
|
||||
.filter(Some(LOG_TARGET), log::LevelFilter::Trace)
|
||||
.try_init();
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
@@ -144,10 +129,12 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarne
|
||||
let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
|
||||
|
||||
let keystore = TestKeyStore::new();
|
||||
keystore.sr25519_generate_new(
|
||||
polkadot_primitives::v1::PARACHAIN_KEY_TYPE_ID,
|
||||
Some(&Sr25519Keyring::Alice.to_seed()),
|
||||
).unwrap();
|
||||
keystore
|
||||
.sr25519_generate_new(
|
||||
polkadot_primitives::v1::PARACHAIN_KEY_TYPE_ID,
|
||||
Some(&Sr25519Keyring::Alice.to_seed()),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let subsystem = run(
|
||||
context,
|
||||
@@ -164,18 +151,20 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(test: impl FnOnce(TestHarne
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
}, subsystem)).1.unwrap();
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer_signal(&mut overseer, OverseerSignal::Conclude).await;
|
||||
},
|
||||
subsystem,
|
||||
))
|
||||
.1
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
async fn overseer_send(
|
||||
overseer: &mut VirtualOverseer,
|
||||
msg: CollatorProtocolMessage,
|
||||
) {
|
||||
async fn overseer_send(overseer: &mut VirtualOverseer, msg: CollatorProtocolMessage) {
|
||||
tracing::trace!("Sending message:\n{:?}", &msg);
|
||||
overseer
|
||||
.send(FromOverseer::Communication { msg })
|
||||
@@ -184,9 +173,7 @@ async fn overseer_send(
|
||||
.expect(&format!("{:?} is enough for sending messages.", TIMEOUT));
|
||||
}
|
||||
|
||||
async fn overseer_recv(
|
||||
overseer: &mut VirtualOverseer,
|
||||
) -> AllMessages {
|
||||
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
||||
let msg = overseer_recv_with_timeout(overseer, TIMEOUT)
|
||||
.await
|
||||
.expect(&format!("{:?} is enough to receive messages.", TIMEOUT));
|
||||
@@ -201,16 +188,10 @@ async fn overseer_recv_with_timeout(
|
||||
timeout: Duration,
|
||||
) -> Option<AllMessages> {
|
||||
tracing::trace!("Waiting for message...");
|
||||
overseer
|
||||
.recv()
|
||||
.timeout(timeout)
|
||||
.await
|
||||
overseer.recv().timeout(timeout).await
|
||||
}
|
||||
|
||||
async fn overseer_signal(
|
||||
overseer: &mut VirtualOverseer,
|
||||
signal: OverseerSignal,
|
||||
) {
|
||||
async fn overseer_signal(overseer: &mut VirtualOverseer, signal: OverseerSignal) {
|
||||
overseer
|
||||
.send(FromOverseer::Signal(signal))
|
||||
.timeout(TIMEOUT)
|
||||
@@ -275,10 +256,7 @@ async fn assert_candidate_backing_second(
|
||||
}
|
||||
|
||||
/// Assert that a collator got disconnected.
|
||||
async fn assert_collator_disconnect(
|
||||
virtual_overseer: &mut VirtualOverseer,
|
||||
expected_peer: PeerId,
|
||||
) {
|
||||
async fn assert_collator_disconnect(virtual_overseer: &mut VirtualOverseer, expected_peer: PeerId) {
|
||||
assert_matches!(
|
||||
overseer_recv(virtual_overseer).await,
|
||||
AllMessages::NetworkBridge(NetworkBridgeMessage::DisconnectPeer(
|
||||
@@ -324,28 +302,26 @@ async fn connect_and_declare_collator(
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(
|
||||
peer.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
),
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
collator.public(),
|
||||
para_id,
|
||||
collator.sign(&protocol_v1::declare_signature_payload(&peer)),
|
||||
)
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
|
||||
peer.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
collator.public(),
|
||||
para_id,
|
||||
collator.sign(&protocol_v1::declare_signature_payload(&peer)),
|
||||
),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
/// Advertise a collation.
|
||||
@@ -356,15 +332,12 @@ async fn advertise_collation(
|
||||
) {
|
||||
overseer_send(
|
||||
virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer,
|
||||
protocol_v1::CollatorProtocolMessage::AdvertiseCollation(
|
||||
relay_parent,
|
||||
)
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
|
||||
peer,
|
||||
protocol_v1::CollatorProtocolMessage::AdvertiseCollation(relay_parent),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
// As we receive a relevant advertisement act on it and issue a collation request.
|
||||
@@ -373,29 +346,39 @@ fn act_on_advertisement() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let pair = CollatorPair::generate().0;
|
||||
tracing::trace!("activating");
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
|
||||
connect_and_declare_collator(&mut virtual_overseer, peer_b.clone(), pair.clone(), test_state.chain_ids[0]).await;
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_b.clone(),
|
||||
pair.clone(),
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), test_state.relay_parent).await;
|
||||
|
||||
assert_fetch_collation_request(&mut virtual_overseer, test_state.relay_parent, test_state.chain_ids[0]).await;
|
||||
assert_fetch_collation_request(
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
|
||||
virtual_overseer
|
||||
});
|
||||
@@ -407,16 +390,15 @@ fn collator_reporting_works() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
@@ -428,19 +410,22 @@ fn collator_reporting_works() {
|
||||
peer_b.clone(),
|
||||
test_state.collators[0].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_c.clone(),
|
||||
test_state.collators[1].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::ReportCollator(test_state.collators[0].public()),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
@@ -462,22 +447,19 @@ fn collator_authentication_verification_works() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(
|
||||
peer_b,
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
),
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer_b,
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
// the peer sends a declare message but sign the wrong payload
|
||||
overseer_send(
|
||||
@@ -521,18 +503,17 @@ fn fetch_collations_works() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let second = Hash::random();
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent, second])
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent, second],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
@@ -545,14 +526,16 @@ fn fetch_collations_works() {
|
||||
peer_b.clone(),
|
||||
test_state.collators[0].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_c.clone(),
|
||||
test_state.collators[1].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), test_state.relay_parent).await;
|
||||
advertise_collation(&mut virtual_overseer, peer_c.clone(), test_state.relay_parent).await;
|
||||
@@ -561,7 +544,8 @@ fn fetch_collations_works() {
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
assert!(
|
||||
overseer_recv_with_timeout(&mut &mut virtual_overseer, Duration::from_millis(30)).await.is_none(),
|
||||
@@ -572,29 +556,35 @@ fn fetch_collations_works() {
|
||||
let mut candidate_a = CandidateReceipt::default();
|
||||
candidate_a.descriptor.para_id = test_state.chain_ids[0];
|
||||
candidate_a.descriptor.relay_parent = test_state.relay_parent;
|
||||
response_channel.send(Ok(
|
||||
CollationFetchingResponse::Collation(
|
||||
candidate_a.clone(),
|
||||
pov.clone(),
|
||||
).encode()
|
||||
)).expect("Sending response should succeed");
|
||||
response_channel
|
||||
.send(Ok(
|
||||
CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()).encode()
|
||||
))
|
||||
.expect("Sending response should succeed");
|
||||
|
||||
assert_candidate_backing_second(
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
&pov,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerDisconnected(peer_b.clone())),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerDisconnected(
|
||||
peer_b.clone(),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerDisconnected(peer_c.clone())),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerDisconnected(
|
||||
peer_c.clone(),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
let peer_c = PeerId::random();
|
||||
@@ -605,32 +595,32 @@ fn fetch_collations_works() {
|
||||
peer_b.clone(),
|
||||
test_state.collators[2].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_c.clone(),
|
||||
test_state.collators[3].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_d.clone(),
|
||||
test_state.collators[4].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), second).await;
|
||||
advertise_collation(&mut virtual_overseer, peer_c.clone(), second).await;
|
||||
advertise_collation(&mut virtual_overseer, peer_d.clone(), second).await;
|
||||
|
||||
// Dropping the response channel should lead to fetching the second collation.
|
||||
assert_fetch_collation_request(
|
||||
&mut virtual_overseer,
|
||||
second,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
assert_fetch_collation_request(&mut virtual_overseer, second, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
@@ -643,20 +633,16 @@ fn fetch_collations_works() {
|
||||
}
|
||||
);
|
||||
|
||||
let response_channel_non_exclusive = assert_fetch_collation_request(
|
||||
&mut virtual_overseer,
|
||||
second,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
let response_channel_non_exclusive =
|
||||
assert_fetch_collation_request(&mut virtual_overseer, second, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
// Third collator should receive response after that timeout:
|
||||
Delay::new(MAX_UNSHARED_DOWNLOAD_TIME + Duration::from_millis(50)).await;
|
||||
|
||||
let response_channel = assert_fetch_collation_request(
|
||||
&mut virtual_overseer,
|
||||
second,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
let response_channel =
|
||||
assert_fetch_collation_request(&mut virtual_overseer, second, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
let pov = PoV { block_data: BlockData(vec![1]) };
|
||||
let mut candidate_a = CandidateReceipt::default();
|
||||
@@ -664,26 +650,25 @@ fn fetch_collations_works() {
|
||||
candidate_a.descriptor.relay_parent = second;
|
||||
|
||||
// First request finishes now:
|
||||
response_channel_non_exclusive.send(Ok(
|
||||
CollationFetchingResponse::Collation(
|
||||
candidate_a.clone(),
|
||||
pov.clone(),
|
||||
).encode()
|
||||
)).expect("Sending response should succeed");
|
||||
response_channel_non_exclusive
|
||||
.send(Ok(
|
||||
CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()).encode()
|
||||
))
|
||||
.expect("Sending response should succeed");
|
||||
|
||||
response_channel.send(Ok(
|
||||
CollationFetchingResponse::Collation(
|
||||
candidate_a.clone(),
|
||||
pov.clone(),
|
||||
).encode()
|
||||
)).expect("Sending response should succeed");
|
||||
response_channel
|
||||
.send(Ok(
|
||||
CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()).encode()
|
||||
))
|
||||
.expect("Sending response should succeed");
|
||||
|
||||
assert_candidate_backing_second(
|
||||
&mut virtual_overseer,
|
||||
second,
|
||||
test_state.chain_ids[0],
|
||||
&pov,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
virtual_overseer
|
||||
});
|
||||
@@ -695,18 +680,17 @@ fn fetch_next_collation_on_invalid_collation() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let second = Hash::random();
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent, second])
|
||||
),
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent, second],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
@@ -719,14 +703,16 @@ fn fetch_next_collation_on_invalid_collation() {
|
||||
peer_b.clone(),
|
||||
test_state.collators[0].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_c.clone(),
|
||||
test_state.collators[1].clone(),
|
||||
test_state.chain_ids[0].clone(),
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), test_state.relay_parent).await;
|
||||
advertise_collation(&mut virtual_overseer, peer_c.clone(), test_state.relay_parent).await;
|
||||
@@ -735,28 +721,33 @@ fn fetch_next_collation_on_invalid_collation() {
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
let pov = PoV { block_data: BlockData(vec![]) };
|
||||
let mut candidate_a = CandidateReceipt::default();
|
||||
candidate_a.descriptor.para_id = test_state.chain_ids[0];
|
||||
candidate_a.descriptor.relay_parent = test_state.relay_parent;
|
||||
response_channel.send(Ok(
|
||||
CollationFetchingResponse::Collation(
|
||||
candidate_a.clone(),
|
||||
pov.clone(),
|
||||
).encode()
|
||||
)).expect("Sending response should succeed");
|
||||
response_channel
|
||||
.send(Ok(
|
||||
CollationFetchingResponse::Collation(candidate_a.clone(), pov.clone()).encode()
|
||||
))
|
||||
.expect("Sending response should succeed");
|
||||
|
||||
let receipt = assert_candidate_backing_second(
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
&pov,
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
// Inform that the candidate was invalid.
|
||||
overseer_send(&mut virtual_overseer, CollatorProtocolMessage::Invalid(test_state.relay_parent, receipt)).await;
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::Invalid(test_state.relay_parent, receipt),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
@@ -774,7 +765,8 @@ fn fetch_next_collation_on_invalid_collation() {
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
).await;
|
||||
)
|
||||
.await;
|
||||
|
||||
virtual_overseer
|
||||
});
|
||||
@@ -785,9 +777,7 @@ fn inactive_disconnected() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let pair = CollatorPair::generate().0;
|
||||
|
||||
@@ -795,19 +785,31 @@ fn inactive_disconnected() {
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![hash_a])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![hash_a],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
|
||||
connect_and_declare_collator(&mut virtual_overseer, peer_b.clone(), pair.clone(), test_state.chain_ids[0]).await;
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_b.clone(),
|
||||
pair.clone(),
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), test_state.relay_parent).await;
|
||||
|
||||
assert_fetch_collation_request(&mut virtual_overseer, test_state.relay_parent, test_state.chain_ids[0]).await;
|
||||
assert_fetch_collation_request(
|
||||
&mut virtual_overseer,
|
||||
test_state.relay_parent,
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
|
||||
Delay::new(ACTIVITY_TIMEOUT * 3).await;
|
||||
|
||||
@@ -832,9 +834,7 @@ fn activity_extends_life() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let pair = CollatorPair::generate().0;
|
||||
|
||||
@@ -844,10 +844,11 @@ fn activity_extends_life() {
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![hash_a, hash_b, hash_c])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![hash_a, hash_b, hash_c],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
// 3 heads, 3 times.
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
@@ -856,13 +857,20 @@ fn activity_extends_life() {
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
|
||||
connect_and_declare_collator(&mut virtual_overseer, peer_b.clone(), pair.clone(), test_state.chain_ids[0]).await;
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_b.clone(),
|
||||
pair.clone(),
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
|
||||
Delay::new(ACTIVITY_TIMEOUT * 2 / 3).await;
|
||||
|
||||
advertise_collation(&mut virtual_overseer, peer_b.clone(), hash_a).await;
|
||||
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_a, test_state.chain_ids[0]).await;
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_a, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
Delay::new(ACTIVITY_TIMEOUT * 2 / 3).await;
|
||||
|
||||
@@ -879,7 +887,8 @@ fn activity_extends_life() {
|
||||
}
|
||||
);
|
||||
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_b, test_state.chain_ids[0]).await;
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_b, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
Delay::new(ACTIVITY_TIMEOUT * 2 / 3).await;
|
||||
|
||||
@@ -896,7 +905,8 @@ fn activity_extends_life() {
|
||||
}
|
||||
);
|
||||
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_c, test_state.chain_ids[0]).await;
|
||||
assert_fetch_collation_request(&mut virtual_overseer, hash_c, test_state.chain_ids[0])
|
||||
.await;
|
||||
|
||||
Delay::new(ACTIVITY_TIMEOUT * 3 / 2).await;
|
||||
|
||||
@@ -922,16 +932,15 @@ fn disconnect_if_no_declare() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
@@ -939,14 +948,13 @@ fn disconnect_if_no_declare() {
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(
|
||||
peer_b.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer_b.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_collator_disconnect(&mut virtual_overseer, peer_b.clone()).await;
|
||||
|
||||
@@ -959,18 +967,17 @@ fn disconnect_if_wrong_declare() {
|
||||
let test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let pair = CollatorPair::generate().0;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
@@ -978,28 +985,26 @@ fn disconnect_if_wrong_declare() {
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerConnected(
|
||||
peer_b.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
|
||||
peer_b.clone(),
|
||||
ObservedRole::Full,
|
||||
None,
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
pair.public(),
|
||||
ParaId::from(69),
|
||||
pair.sign(&protocol_v1::declare_signature_payload(&peer_b)),
|
||||
)
|
||||
)
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
|
||||
peer_b.clone(),
|
||||
protocol_v1::CollatorProtocolMessage::Declare(
|
||||
pair.public(),
|
||||
ParaId::from(69),
|
||||
pair.sign(&protocol_v1::declare_signature_payload(&peer_b)),
|
||||
),
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
assert_matches!(
|
||||
overseer_recv(&mut virtual_overseer).await,
|
||||
@@ -1023,33 +1028,39 @@ fn view_change_clears_old_collators() {
|
||||
let mut test_state = TestState::default();
|
||||
|
||||
test_harness(|test_harness| async move {
|
||||
let TestHarness {
|
||||
mut virtual_overseer,
|
||||
} = test_harness;
|
||||
let TestHarness { mut virtual_overseer } = test_harness;
|
||||
|
||||
let pair = CollatorPair::generate().0;
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![test_state.relay_parent])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![test_state.relay_parent],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
let peer_b = PeerId::random();
|
||||
|
||||
connect_and_declare_collator(&mut virtual_overseer, peer_b.clone(), pair.clone(), test_state.chain_ids[0]).await;
|
||||
connect_and_declare_collator(
|
||||
&mut virtual_overseer,
|
||||
peer_b.clone(),
|
||||
pair.clone(),
|
||||
test_state.chain_ids[0],
|
||||
)
|
||||
.await;
|
||||
|
||||
let hash_b = Hash::repeat_byte(69);
|
||||
|
||||
overseer_send(
|
||||
&mut virtual_overseer,
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(
|
||||
NetworkBridgeEvent::OurViewChange(our_view![hash_b])
|
||||
)
|
||||
).await;
|
||||
CollatorProtocolMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
|
||||
our_view![hash_b],
|
||||
)),
|
||||
)
|
||||
.await;
|
||||
|
||||
test_state.group_rotation_info = test_state.group_rotation_info.bump_rotation();
|
||||
respond_to_core_info_queries(&mut virtual_overseer, &test_state).await;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,29 +24,20 @@
|
||||
//! in this graph will be forwarded to the network bridge with
|
||||
//! the `NetworkBridgeMessage::NewGossipTopology` message.
|
||||
|
||||
use std::time::{Duration, Instant};
|
||||
use futures::{channel::oneshot, FutureExt as _};
|
||||
use rand::{SeedableRng, seq::SliceRandom as _};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use polkadot_node_network_protocol::peer_set::PeerSet;
|
||||
use polkadot_node_subsystem::{
|
||||
overseer,
|
||||
messages::{GossipSupportMessage, NetworkBridgeMessage, RuntimeApiMessage, RuntimeApiRequest},
|
||||
overseer, ActiveLeavesUpdate, FromOverseer, OverseerSignal, SpawnedSubsystem, SubsystemContext,
|
||||
SubsystemError,
|
||||
FromOverseer, SpawnedSubsystem, SubsystemContext,
|
||||
messages::{
|
||||
GossipSupportMessage,
|
||||
NetworkBridgeMessage,
|
||||
RuntimeApiMessage,
|
||||
RuntimeApiRequest,
|
||||
},
|
||||
ActiveLeavesUpdate, OverseerSignal,
|
||||
};
|
||||
use polkadot_node_subsystem_util as util;
|
||||
use polkadot_primitives::v1::{
|
||||
Hash, SessionIndex, AuthorityDiscoveryId,
|
||||
};
|
||||
use polkadot_node_network_protocol::peer_set::PeerSet;
|
||||
use polkadot_primitives::v1::{AuthorityDiscoveryId, Hash, SessionIndex};
|
||||
use rand::{seq::SliceRandom as _, SeedableRng};
|
||||
use rand_chacha::ChaCha20Rng;
|
||||
use sp_application_crypto::{AppKey, Public};
|
||||
use sp_keystore::{CryptoStore, SyncCryptoStorePtr};
|
||||
use sp_application_crypto::{Public, AppKey};
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
@@ -90,9 +81,7 @@ struct State {
|
||||
impl GossipSupport {
|
||||
/// Create a new instance of the [`GossipSupport`] subsystem.
|
||||
pub fn new(keystore: SyncCryptoStorePtr) -> Self {
|
||||
Self {
|
||||
keystore,
|
||||
}
|
||||
Self { keystore }
|
||||
}
|
||||
|
||||
async fn run<Context>(self, ctx: Context)
|
||||
@@ -119,7 +108,7 @@ impl GossipSupport {
|
||||
err = ?e,
|
||||
"Failed to receive a message from Overseer, exiting",
|
||||
);
|
||||
return;
|
||||
return
|
||||
},
|
||||
};
|
||||
match message {
|
||||
@@ -134,11 +123,9 @@ impl GossipSupport {
|
||||
if let Err(e) = state.handle_active_leaves(&mut ctx, &keystore, leaves).await {
|
||||
tracing::debug!(target: LOG_TARGET, error = ?e);
|
||||
}
|
||||
}
|
||||
},
|
||||
FromOverseer::Signal(OverseerSignal::BlockFinalized(_hash, _number)) => {},
|
||||
FromOverseer::Signal(OverseerSignal::Conclude) => {
|
||||
return;
|
||||
}
|
||||
FromOverseer::Signal(OverseerSignal::Conclude) => return,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,11 +155,8 @@ async fn ensure_i_am_an_authority(
|
||||
authorities: &[AuthorityDiscoveryId],
|
||||
) -> Result<usize, util::Error> {
|
||||
for (i, v) in authorities.iter().enumerate() {
|
||||
if CryptoStore::has_keys(
|
||||
&**keystore,
|
||||
&[(v.to_raw_vec(), AuthorityDiscoveryId::ID)]
|
||||
).await {
|
||||
return Ok(i);
|
||||
if CryptoStore::has_keys(&**keystore, &[(v.to_raw_vec(), AuthorityDiscoveryId::ID)]).await {
|
||||
return Ok(i)
|
||||
}
|
||||
}
|
||||
Err(util::Error::NotAValidator)
|
||||
@@ -189,13 +173,8 @@ where
|
||||
Context: overseer::SubsystemContext<Message = GossipSupportMessage>,
|
||||
{
|
||||
let (failed, failed_rx) = oneshot::channel();
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::ConnectToValidators {
|
||||
validator_ids,
|
||||
peer_set,
|
||||
failed,
|
||||
}
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::ConnectToValidators { validator_ids, peer_set, failed })
|
||||
.await;
|
||||
failed_rx
|
||||
}
|
||||
|
||||
@@ -224,7 +203,8 @@ where
|
||||
ctx.send_message(RuntimeApiMessage::Request(
|
||||
relay_parent,
|
||||
RuntimeApiRequest::CurrentBabeEpoch(tx),
|
||||
)).await;
|
||||
))
|
||||
.await;
|
||||
|
||||
let randomness = rx.await??.randomness;
|
||||
let mut subject = [0u8; 40];
|
||||
@@ -238,24 +218,22 @@ where
|
||||
let len = authorities.len();
|
||||
let mut indices: Vec<usize> = (0..len).collect();
|
||||
indices.shuffle(&mut rng);
|
||||
let our_shuffled_position = indices.iter()
|
||||
let our_shuffled_position = indices
|
||||
.iter()
|
||||
.position(|i| *i == our_index)
|
||||
.expect("our_index < len; indices contains it; qed");
|
||||
|
||||
let neighbors = matrix_neighbors(our_shuffled_position, len);
|
||||
let our_neighbors = neighbors.map(|i| authorities[indices[i]].clone()).collect();
|
||||
|
||||
ctx.send_message(
|
||||
NetworkBridgeMessage::NewGossipTopology {
|
||||
our_neighbors,
|
||||
}
|
||||
).await;
|
||||
ctx.send_message(NetworkBridgeMessage::NewGossipTopology { our_neighbors })
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Compute our row and column neighbors in a matrix
|
||||
fn matrix_neighbors(our_index: usize, len: usize) -> impl Iterator<Item=usize> {
|
||||
fn matrix_neighbors(our_index: usize, len: usize) -> impl Iterator<Item = usize> {
|
||||
assert!(our_index < len, "our_index is computed using `enumerate`; qed");
|
||||
|
||||
// e.g. for size 11 the matrix would be
|
||||
@@ -291,7 +269,8 @@ impl State {
|
||||
Context: overseer::SubsystemContext<Message = GossipSupportMessage>,
|
||||
{
|
||||
for leaf in leaves {
|
||||
let current_index = util::request_session_index_for_child(leaf, ctx.sender()).await.await??;
|
||||
let current_index =
|
||||
util::request_session_index_for_child(leaf, ctx.sender()).await.await??;
|
||||
let since_failure = self.last_failure.map(|i| i.elapsed()).unwrap_or_default();
|
||||
let force_request = since_failure >= BACKOFF_DURATION;
|
||||
let leaf_session = Some((current_index, leaf));
|
||||
@@ -300,11 +279,8 @@ impl State {
|
||||
_ => leaf_session,
|
||||
};
|
||||
|
||||
let maybe_issue_connection = if force_request {
|
||||
leaf_session
|
||||
} else {
|
||||
maybe_new_session
|
||||
};
|
||||
let maybe_issue_connection =
|
||||
if force_request { leaf_session } else { maybe_new_session };
|
||||
|
||||
if let Some((session_index, relay_parent)) = maybe_issue_connection {
|
||||
let is_new_session = maybe_new_session.is_some();
|
||||
@@ -326,7 +302,6 @@ impl State {
|
||||
update_gossip_topology(ctx, our_index, authorities, relay_parent).await?;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -344,11 +319,7 @@ impl State {
|
||||
let num = authorities.len();
|
||||
tracing::debug!(target: LOG_TARGET, %num, "Issuing a connection request");
|
||||
|
||||
let failures = connect_to_authorities(
|
||||
ctx,
|
||||
authorities,
|
||||
PeerSet::Validation,
|
||||
).await;
|
||||
let failures = connect_to_authorities(ctx, authorities, PeerSet::Validation).await;
|
||||
|
||||
// we await for the request to be processed
|
||||
// this is fine, it should take much less time than one session
|
||||
@@ -367,8 +338,7 @@ impl State {
|
||||
target = ?num,
|
||||
"Low connectivity - authority lookup failed for too many validators."
|
||||
);
|
||||
|
||||
}
|
||||
},
|
||||
Some(_) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
@@ -376,7 +346,7 @@ impl State {
|
||||
target = ?num,
|
||||
"Low connectivity (due to authority lookup failures) - expected on startup."
|
||||
);
|
||||
}
|
||||
},
|
||||
}
|
||||
self.last_failure = Some(timestamp);
|
||||
} else {
|
||||
@@ -394,13 +364,8 @@ where
|
||||
Context: overseer::SubsystemContext<Message = GossipSupportMessage>,
|
||||
{
|
||||
fn start(self, ctx: Context) -> SpawnedSubsystem {
|
||||
let future = self.run(ctx)
|
||||
.map(|_| Ok(()))
|
||||
.boxed();
|
||||
let future = self.run(ctx).map(|_| Ok(())).boxed();
|
||||
|
||||
SpawnedSubsystem {
|
||||
name: "gossip-support-subsystem",
|
||||
future,
|
||||
}
|
||||
SpawnedSubsystem { name: "gossip-support-subsystem", future }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,21 +18,19 @@
|
||||
|
||||
use super::*;
|
||||
use polkadot_node_subsystem::{
|
||||
jaeger, ActivatedLeaf, LeafStatus,
|
||||
jaeger,
|
||||
messages::{AllMessages, RuntimeApiMessage, RuntimeApiRequest},
|
||||
ActivatedLeaf, LeafStatus,
|
||||
};
|
||||
use polkadot_node_subsystem_test_helpers as test_helpers;
|
||||
use polkadot_node_subsystem_util::TimeoutExt as _;
|
||||
use sp_consensus_babe::{AllowedSlots, BabeEpochConfiguration, Epoch as BabeEpoch};
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
use sp_consensus_babe::{
|
||||
Epoch as BabeEpoch, BabeEpochConfiguration, AllowedSlots,
|
||||
};
|
||||
use test_helpers::mock::make_ferdie_keystore;
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
use assert_matches::assert_matches;
|
||||
use futures::{Future, executor, future};
|
||||
use futures::{executor, future, Future};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
|
||||
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<GossipSupportMessage>;
|
||||
|
||||
@@ -53,14 +51,17 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
futures::pin_mut!(test_fut);
|
||||
futures::pin_mut!(subsystem);
|
||||
|
||||
executor::block_on(future::join(async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::Conclude))
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Conclude send timeout");
|
||||
}, subsystem));
|
||||
executor::block_on(future::join(
|
||||
async move {
|
||||
let mut overseer = test_fut.await;
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::Conclude))
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("Conclude send timeout");
|
||||
},
|
||||
subsystem,
|
||||
));
|
||||
}
|
||||
|
||||
state
|
||||
@@ -68,10 +69,7 @@ fn test_harness<T: Future<Output = VirtualOverseer>>(
|
||||
|
||||
const TIMEOUT: Duration = Duration::from_millis(100);
|
||||
|
||||
async fn overseer_signal_active_leaves(
|
||||
overseer: &mut VirtualOverseer,
|
||||
leaf: Hash,
|
||||
) {
|
||||
async fn overseer_signal_active_leaves(overseer: &mut VirtualOverseer, leaf: Hash) {
|
||||
let leaf = ActivatedLeaf {
|
||||
hash: leaf,
|
||||
number: 0xdeadcafe,
|
||||
@@ -79,20 +77,16 @@ async fn overseer_signal_active_leaves(
|
||||
span: Arc::new(jaeger::Span::Disabled),
|
||||
};
|
||||
overseer
|
||||
.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(leaf))))
|
||||
.send(FromOverseer::Signal(OverseerSignal::ActiveLeaves(ActiveLeavesUpdate::start_work(
|
||||
leaf,
|
||||
))))
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("signal send timeout");
|
||||
}
|
||||
|
||||
async fn overseer_recv(
|
||||
overseer: &mut VirtualOverseer,
|
||||
) -> AllMessages {
|
||||
let msg = overseer
|
||||
.recv()
|
||||
.timeout(TIMEOUT)
|
||||
.await
|
||||
.expect("msg recv timeout");
|
||||
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
|
||||
let msg = overseer.recv().timeout(TIMEOUT).await.expect("msg recv timeout");
|
||||
|
||||
msg
|
||||
}
|
||||
@@ -368,7 +362,9 @@ fn test_matrix_neighbors() {
|
||||
(9, 10, vec![0, 3, 6]),
|
||||
(10, 11, vec![1, 4, 7, 9]),
|
||||
(7, 11, vec![1, 4, 6, 8, 10]),
|
||||
].into_iter() {
|
||||
]
|
||||
.into_iter()
|
||||
{
|
||||
let mut result: Vec<_> = matrix_neighbors(our_index, len).collect();
|
||||
result.sort();
|
||||
assert_eq!(result, expected);
|
||||
|
||||
@@ -31,18 +31,30 @@ use sc_network::{Multiaddr, PeerId};
|
||||
#[async_trait]
|
||||
pub trait AuthorityDiscovery: Send + Debug + '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>>;
|
||||
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 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>> {
|
||||
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> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,13 +19,13 @@
|
||||
#![deny(unused_crate_dependencies)]
|
||||
#![warn(missing_docs)]
|
||||
|
||||
use polkadot_primitives::v1::{Hash, BlockNumber};
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use std::{fmt, collections::HashMap};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use polkadot_primitives::v1::{BlockNumber, Hash};
|
||||
use std::{collections::HashMap, fmt};
|
||||
|
||||
pub use sc_network::{PeerId, IfDisconnected};
|
||||
#[doc(hidden)]
|
||||
pub use polkadot_node_jaeger as jaeger;
|
||||
pub use sc_network::{IfDisconnected, PeerId};
|
||||
#[doc(hidden)]
|
||||
pub use std::sync::Arc;
|
||||
|
||||
@@ -46,7 +46,6 @@ pub type ProtocolVersion = u32;
|
||||
/// The minimum amount of peers to send gossip messages to.
|
||||
pub const MIN_GOSSIP_PEERS: usize = 25;
|
||||
|
||||
|
||||
/// An error indicating that this the over-arching message type had the wrong variant
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct WrongVariant;
|
||||
@@ -117,10 +116,9 @@ macro_rules! impl_try_from {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// Specialized wrapper around [`View`].
|
||||
///
|
||||
/// Besides the access to the view itself, it also gives access to the [`jaeger::Span`] per leave/head.
|
||||
@@ -132,16 +130,13 @@ pub struct OurView {
|
||||
|
||||
impl OurView {
|
||||
/// Creates a new instance.
|
||||
pub fn new(heads: impl IntoIterator<Item = (Hash, Arc<jaeger::Span>)>, finalized_number: BlockNumber) -> Self {
|
||||
pub fn new(
|
||||
heads: impl IntoIterator<Item = (Hash, Arc<jaeger::Span>)>,
|
||||
finalized_number: BlockNumber,
|
||||
) -> Self {
|
||||
let state_per_head = heads.into_iter().collect::<HashMap<_, _>>();
|
||||
let view = View::new(
|
||||
state_per_head.keys().cloned(),
|
||||
finalized_number,
|
||||
);
|
||||
Self {
|
||||
view,
|
||||
span_per_head: state_per_head,
|
||||
}
|
||||
let view = View::new(state_per_head.keys().cloned(), finalized_number);
|
||||
Self { view, span_per_head: state_per_head }
|
||||
}
|
||||
|
||||
/// Returns the span per head map.
|
||||
@@ -220,22 +215,15 @@ macro_rules! view {
|
||||
|
||||
impl View {
|
||||
/// Construct a new view based on heads and a finalized block number.
|
||||
pub fn new(heads: impl IntoIterator<Item=Hash>, finalized_number: BlockNumber) -> Self
|
||||
{
|
||||
pub fn new(heads: impl IntoIterator<Item = Hash>, finalized_number: BlockNumber) -> Self {
|
||||
let mut heads = heads.into_iter().collect::<Vec<Hash>>();
|
||||
heads.sort();
|
||||
Self {
|
||||
heads,
|
||||
finalized_number,
|
||||
}
|
||||
Self { heads, finalized_number }
|
||||
}
|
||||
|
||||
/// Start with no heads, but only a finalized block number.
|
||||
pub fn with_finalized(finalized_number: BlockNumber) -> Self {
|
||||
Self {
|
||||
heads: Vec::new(),
|
||||
finalized_number,
|
||||
}
|
||||
Self { heads: Vec::new(), finalized_number }
|
||||
}
|
||||
|
||||
/// Obtain the number of heads that are in view.
|
||||
@@ -249,12 +237,12 @@ impl View {
|
||||
}
|
||||
|
||||
/// Obtain an iterator over all heads.
|
||||
pub fn iter<'a>(&'a self) -> impl Iterator<Item=&'a Hash> {
|
||||
pub fn iter<'a>(&'a self) -> impl Iterator<Item = &'a Hash> {
|
||||
self.heads.iter()
|
||||
}
|
||||
|
||||
/// Obtain an iterator over all heads.
|
||||
pub fn into_iter(self) -> impl Iterator<Item=Hash> {
|
||||
pub fn into_iter(self) -> impl Iterator<Item = Hash> {
|
||||
self.heads.into_iter()
|
||||
}
|
||||
|
||||
@@ -293,13 +281,12 @@ impl View {
|
||||
|
||||
/// v1 protocol types.
|
||||
pub mod v1 {
|
||||
use parity_scale_codec::{Encode, Decode};
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateHash, CandidateIndex, CollatorId, CollatorSignature,
|
||||
CompactStatement, Hash, Id as ParaId, UncheckedSignedAvailabilityBitfield,
|
||||
ValidatorIndex, ValidatorSignature,
|
||||
CandidateHash, CandidateIndex, CollatorId, CollatorSignature, CompactStatement, Hash,
|
||||
Id as ParaId, UncheckedSignedAvailabilityBitfield, ValidatorIndex, ValidatorSignature,
|
||||
};
|
||||
|
||||
use polkadot_node_primitives::{
|
||||
@@ -307,7 +294,6 @@ pub mod v1 {
|
||||
UncheckedSignedFullStatement,
|
||||
};
|
||||
|
||||
|
||||
/// Network messages used by the bitfield distribution subsystem.
|
||||
#[derive(Debug, Clone, Encode, Decode, PartialEq, Eq)]
|
||||
pub enum BitfieldDistributionMessage {
|
||||
@@ -360,8 +346,10 @@ pub mod v1 {
|
||||
/// Get fingerprint describing the contained statement uniquely.
|
||||
pub fn get_fingerprint(&self) -> (CompactStatement, ValidatorIndex) {
|
||||
match self {
|
||||
Self::Statement(_, statement) =>
|
||||
(statement.unchecked_payload().to_compact(), statement.unchecked_validator_index()),
|
||||
Self::Statement(_, statement) => (
|
||||
statement.unchecked_payload().to_compact(),
|
||||
statement.unchecked_validator_index(),
|
||||
),
|
||||
Self::LargeStatement(meta) =>
|
||||
(CompactStatement::Seconded(meta.candidate_hash), meta.signed_by),
|
||||
}
|
||||
|
||||
@@ -17,7 +17,10 @@
|
||||
//! All peersets and protocols used for parachains.
|
||||
|
||||
use sc_network::config::{NonDefaultSetConfig, SetConfig};
|
||||
use std::{borrow::Cow, ops::{Index, IndexMut}};
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
|
||||
/// The peer-sets and thus the protocols which are used for the network.
|
||||
@@ -79,7 +82,7 @@ impl PeerSet {
|
||||
sc_network::config::NonReservedPeerMode::Accept
|
||||
} else {
|
||||
sc_network::config::NonReservedPeerMode::Deny
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -68,9 +68,6 @@ impl UnifiedReputationChange {
|
||||
|
||||
/// Convert into a base reputation as used with substrate.
|
||||
pub const fn into_base_rep(self) -> ReputationChange {
|
||||
ReputationChange::new(
|
||||
self.cost_or_benefit(),
|
||||
self.description()
|
||||
)
|
||||
ReputationChange::new(self.cost_or_benefit(), self.description())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +32,19 @@
|
||||
//!
|
||||
//! Versioned (v1 module): The actual requests and responses as sent over the network.
|
||||
|
||||
use std::{borrow::Cow, u64};
|
||||
use std::time::Duration;
|
||||
use std::{borrow::Cow, time::Duration, u64};
|
||||
|
||||
use futures::channel::mpsc;
|
||||
use polkadot_primitives::v1::{MAX_CODE_SIZE, MAX_POV_SIZE};
|
||||
use strum::EnumIter;
|
||||
|
||||
pub use sc_network::config as network;
|
||||
pub use sc_network::config::RequestResponseConfig;
|
||||
pub use sc_network::{config as network, config::RequestResponseConfig};
|
||||
|
||||
/// All requests that can be sent to the network bridge.
|
||||
pub mod request;
|
||||
pub use request::{IncomingRequest, OutgoingRequest, Requests, Recipient, OutgoingResult, ResponseSender};
|
||||
pub use request::{
|
||||
IncomingRequest, OutgoingRequest, OutgoingResult, Recipient, Requests, ResponseSender,
|
||||
};
|
||||
|
||||
///// Multiplexer for incoming requests.
|
||||
// pub mod multiplexer;
|
||||
@@ -70,10 +70,9 @@ pub enum Protocol {
|
||||
DisputeSending,
|
||||
}
|
||||
|
||||
|
||||
/// Minimum bandwidth we expect for validators - 500Mbit/s is the recommendation, so approximately
|
||||
/// 50MB per second:
|
||||
const MIN_BANDWIDTH_BYTES: u64 = 50 * 1024 * 1024;
|
||||
const MIN_BANDWIDTH_BYTES: u64 = 50 * 1024 * 1024;
|
||||
|
||||
/// Default request timeout in seconds.
|
||||
///
|
||||
@@ -108,12 +107,7 @@ impl Protocol {
|
||||
///
|
||||
/// Returns a receiver for messages received on this protocol and the requested
|
||||
/// `ProtocolConfig`.
|
||||
pub fn get_config(
|
||||
self,
|
||||
) -> (
|
||||
mpsc::Receiver<network::IncomingRequest>,
|
||||
RequestResponseConfig,
|
||||
) {
|
||||
pub fn get_config(self) -> (mpsc::Receiver<network::IncomingRequest>, RequestResponseConfig) {
|
||||
let p_name = self.into_protocol_name();
|
||||
let (tx, rx) = mpsc::channel(self.get_channel_size());
|
||||
let cfg = match self {
|
||||
@@ -208,15 +202,16 @@ impl Protocol {
|
||||
// wasting precious time.
|
||||
let available_bandwidth = 7 * MIN_BANDWIDTH_BYTES / 10;
|
||||
let size = u64::saturating_sub(
|
||||
STATEMENTS_TIMEOUT.as_millis() as u64 * available_bandwidth / (1000 * MAX_CODE_SIZE as u64),
|
||||
MAX_PARALLEL_STATEMENT_REQUESTS as u64
|
||||
STATEMENTS_TIMEOUT.as_millis() as u64 * available_bandwidth /
|
||||
(1000 * MAX_CODE_SIZE as u64),
|
||||
MAX_PARALLEL_STATEMENT_REQUESTS as u64,
|
||||
);
|
||||
debug_assert!(
|
||||
size > 0,
|
||||
"We should have a channel size greater zero, otherwise we won't accept any requests."
|
||||
);
|
||||
size as usize
|
||||
}
|
||||
},
|
||||
// Incoming requests can get bursty, we should also be able to handle them fast on
|
||||
// average, so something in the ballpark of 100 should be fine. Nodes will retry on
|
||||
// failure, so having a good value here is mostly about performance tuning.
|
||||
|
||||
@@ -16,14 +16,12 @@
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use futures::channel::oneshot;
|
||||
use futures::prelude::Future;
|
||||
use futures::{channel::oneshot, prelude::Future};
|
||||
|
||||
use thiserror::Error;
|
||||
use parity_scale_codec::{Decode, Encode, Error as DecodingError};
|
||||
use sc_network as network;
|
||||
use sc_network::config as netconfig;
|
||||
use sc_network::PeerId;
|
||||
use sc_network::{config as netconfig, PeerId};
|
||||
use thiserror::Error;
|
||||
|
||||
use polkadot_primitives::v1::AuthorityDiscoveryId;
|
||||
|
||||
@@ -164,16 +162,9 @@ where
|
||||
pub fn new(
|
||||
peer: Recipient,
|
||||
payload: Req,
|
||||
) -> (
|
||||
Self,
|
||||
impl Future<Output = OutgoingResult<Req::Response>>,
|
||||
) {
|
||||
) -> (Self, impl Future<Output = OutgoingResult<Req::Response>>) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let r = Self {
|
||||
peer,
|
||||
payload,
|
||||
pending_response: tx,
|
||||
};
|
||||
let r = Self { peer, payload, pending_response: tx };
|
||||
(r, receive_response::<Req>(rx))
|
||||
}
|
||||
|
||||
@@ -182,16 +173,8 @@ where
|
||||
/// As this throws away type information, we also return the `Protocol` this encoded request
|
||||
/// adheres to.
|
||||
pub fn encode_request(self) -> (Protocol, OutgoingRequest<Vec<u8>>) {
|
||||
let OutgoingRequest {
|
||||
peer,
|
||||
payload,
|
||||
pending_response,
|
||||
} = self;
|
||||
let encoded = OutgoingRequest {
|
||||
peer,
|
||||
payload: payload.encode(),
|
||||
pending_response,
|
||||
};
|
||||
let OutgoingRequest { peer, payload, pending_response } = self;
|
||||
let encoded = OutgoingRequest { peer, payload: payload.encode(), pending_response };
|
||||
(Req::PROTOCOL, encoded)
|
||||
}
|
||||
}
|
||||
@@ -229,12 +212,12 @@ pub struct IncomingRequest<Req> {
|
||||
|
||||
/// Sender for sending back responses on an `IncomingRequest`.
|
||||
#[derive(Debug)]
|
||||
pub struct OutgoingResponseSender<Req>{
|
||||
pub struct OutgoingResponseSender<Req> {
|
||||
pending_response: oneshot::Sender<netconfig::OutgoingResponse>,
|
||||
phantom: PhantomData<Req>,
|
||||
}
|
||||
|
||||
impl<Req> OutgoingResponseSender<Req>
|
||||
impl<Req> OutgoingResponseSender<Req>
|
||||
where
|
||||
Req: IsRequest + Decode,
|
||||
Req::Response: Encode,
|
||||
@@ -260,20 +243,15 @@ where
|
||||
/// This variant allows for waiting for the response to be sent out, allows for changing peer's
|
||||
/// reputation and allows for not sending a response at all (for only changing the peer's
|
||||
/// reputation).
|
||||
pub fn send_outgoing_response(self, resp: OutgoingResponse<<Req as IsRequest>::Response>)
|
||||
-> Result<(), ()> {
|
||||
let OutgoingResponse {
|
||||
result,
|
||||
reputation_changes,
|
||||
sent_feedback,
|
||||
} = resp;
|
||||
pub fn send_outgoing_response(
|
||||
self,
|
||||
resp: OutgoingResponse<<Req as IsRequest>::Response>,
|
||||
) -> Result<(), ()> {
|
||||
let OutgoingResponse { result, reputation_changes, sent_feedback } = resp;
|
||||
|
||||
let response = netconfig::OutgoingResponse {
|
||||
result: result.map(|v| v.encode()),
|
||||
reputation_changes: reputation_changes
|
||||
.into_iter()
|
||||
.map(|c| c.into_base_rep())
|
||||
.collect(),
|
||||
reputation_changes: reputation_changes.into_iter().map(|c| c.into_base_rep()).collect(),
|
||||
sent_feedback,
|
||||
};
|
||||
|
||||
@@ -313,10 +291,7 @@ where
|
||||
Self {
|
||||
peer,
|
||||
payload,
|
||||
pending_response: OutgoingResponseSender {
|
||||
pending_response,
|
||||
phantom: PhantomData {},
|
||||
},
|
||||
pending_response: OutgoingResponseSender { pending_response, phantom: PhantomData {} },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -330,20 +305,14 @@ where
|
||||
/// - Reputation changes to apply for the peer in case decoding fails.
|
||||
pub fn try_from_raw(
|
||||
raw: sc_network::config::IncomingRequest,
|
||||
reputation_changes: Vec<UnifiedReputationChange>
|
||||
reputation_changes: Vec<UnifiedReputationChange>,
|
||||
) -> Result<Self, ReceiveError> {
|
||||
let sc_network::config::IncomingRequest {
|
||||
payload,
|
||||
peer,
|
||||
pending_response,
|
||||
} = raw;
|
||||
let sc_network::config::IncomingRequest { payload, peer, pending_response } = raw;
|
||||
let payload = match Req::decode(&mut payload.as_ref()) {
|
||||
Ok(payload) => payload,
|
||||
Err(err) => {
|
||||
let reputation_changes = reputation_changes
|
||||
.into_iter()
|
||||
.map(|r| r.into_base_rep())
|
||||
.collect();
|
||||
let reputation_changes =
|
||||
reputation_changes.into_iter().map(|r| r.into_base_rep()).collect();
|
||||
let response = sc_network::config::OutgoingResponse {
|
||||
result: Err(()),
|
||||
reputation_changes,
|
||||
@@ -354,7 +323,7 @@ where
|
||||
return Err(ReceiveError::DecodingErrorNoReputationChange(peer, err))
|
||||
}
|
||||
return Err(ReceiveError::DecodingError(peer, err))
|
||||
}
|
||||
},
|
||||
};
|
||||
Ok(Self::new(peer, payload, pending_response))
|
||||
}
|
||||
@@ -369,8 +338,10 @@ where
|
||||
/// Send response with additional options.
|
||||
///
|
||||
/// Calls [`OutgoingResponseSender::send_outgoing_response`].
|
||||
pub fn send_outgoing_response(self, resp: OutgoingResponse<<Req as IsRequest>::Response>)
|
||||
-> Result<(), ()> {
|
||||
pub fn send_outgoing_response(
|
||||
self,
|
||||
resp: OutgoingResponse<<Req as IsRequest>::Response>,
|
||||
) -> Result<(), ()> {
|
||||
self.pending_response.send_outgoing_response(resp)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,12 +18,14 @@
|
||||
|
||||
use parity_scale_codec::{Decode, Encode};
|
||||
|
||||
use polkadot_primitives::v1::{CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, ValidatorIndex};
|
||||
use polkadot_primitives::v1::Id as ParaId;
|
||||
use polkadot_node_primitives::{AvailableData, DisputeMessage, ErasureChunk, PoV, UncheckedDisputeMessage};
|
||||
use polkadot_node_primitives::{
|
||||
AvailableData, DisputeMessage, ErasureChunk, PoV, UncheckedDisputeMessage,
|
||||
};
|
||||
use polkadot_primitives::v1::{
|
||||
CandidateHash, CandidateReceipt, CommittedCandidateReceipt, Hash, Id as ParaId, ValidatorIndex,
|
||||
};
|
||||
|
||||
use super::request::IsRequest;
|
||||
use super::Protocol;
|
||||
use super::{request::IsRequest, Protocol};
|
||||
|
||||
/// Request an availability chunk.
|
||||
#[derive(Debug, Copy, Clone, Encode, Decode)]
|
||||
@@ -69,19 +71,15 @@ pub struct ChunkResponse {
|
||||
}
|
||||
|
||||
impl From<ErasureChunk> for ChunkResponse {
|
||||
fn from(ErasureChunk {chunk, index: _, proof}: ErasureChunk) -> Self {
|
||||
ChunkResponse {chunk, proof}
|
||||
fn from(ErasureChunk { chunk, index: _, proof }: ErasureChunk) -> Self {
|
||||
ChunkResponse { chunk, proof }
|
||||
}
|
||||
}
|
||||
|
||||
impl ChunkResponse {
|
||||
/// Re-build an `ErasureChunk` from response and request.
|
||||
pub fn recombine_into_chunk(self, req: &ChunkFetchingRequest) -> ErasureChunk {
|
||||
ErasureChunk {
|
||||
chunk: self.chunk,
|
||||
proof: self.proof,
|
||||
index: req.index,
|
||||
}
|
||||
ErasureChunk { chunk: self.chunk, proof: self.proof, index: req.index }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +208,7 @@ impl From<DisputeMessage> for DisputeRequest {
|
||||
pub enum DisputeResponse {
|
||||
/// Recipient successfully processed the dispute request.
|
||||
#[codec(index = 0)]
|
||||
Confirmed
|
||||
Confirmed,
|
||||
}
|
||||
|
||||
impl IsRequest for DisputeRequest {
|
||||
|
||||
@@ -22,7 +22,7 @@ use polkadot_primitives::v1::{CandidateHash, Hash};
|
||||
use polkadot_subsystem::SubsystemError;
|
||||
use thiserror::Error;
|
||||
|
||||
use polkadot_node_subsystem_util::{Fault, runtime, unwrap_non_fatal};
|
||||
use polkadot_node_subsystem_util::{runtime, unwrap_non_fatal, Fault};
|
||||
|
||||
use crate::LOG_TARGET;
|
||||
|
||||
@@ -112,9 +112,7 @@ pub enum 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<()>, ctx: &'static str)
|
||||
-> FatalResult<()>
|
||||
{
|
||||
pub fn log_error(result: Result<()>, ctx: &'static str) -> FatalResult<()> {
|
||||
if let Some(error) = unwrap_non_fatal(result.map_err(|e| e.0))? {
|
||||
tracing::debug!(target: LOG_TARGET, error = ?error, ctx)
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -16,21 +16,23 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use futures::{SinkExt, channel::{mpsc, oneshot}};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
SinkExt,
|
||||
};
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
PeerId, UnifiedReputationChange,
|
||||
request_response::{
|
||||
v1::{StatementFetchingRequest, StatementFetchingResponse},
|
||||
OutgoingRequest, Recipient, Requests,
|
||||
v1::{
|
||||
StatementFetchingRequest, StatementFetchingResponse
|
||||
}
|
||||
}};
|
||||
},
|
||||
PeerId, UnifiedReputationChange,
|
||||
};
|
||||
use polkadot_node_subsystem_util::TimeoutExt;
|
||||
use polkadot_primitives::v1::{CandidateHash, CommittedCandidateReceipt, Hash};
|
||||
use polkadot_subsystem::{Span, Stage};
|
||||
|
||||
use crate::{LOG_TARGET, Metrics, COST_WRONG_HASH};
|
||||
use crate::{Metrics, COST_WRONG_HASH, LOG_TARGET};
|
||||
|
||||
// In case we failed fetching from our known peers, how long we should wait before attempting a
|
||||
// retry, even though we have not yet discovered any new peers. Or in other words how long to
|
||||
@@ -43,7 +45,7 @@ pub enum RequesterMessage {
|
||||
GetMorePeers {
|
||||
relay_parent: Hash,
|
||||
candidate_hash: CandidateHash,
|
||||
tx: oneshot::Sender<Vec<PeerId>>
|
||||
tx: oneshot::Sender<Vec<PeerId>>,
|
||||
},
|
||||
/// Fetching finished, ask for verification. If verification fails, task will continue asking
|
||||
/// peers for data.
|
||||
@@ -65,7 +67,6 @@ pub enum RequesterMessage {
|
||||
SendRequest(Requests),
|
||||
}
|
||||
|
||||
|
||||
/// A fetching task, taking care of fetching large statements via request/response.
|
||||
///
|
||||
/// A fetch task does not know about a particular `Statement` instead it just tries fetching a
|
||||
@@ -87,28 +88,21 @@ pub async fn fetch(
|
||||
// Peers left for trying out.
|
||||
let mut new_peers = peers;
|
||||
|
||||
let req = StatementFetchingRequest {
|
||||
relay_parent,
|
||||
candidate_hash,
|
||||
};
|
||||
let req = StatementFetchingRequest { relay_parent, candidate_hash };
|
||||
|
||||
// We retry endlessly (with sleep periods), and rely on the subsystem to kill us eventually.
|
||||
loop {
|
||||
|
||||
let span = span.child("try-available-peers");
|
||||
|
||||
while let Some(peer) = new_peers.pop() {
|
||||
let _span = span.child("try-peer").with_peer_id(&peer);
|
||||
|
||||
let _span = span.child("try-peer")
|
||||
.with_peer_id(&peer);
|
||||
|
||||
let (outgoing, pending_response) = OutgoingRequest::new(
|
||||
Recipient::Peer(peer),
|
||||
req.clone(),
|
||||
);
|
||||
if let Err(err) = sender.feed(
|
||||
RequesterMessage::SendRequest(Requests::StatementFetching(outgoing))
|
||||
).await {
|
||||
let (outgoing, pending_response) =
|
||||
OutgoingRequest::new(Recipient::Peer(peer), req.clone());
|
||||
if let Err(err) = sender
|
||||
.feed(RequesterMessage::SendRequest(Requests::StatementFetching(outgoing)))
|
||||
.await
|
||||
{
|
||||
tracing::info!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
@@ -121,13 +115,12 @@ pub async fn fetch(
|
||||
|
||||
match pending_response.await {
|
||||
Ok(StatementFetchingResponse::Statement(statement)) => {
|
||||
|
||||
if statement.hash() != candidate_hash {
|
||||
metrics.on_received_response(false);
|
||||
|
||||
if let Err(err) = sender.feed(
|
||||
RequesterMessage::ReportPeer(peer, COST_WRONG_HASH)
|
||||
).await {
|
||||
if let Err(err) =
|
||||
sender.feed(RequesterMessage::ReportPeer(peer, COST_WRONG_HASH)).await
|
||||
{
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
@@ -138,15 +131,16 @@ pub async fn fetch(
|
||||
continue
|
||||
}
|
||||
|
||||
if let Err(err) = sender.feed(
|
||||
RequesterMessage::Finished {
|
||||
if let Err(err) = sender
|
||||
.feed(RequesterMessage::Finished {
|
||||
relay_parent,
|
||||
candidate_hash,
|
||||
from_peer: peer,
|
||||
response: statement,
|
||||
bad_peers: tried_peers,
|
||||
}
|
||||
).await {
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::warn!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
@@ -167,7 +161,7 @@ pub async fn fetch(
|
||||
);
|
||||
|
||||
metrics.on_received_response(false);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
tried_peers.push(peer);
|
||||
@@ -178,14 +172,10 @@ pub async fn fetch(
|
||||
// All our peers failed us - try getting new ones before trying again:
|
||||
match try_get_new_peers(relay_parent, candidate_hash, &mut sender, &span).await {
|
||||
Ok(Some(mut peers)) => {
|
||||
tracing::trace!(
|
||||
target: LOG_TARGET,
|
||||
?peers,
|
||||
"Received new peers."
|
||||
);
|
||||
tracing::trace!(target: LOG_TARGET, ?peers, "Received new peers.");
|
||||
// New arrivals will be tried first:
|
||||
new_peers.append(&mut peers);
|
||||
}
|
||||
},
|
||||
// No new peers, try the old ones again (if we have any):
|
||||
Ok(None) => {
|
||||
// Note: In case we don't have any more peers, we will just keep asking for new
|
||||
@@ -205,14 +195,14 @@ async fn try_get_new_peers(
|
||||
sender: &mut mpsc::Sender<RequesterMessage>,
|
||||
span: &Span,
|
||||
) -> Result<Option<Vec<PeerId>>, ()> {
|
||||
|
||||
let _span = span.child("wait-for-peers");
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
|
||||
if let Err(err) = sender.send(
|
||||
RequesterMessage::GetMorePeers { relay_parent, candidate_hash, tx }
|
||||
).await {
|
||||
if let Err(err) = sender
|
||||
.send(RequesterMessage::GetMorePeers { relay_parent, candidate_hash, tx })
|
||||
.await
|
||||
{
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
@@ -223,13 +213,9 @@ async fn try_get_new_peers(
|
||||
|
||||
match rx.timeout(RETRY_TIMEOUT).await.transpose() {
|
||||
Err(_) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Failed fetching more peers."
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, "Failed fetching more peers.");
|
||||
Err(())
|
||||
}
|
||||
Ok(val) => Ok(val)
|
||||
},
|
||||
Ok(val) => Ok(val),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,16 +14,19 @@
|
||||
|
||||
//! Large statement responding background task logic.
|
||||
|
||||
use futures::{SinkExt, StreamExt, channel::{mpsc, oneshot}, stream::FuturesUnordered};
|
||||
use futures::{
|
||||
channel::{mpsc, oneshot},
|
||||
stream::FuturesUnordered,
|
||||
SinkExt, StreamExt,
|
||||
};
|
||||
|
||||
use polkadot_node_network_protocol::{
|
||||
PeerId, UnifiedReputationChange as Rep,
|
||||
request_response::{
|
||||
IncomingRequest, MAX_PARALLEL_STATEMENT_REQUESTS, request::OutgoingResponse,
|
||||
v1::{
|
||||
StatementFetchingRequest, StatementFetchingResponse
|
||||
},
|
||||
request::OutgoingResponse,
|
||||
v1::{StatementFetchingRequest, StatementFetchingResponse},
|
||||
IncomingRequest, MAX_PARALLEL_STATEMENT_REQUESTS,
|
||||
},
|
||||
PeerId, UnifiedReputationChange as Rep,
|
||||
};
|
||||
use polkadot_primitives::v1::{CandidateHash, CommittedCandidateReceipt, Hash};
|
||||
|
||||
@@ -38,11 +41,10 @@ pub enum ResponderMessage {
|
||||
requesting_peer: PeerId,
|
||||
relay_parent: Hash,
|
||||
candidate_hash: CandidateHash,
|
||||
tx: oneshot::Sender<CommittedCandidateReceipt>
|
||||
tx: oneshot::Sender<CommittedCandidateReceipt>,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
/// A fetching task, taking care of fetching large statements via request/response.
|
||||
///
|
||||
/// A fetch task does not know about a particular `Statement` instead it just tries fetching a
|
||||
@@ -74,56 +76,41 @@ pub async fn respond(
|
||||
|
||||
let raw = match receiver.next().await {
|
||||
None => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Shutting down request responder"
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, "Shutting down request responder");
|
||||
return
|
||||
}
|
||||
},
|
||||
Some(v) => v,
|
||||
};
|
||||
|
||||
let req =
|
||||
match IncomingRequest::<StatementFetchingRequest>::try_from_raw(
|
||||
raw,
|
||||
vec![COST_INVALID_REQUEST],
|
||||
) {
|
||||
let req = match IncomingRequest::<StatementFetchingRequest>::try_from_raw(
|
||||
raw,
|
||||
vec![COST_INVALID_REQUEST],
|
||||
) {
|
||||
Err(err) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"Decoding request failed"
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, ?err, "Decoding request failed");
|
||||
continue
|
||||
}
|
||||
},
|
||||
Ok(payload) => payload,
|
||||
};
|
||||
|
||||
let (tx, rx) = oneshot::channel();
|
||||
if let Err(err) = sender.feed(
|
||||
ResponderMessage::GetData {
|
||||
if let Err(err) = sender
|
||||
.feed(ResponderMessage::GetData {
|
||||
requesting_peer: req.peer,
|
||||
relay_parent: req.payload.relay_parent,
|
||||
candidate_hash: req.payload.candidate_hash,
|
||||
tx,
|
||||
}
|
||||
).await {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"Shutting down responder"
|
||||
);
|
||||
})
|
||||
.await
|
||||
{
|
||||
tracing::debug!(target: LOG_TARGET, ?err, "Shutting down responder");
|
||||
return
|
||||
}
|
||||
let response = match rx.await {
|
||||
Err(err) => {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
?err,
|
||||
"Requested data not found."
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, ?err, "Requested data not found.");
|
||||
Err(())
|
||||
}
|
||||
},
|
||||
Ok(v) => Ok(StatementFetchingResponse::Statement(v)),
|
||||
};
|
||||
let (pending_sent_tx, pending_sent_rx) = oneshot::channel();
|
||||
@@ -134,10 +121,7 @@ pub async fn respond(
|
||||
};
|
||||
pending_out.push(pending_sent_rx);
|
||||
if let Err(_) = req.send_outgoing_response(response) {
|
||||
tracing::debug!(
|
||||
target: LOG_TARGET,
|
||||
"Sending response failed"
|
||||
);
|
||||
tracing::debug!(target: LOG_TARGET, "Sending response failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user