Files
pezkuwi-subxt/polkadot/node/network/approval-distribution/src/tests.rs
T
Robert Klotzner dacde443f7 Infrastructure improvements (#2897)
* Factor out runtime module into utils.

* Add maybe_authority information to `PeerConnected` event.

We already gather this information in authority discovery, so we might
as well share it with others.

This opens up an easy path to trigger validators differently from normal
nodes, e.g. for prioritization. This change has become more important
now, that we just connect to all validators and therefore just have a
long peer list without any information about those nodes.

* Test fix.
2021-04-16 21:42:20 +02:00

1019 lines
28 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 std::time::Duration;
use futures::{future, Future, executor};
use assert_matches::assert_matches;
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::*;
type VirtualOverseer = test_helpers::TestSubsystemContextHandle<ApprovalDistributionMessage>;
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,
) {
tracing::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,
) {
tracing::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 {
tracing::trace!("Waiting for a message");
let msg = overseer
.recv()
.timeout(TIMEOUT)
.await
.expect("msg recv timeout");
tracing::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: Default::default(),
};
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: Default::default(),
};
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).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: Default::default(),
};
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: Default::default(),
};
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
});
}