Files
pezkuwi-subxt/polkadot/node/network/approval-distribution/src/tests.rs
T
Bernhard Schuster d631f1dea8 observability: tracing gum, automatically cross ref traceID (#5079)
* add some gum

* bump expander

* gum

* fix all remaining issues

* last fixup

* Update node/gum/proc-macro/src/lib.rs

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>

* change

* netowrk

* fixins

* chore

* allow optional fmt str + args, prep for expr as kv field

* tracing -> gum rename fallout

* restrict further

* allow multiple levels of field accesses

* another round of docs and a slip of the pen

* update ADR

* fixup lock fiel

* use target: instead of target=

* minors

* fix

* chore

* Update node/gum/README.md

Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>

Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com>
Co-authored-by: Andrei Sandu <54316454+sandreim@users.noreply.github.com>
2022-03-15 11:05:16 +00:00

1055 lines
31 KiB
Rust

// Copyright 2020 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
use super::*;
use assert_matches::assert_matches;
use futures::{executor, future, Future};
use polkadot_node_network_protocol::{our_view, 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 std::time::Duration;
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ApprovalDistributionMessage>;
fn dummy_signature() -> polkadot_primitives::v2::ValidatorSignature {
sp_core::crypto::UncheckedFrom::unchecked_from([1u8; 64])
}
fn test_harness<T: Future<Output = VirtualOverseer>>(
mut state: State,
test_fn: impl FnOnce(VirtualOverseer) -> T,
) -> State {
let _ = env_logger::builder()
.is_test(true)
.filter(Some(LOG_TARGET), log::LevelFilter::Trace)
.try_init();
let pool = sp_core::testing::TaskExecutor::new();
let (context, virtual_overseer) = test_helpers::make_subsystem_context(pool.clone());
let subsystem = ApprovalDistribution::new(Default::default());
{
let subsystem = subsystem.run_inner(context, &mut state);
let test_fut = test_fn(virtual_overseer);
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,
));
}
state
}
const TIMEOUT: Duration = Duration::from_millis(100);
async fn overseer_send(overseer: &mut VirtualOverseer, msg: ApprovalDistributionMessage) {
gum::trace!(msg = ?msg, "Sending message");
overseer
.send(FromOverseer::Communication { msg })
.timeout(TIMEOUT)
.await
.expect("msg send timeout");
}
async fn overseer_signal_block_finalized(overseer: &mut VirtualOverseer, number: BlockNumber) {
gum::trace!(?number, "Sending a finalized signal");
// we don't care about the block hash
overseer
.send(FromOverseer::Signal(OverseerSignal::BlockFinalized(Hash::zero(), number)))
.timeout(TIMEOUT)
.await
.expect("signal send timeout");
}
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
gum::trace!("Waiting for a message");
let msg = overseer.recv().timeout(TIMEOUT).await.expect("msg recv timeout");
gum::trace!(msg = ?msg, "Received message");
msg
}
async fn setup_peer_with_view(
virtual_overseer: &mut VirtualOverseer,
peer_id: &PeerId,
view: View,
) {
overseer_send(
virtual_overseer,
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerConnected(
peer_id.clone(),
ObservedRole::Full,
None,
)),
)
.await;
overseer_send(
virtual_overseer,
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerViewChange(
peer_id.clone(),
view,
)),
)
.await;
}
async fn send_message_from_peer(
virtual_overseer: &mut VirtualOverseer,
peer_id: &PeerId,
msg: protocol_v1::ApprovalDistributionMessage,
) {
overseer_send(
virtual_overseer,
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::PeerMessage(
peer_id.clone(),
msg,
)),
)
.await;
}
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;
let keypair = schnorrkel::Keypair::generate_with(&mut prng);
let (inout, proof, _) = keypair.vrf_sign(ctx.bytes(msg));
let out = inout.to_output();
IndirectAssignmentCert {
block_hash,
validator,
cert: AssignmentCert {
kind: AssignmentCertKind::RelayVRFModulo { sample: 1 },
vrf: (VRFOutput(out), VRFProof(proof)),
},
}
}
async fn expect_reputation_change(
virtual_overseer: &mut VirtualOverseer,
peer_id: &PeerId,
expected_reputation_change: Rep,
) {
assert_matches!(
overseer_recv(virtual_overseer).await,
AllMessages::NetworkBridge(
NetworkBridgeMessage::ReportPeer(
rep_peer,
rep,
)
) => {
assert_eq!(peer_id, &rep_peer);
assert_eq!(expected_reputation_change, rep);
}
);
}
/// import an assignment
/// connect a new peer
/// the new peer sends us the same assignment
#[test]
fn try_import_the_same_assignment() {
let peer_a = PeerId::random();
let peer_b = PeerId::random();
let peer_c = PeerId::random();
let peer_d = PeerId::random();
let parent_hash = Hash::repeat_byte(0xFF);
let hash = Hash::repeat_byte(0xAA);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// setup peers
setup_peer_with_view(overseer, &peer_a, view![]).await;
setup_peer_with_view(overseer, &peer_b, view![hash]).await;
setup_peer_with_view(overseer, &peer_c, view![hash]).await;
// new block `hash_a` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 2,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
// send the assignment related to `hash`
let validator_index = ValidatorIndex(0);
let cert = fake_assignment_cert(hash, validator_index);
let assignments = vec![(cert.clone(), 0u32)];
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
send_message_from_peer(overseer, &peer_a, msg).await;
expect_reputation_change(overseer, &peer_a, COST_UNEXPECTED_MESSAGE).await;
// send an `Accept` message from the Approval Voting subsystem
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
assignment,
0u32,
tx,
)) => {
assert_eq!(assignment, cert);
tx.send(AssignmentCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, &peer_a, BENEFIT_VALID_MESSAGE_FIRST).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
)
)) => {
assert_eq!(peers.len(), 2);
assert_eq!(assignments.len(), 1);
}
);
// setup new peer
setup_peer_with_view(overseer, &peer_d, view![]).await;
// send the same assignment from peer_d
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
send_message_from_peer(overseer, &peer_d, msg).await;
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");
virtual_overseer
});
}
/// <https://github.com/paritytech/polkadot/pull/2160#discussion_r547594835>
///
/// 1. Send a view update that removes block B from their view.
/// 2. Send a message from B that they incur `COST_UNEXPECTED_MESSAGE` for,
/// but then they receive `BENEFIT_VALID_MESSAGE`.
/// 3. Send all other messages related to B.
#[test]
fn spam_attack_results_in_negative_reputation_change() {
let parent_hash = Hash::repeat_byte(0xFF);
let peer_a = PeerId::random();
let hash_b = Hash::repeat_byte(0xBB);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
let peer = &peer_a;
setup_peer_with_view(overseer, peer, view![]).await;
// new block `hash_b` with 20 candidates
let candidates_count = 20;
let meta = BlockApprovalMeta {
hash: hash_b.clone(),
parent_hash,
number: 2,
candidates: vec![Default::default(); candidates_count],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
// send 20 assignments related to `hash_b`
// to populate our knowledge
let assignments: Vec<_> = (0..candidates_count)
.map(|candidate_index| {
let validator_index = ValidatorIndex(candidate_index as u32);
let cert = fake_assignment_cert(hash_b, validator_index);
(cert, candidate_index as u32)
})
.collect();
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
send_message_from_peer(overseer, peer, msg.clone()).await;
for i in 0..candidates_count {
expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
assignment,
claimed_candidate_index,
tx,
)) => {
assert_eq!(assignment, assignments[i].0);
assert_eq!(claimed_candidate_index, assignments[i].1);
tx.send(AssignmentCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
}
// 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;
// send the assignments again
send_message_from_peer(overseer, peer, msg.clone()).await;
// each of them will incur `COST_UNEXPECTED_MESSAGE`, not only the first one
for _ in 0..candidates_count {
expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE).await;
}
virtual_overseer
});
}
/// 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.
///
/// See <https://github.com/paritytech/polkadot/issues/2499>.
#[test]
fn peer_sending_us_the_same_we_just_sent_them_is_ok() {
let parent_hash = Hash::repeat_byte(0xFF);
let peer_a = PeerId::random();
let hash = Hash::repeat_byte(0xAA);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
let peer = &peer_a;
setup_peer_with_view(overseer, peer, view![]).await;
// new block `hash` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
// import an assignment related to `hash` locally
let validator_index = ValidatorIndex(0);
let candidate_index = 0u32;
let cert = fake_assignment_cert(hash, validator_index);
overseer_send(
overseer,
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;
// we should send them the assignment
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
)
)) => {
assert_eq!(peers.len(), 1);
assert_eq!(assignments.len(), 1);
}
);
// but if someone else is sending it the same assignment
// the peer could send us it as well
let assignments = vec![(cert, candidate_index)];
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");
// send the assignments again
send_message_from_peer(overseer, peer, msg).await;
// now we should
expect_reputation_change(overseer, peer, COST_DUPLICATE_MESSAGE).await;
virtual_overseer
});
}
#[test]
fn import_approval_happy_path() {
let peer_a = PeerId::random();
let peer_b = PeerId::random();
let peer_c = PeerId::random();
let parent_hash = Hash::repeat_byte(0xFF);
let hash = Hash::repeat_byte(0xAA);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// setup peers
setup_peer_with_view(overseer, &peer_a, view![]).await;
setup_peer_with_view(overseer, &peer_b, view![hash]).await;
setup_peer_with_view(overseer, &peer_c, view![hash]).await;
// new block `hash_a` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
// import an assignment related to `hash` locally
let validator_index = ValidatorIndex(0);
let candidate_index = 0u32;
let cert = fake_assignment_cert(hash, validator_index);
overseer_send(
overseer,
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
)
.await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
)
)) => {
assert_eq!(peers.len(), 2);
assert_eq!(assignments.len(), 1);
}
);
// send the an approval from peer_b
let approval = IndirectSignedApprovalVote {
block_hash: hash,
candidate_index,
validator: validator_index,
signature: dummy_signature(),
};
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
send_message_from_peer(overseer, &peer_b, msg).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
vote,
tx,
)) => {
assert_eq!(vote, approval);
tx.send(ApprovalCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Approvals(approvals)
)
)) => {
assert_eq!(peers.len(), 1);
assert_eq!(approvals.len(), 1);
}
);
virtual_overseer
});
}
#[test]
fn import_approval_bad() {
let peer_a = PeerId::random();
let peer_b = PeerId::random();
let parent_hash = Hash::repeat_byte(0xFF);
let hash = Hash::repeat_byte(0xAA);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// setup peers
setup_peer_with_view(overseer, &peer_a, view![]).await;
setup_peer_with_view(overseer, &peer_b, view![hash]).await;
// new block `hash_a` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
let validator_index = ValidatorIndex(0);
let candidate_index = 0u32;
let cert = fake_assignment_cert(hash, validator_index);
// send the an approval from peer_b, we don't have an assignment yet
let approval = IndirectSignedApprovalVote {
block_hash: hash,
candidate_index,
validator: validator_index,
signature: dummy_signature(),
};
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
send_message_from_peer(overseer, &peer_b, msg).await;
expect_reputation_change(overseer, &peer_b, COST_UNEXPECTED_MESSAGE).await;
// now import an assignment from peer_b
let assignments = vec![(cert.clone(), candidate_index)];
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments);
send_message_from_peer(overseer, &peer_b, msg).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
assignment,
i,
tx,
)) => {
assert_eq!(assignment, cert);
assert_eq!(i, candidate_index);
tx.send(AssignmentCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, &peer_b, BENEFIT_VALID_MESSAGE_FIRST).await;
// and try again
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
send_message_from_peer(overseer, &peer_b, msg).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
vote,
tx,
)) => {
assert_eq!(vote, approval);
tx.send(ApprovalCheckResult::Bad(ApprovalCheckError::UnknownBlock(hash))).unwrap();
}
);
expect_reputation_change(overseer, &peer_b, COST_INVALID_MESSAGE).await;
virtual_overseer
});
}
/// make sure we clean up the state on block finalized
#[test]
fn update_our_view() {
let parent_hash = Hash::repeat_byte(0xFF);
let hash_a = Hash::repeat_byte(0xAA);
let hash_b = Hash::repeat_byte(0xBB);
let hash_c = Hash::repeat_byte(0xCC);
let state = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// new block `hash_a` with 1 candidates
let meta_a = BlockApprovalMeta {
hash: hash_a,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let meta_b = BlockApprovalMeta {
hash: hash_b,
parent_hash: hash_a,
number: 2,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let meta_c = BlockApprovalMeta {
hash: hash_c,
parent_hash: hash_b,
number: 3,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]);
overseer_send(overseer, msg).await;
virtual_overseer
});
assert!(state.blocks_by_number.get(&1).is_some());
assert!(state.blocks_by_number.get(&2).is_some());
assert!(state.blocks_by_number.get(&3).is_some());
assert!(state.blocks.get(&hash_a).is_some());
assert!(state.blocks.get(&hash_b).is_some());
assert!(state.blocks.get(&hash_c).is_some());
let state = test_harness(state, |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// finalize a block
overseer_signal_block_finalized(overseer, 2).await;
virtual_overseer
});
assert!(state.blocks_by_number.get(&1).is_none());
assert!(state.blocks_by_number.get(&2).is_none());
assert!(state.blocks_by_number.get(&3).is_some());
assert!(state.blocks.get(&hash_a).is_none());
assert!(state.blocks.get(&hash_b).is_none());
assert!(state.blocks.get(&hash_c).is_some());
let state = test_harness(state, |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// finalize a very high block
overseer_signal_block_finalized(overseer, 4_000_000_000).await;
virtual_overseer
});
assert!(state.blocks_by_number.get(&3).is_none());
assert!(state.blocks.get(&hash_c).is_none());
}
/// make sure we unify with peers and clean up the state
#[test]
fn update_peer_view() {
let parent_hash = Hash::repeat_byte(0xFF);
let hash_a = Hash::repeat_byte(0xAA);
let hash_b = Hash::repeat_byte(0xBB);
let hash_c = Hash::repeat_byte(0xCC);
let hash_d = Hash::repeat_byte(0xDD);
let peer_a = PeerId::random();
let peer = &peer_a;
let state = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// new block `hash_a` with 1 candidates
let meta_a = BlockApprovalMeta {
hash: hash_a,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let meta_b = BlockApprovalMeta {
hash: hash_b,
parent_hash: hash_a,
number: 2,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let meta_c = BlockApprovalMeta {
hash: hash_c,
parent_hash: hash_b,
number: 3,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta_a, meta_b, meta_c]);
overseer_send(overseer, msg).await;
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_b, 0)).await;
// connect a peer
setup_peer_with_view(overseer, peer, view![hash_a]).await;
// we should send relevant assignments to the peer
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
)
)) => {
assert_eq!(peers.len(), 1);
assert_eq!(assignments.len(), 1);
}
);
virtual_overseer
});
assert_eq!(state.peer_views.get(peer).map(|v| v.finalized_number), Some(0));
assert_eq!(
state
.blocks
.get(&hash_a)
.unwrap()
.known_by
.get(peer)
.unwrap()
.sent
.known_messages
.len(),
1,
);
let state = test_harness(state, |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// update peer's view
overseer_send(
overseer,
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;
// we should send relevant assignments to the peer
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(assignments)
)
)) => {
assert_eq!(peers.len(), 1);
assert_eq!(assignments.len(), 1);
assert_eq!(assignments[0].0, cert_c);
}
);
virtual_overseer
});
assert_eq!(state.peer_views.get(peer).map(|v| v.finalized_number), Some(2));
assert_eq!(
state
.blocks
.get(&hash_c)
.unwrap()
.known_by
.get(peer)
.unwrap()
.sent
.known_messages
.len(),
1,
);
let finalized_number = 4_000_000_000;
let state = test_harness(state, |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// update peer's view
overseer_send(
overseer,
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());
}
/// E.g. if someone copies the keys...
#[test]
fn import_remotely_then_locally() {
let peer_a = PeerId::random();
let parent_hash = Hash::repeat_byte(0xFF);
let hash = Hash::repeat_byte(0xAA);
let peer = &peer_a;
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// setup the peer
setup_peer_with_view(overseer, peer, view![hash]).await;
// new block `hash_a` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
// import the assignment remotely first
let validator_index = ValidatorIndex(0);
let candidate_index = 0u32;
let cert = fake_assignment_cert(hash, validator_index);
let assignments = vec![(cert.clone(), candidate_index)];
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
send_message_from_peer(overseer, peer, msg).await;
// send an `Accept` message from the Approval Voting subsystem
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
assignment,
i,
tx,
)) => {
assert_eq!(assignment, cert);
assert_eq!(i, candidate_index);
tx.send(AssignmentCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
// import the same assignment locally
overseer_send(
overseer,
ApprovalDistributionMessage::DistributeAssignment(cert, candidate_index),
)
.await;
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
// send the approval remotely
let approval = IndirectSignedApprovalVote {
block_hash: hash,
candidate_index,
validator: validator_index,
signature: dummy_signature(),
};
let msg = protocol_v1::ApprovalDistributionMessage::Approvals(vec![approval.clone()]);
send_message_from_peer(overseer, peer, msg).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportApproval(
vote,
tx,
)) => {
assert_eq!(vote, approval);
tx.send(ApprovalCheckResult::Accepted).unwrap();
}
);
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
// import the same approval locally
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval)).await;
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
virtual_overseer
});
}
#[test]
fn sends_assignments_even_when_state_is_approved() {
let peer_a = PeerId::random();
let parent_hash = Hash::repeat_byte(0xFF);
let hash = Hash::repeat_byte(0xAA);
let peer = &peer_a;
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
// new block `hash_a` with 1 candidates
let meta = BlockApprovalMeta {
hash,
parent_hash,
number: 1,
candidates: vec![Default::default(); 1],
slot: 1.into(),
};
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
let validator_index = ValidatorIndex(0);
let candidate_index = 0u32;
// import an assignment and approval locally.
let cert = fake_assignment_cert(hash, validator_index);
let approval = IndirectSignedApprovalVote {
block_hash: hash,
candidate_index,
validator: validator_index,
signature: dummy_signature(),
};
overseer_send(
overseer,
ApprovalDistributionMessage::DistributeAssignment(cert.clone(), candidate_index),
)
.await;
overseer_send(overseer, ApprovalDistributionMessage::DistributeApproval(approval.clone()))
.await;
// connect the peer.
setup_peer_with_view(overseer, peer, view![hash]).await;
let assignments = vec![(cert.clone(), candidate_index)];
let approvals = vec![approval.clone()];
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Assignments(sent_assignments)
)
)) => {
assert_eq!(peers, vec![peer.clone()]);
assert_eq!(sent_assignments, assignments);
}
);
assert_matches!(
overseer_recv(overseer).await,
AllMessages::NetworkBridge(NetworkBridgeMessage::SendValidationMessage(
peers,
protocol_v1::ValidationProtocol::ApprovalDistribution(
protocol_v1::ApprovalDistributionMessage::Approvals(sent_approvals)
)
)) => {
assert_eq!(peers, vec![peer.clone()]);
assert_eq!(sent_approvals, approvals);
}
);
assert!(overseer.recv().timeout(TIMEOUT).await.is_none(), "no message should be sent");
virtual_overseer
});
}
/// <https://github.com/paritytech/polkadot/pull/5089>
///
/// 1. Receive remote peer view update with an unknown head
/// 2. Receive assignments for that unknown head
/// 3. Update our view and import the new block
/// 4. Expect that no reputation with `COST_UNEXPECTED_MESSAGE` is applied
#[test]
fn race_condition_in_local_vs_remote_view_update() {
let parent_hash = Hash::repeat_byte(0xFF);
let peer_a = PeerId::random();
let hash_b = Hash::repeat_byte(0xBB);
let _ = test_harness(State::default(), |mut virtual_overseer| async move {
let overseer = &mut virtual_overseer;
let peer = &peer_a;
// Test a small number of candidates
let candidates_count = 1;
let meta = BlockApprovalMeta {
hash: hash_b.clone(),
parent_hash,
number: 2,
candidates: vec![Default::default(); candidates_count],
slot: 1.into(),
};
// This will send a peer view that is ahead of our view
setup_peer_with_view(overseer, peer, view![hash_b]).await;
// Send our view update to include a new head
overseer_send(
overseer,
ApprovalDistributionMessage::NetworkBridgeUpdateV1(NetworkBridgeEvent::OurViewChange(
our_view![hash_b],
)),
)
.await;
// send assignments related to `hash_b` but they will come to the MessagesPending
let assignments: Vec<_> = (0..candidates_count)
.map(|candidate_index| {
let validator_index = ValidatorIndex(candidate_index as u32);
let cert = fake_assignment_cert(hash_b, validator_index);
(cert, candidate_index as u32)
})
.collect();
let msg = protocol_v1::ApprovalDistributionMessage::Assignments(assignments.clone());
send_message_from_peer(overseer, peer, msg.clone()).await;
// This will handle pending messages being processed
let msg = ApprovalDistributionMessage::NewBlocks(vec![meta]);
overseer_send(overseer, msg).await;
for i in 0..candidates_count {
// Previously, this has caused out-of-view assignments/approvals
//expect_reputation_change(overseer, peer, COST_UNEXPECTED_MESSAGE).await;
assert_matches!(
overseer_recv(overseer).await,
AllMessages::ApprovalVoting(ApprovalVotingMessage::CheckAndImportAssignment(
assignment,
claimed_candidate_index,
tx,
)) => {
assert_eq!(assignment, assignments[i].0);
assert_eq!(claimed_candidate_index, assignments[i].1);
tx.send(AssignmentCheckResult::Accepted).unwrap();
}
);
// Since we have a valid statement pending, this should always occur
expect_reputation_change(overseer, peer, BENEFIT_VALID_MESSAGE_FIRST).await;
}
virtual_overseer
});
}