mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-19 17:01:02 +00:00
Use grid topology for bitfileds distribution messages (#5389)
* Move NewGossipTopology -> SessionGridTopology outside as this implementation is shared * Add method to return peers difference between topologies * Implement basic grid topology usage for the bitfield distribution * Fix tests * Oops, fix tests * Add some tests for random routing * Add a unit test for topology distribution * Store the current and the previous topology to match sessions boundaries * Update tests * Update node/network/bitfield-distribution/src/lib.rs Co-authored-by: Andronik <write@reusable.software> * Update node/network/protocol/src/grid_topology.rs Co-authored-by: Andronik <write@reusable.software> * Update node/network/bitfield-distribution/src/lib.rs Co-authored-by: Andronik <write@reusable.software> * Add some debug * Fix tests as HashSet order is undefined Co-authored-by: Andronik <write@reusable.software>
This commit is contained in:
@@ -27,6 +27,7 @@ use polkadot_subsystem::{
|
||||
jaeger,
|
||||
jaeger::{PerLeafSpan, Span},
|
||||
};
|
||||
use rand_chacha::ChaCha12Rng;
|
||||
use sp_application_crypto::AppKey;
|
||||
use sp_core::Pair as PairT;
|
||||
use sp_keyring::Sr25519Keyring;
|
||||
@@ -42,6 +43,11 @@ macro_rules! launch {
|
||||
};
|
||||
}
|
||||
|
||||
/// Pre-seeded `crypto` random numbers generator for testing purposes
|
||||
fn dummy_rng() -> ChaCha12Rng {
|
||||
rand_chacha::ChaCha12Rng::seed_from_u64(12345)
|
||||
}
|
||||
|
||||
/// A very limited state, only interested in the relay parent of the
|
||||
/// given message, which must be signed by `validator` and a set of peers
|
||||
/// which are also only interested in that relay parent.
|
||||
@@ -52,6 +58,10 @@ fn prewarmed_state(
|
||||
peers: Vec<PeerId>,
|
||||
) -> ProtocolState {
|
||||
let relay_parent = known_message.relay_parent.clone();
|
||||
let mut topology: SessionGridTopology = Default::default();
|
||||
topology.peers_x = peers.iter().cloned().collect();
|
||||
let mut topologies: BitfieldGridTopologyStorage = Default::default();
|
||||
topologies.update_topology(0_u32, topology);
|
||||
ProtocolState {
|
||||
per_relay_parent: hashmap! {
|
||||
relay_parent.clone() =>
|
||||
@@ -67,7 +77,7 @@ fn prewarmed_state(
|
||||
},
|
||||
},
|
||||
peer_views: peers.iter().cloned().map(|peer| (peer, view!(relay_parent))).collect(),
|
||||
gossip_peers: peers.into_iter().collect(),
|
||||
topologies,
|
||||
view: our_view!(relay_parent),
|
||||
}
|
||||
}
|
||||
@@ -191,6 +201,7 @@ fn receive_invalid_signature() {
|
||||
.unwrap()
|
||||
.validator_set
|
||||
.push(validator_1.into());
|
||||
let mut rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
launch!(handle_network_msg(
|
||||
@@ -198,6 +209,7 @@ fn receive_invalid_signature() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), invalid_msg.into_network_message()),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// reputation doesn't change due to one_job_per_validator check
|
||||
@@ -208,6 +220,7 @@ fn receive_invalid_signature() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), invalid_msg_2.into_network_message()),
|
||||
&mut rng,
|
||||
));
|
||||
// reputation change due to invalid signature
|
||||
assert_matches!(
|
||||
@@ -259,6 +272,7 @@ fn receive_invalid_validator_index() {
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let mut rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
launch!(handle_network_msg(
|
||||
@@ -266,6 +280,7 @@ fn receive_invalid_validator_index() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.into_network_message()),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// reputation change due to invalid validator index
|
||||
@@ -319,6 +334,7 @@ fn receive_duplicate_messages() {
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let mut rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
// send a first message
|
||||
@@ -327,6 +343,7 @@ fn receive_duplicate_messages() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// none of our peers has any interest in any messages
|
||||
@@ -359,6 +376,7 @@ fn receive_duplicate_messages() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_a.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -377,6 +395,7 @@ fn receive_duplicate_messages() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -431,9 +450,13 @@ 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 rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
let gossip_peers = HashSet::from_iter(vec![peer_a.clone(), peer_b.clone()].into_iter());
|
||||
let gossip_peers = SessionGridTopology {
|
||||
peers_x: HashSet::from_iter(vec![peer_a.clone(), peer_b.clone()].into_iter()),
|
||||
..Default::default()
|
||||
};
|
||||
relay_message(
|
||||
&mut ctx,
|
||||
state.per_relay_parent.get_mut(&hash).unwrap(),
|
||||
@@ -441,6 +464,8 @@ fn do_not_relay_message_twice() {
|
||||
&mut state.peer_views,
|
||||
validator.clone(),
|
||||
msg.clone(),
|
||||
RequiredRouting::GridXY,
|
||||
&mut rng,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -475,6 +500,8 @@ fn do_not_relay_message_twice() {
|
||||
&mut state.peer_views,
|
||||
validator.clone(),
|
||||
msg.clone(),
|
||||
RequiredRouting::GridXY,
|
||||
&mut rng,
|
||||
)
|
||||
.await;
|
||||
|
||||
@@ -532,6 +559,7 @@ fn changing_view() {
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let mut rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
launch!(handle_network_msg(
|
||||
@@ -539,6 +567,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerConnected(peer_b.clone(), ObservedRole::Full, 1, None),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// make peer b interested
|
||||
@@ -547,6 +576,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerViewChange(peer_b.clone(), view![hash_a, hash_b]),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert!(state.peer_views.contains_key(&peer_b));
|
||||
@@ -557,6 +587,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// gossip to the overseer
|
||||
@@ -587,6 +618,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerViewChange(peer_b.clone(), view![]),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert!(state.peer_views.contains_key(&peer_b));
|
||||
@@ -599,6 +631,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// reputation change for peer B
|
||||
@@ -617,6 +650,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerDisconnected(peer_b.clone()),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// we are not interested in any peers at all anymore
|
||||
@@ -629,6 +663,7 @@ fn changing_view() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_a.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
// reputation change for peer B
|
||||
@@ -683,6 +718,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 rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
// send a first message
|
||||
@@ -691,6 +727,7 @@ fn do_not_send_message_back_to_origin() {
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peer_b.clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
@@ -727,6 +764,117 @@ fn do_not_send_message_back_to_origin() {
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn topology_test() {
|
||||
let _ = env_logger::builder()
|
||||
.filter(None, log::LevelFilter::Trace)
|
||||
.is_test(true)
|
||||
.try_init();
|
||||
|
||||
let hash: Hash = [0; 32].into();
|
||||
let peers_x = (0..25).map(|_| PeerId::random()).collect::<Vec<_>>();
|
||||
let peers_y = (0..25).map(|_| PeerId::random()).collect::<Vec<_>>();
|
||||
|
||||
// ensure all unique
|
||||
assert_eq!(
|
||||
peers_x.iter().chain(peers_y.iter()).collect::<HashSet<_>>().len(),
|
||||
peers_x.len() + peers_y.len()
|
||||
);
|
||||
|
||||
// validator 0 key pair
|
||||
let (mut state, signing_context, keystore, validator) = state_with_view(our_view![hash], hash);
|
||||
|
||||
// Create a simple grid
|
||||
let mut topology: SessionGridTopology = Default::default();
|
||||
topology.peers_x = peers_x.iter().cloned().collect::<HashSet<_>>();
|
||||
topology.validator_indices_x = peers_x
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, _)| ValidatorIndex(idx as u32))
|
||||
.collect::<HashSet<_>>();
|
||||
topology.peers_y = peers_y.iter().cloned().collect::<HashSet<_>>();
|
||||
topology.validator_indices_y = peers_y
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(idx, _)| ValidatorIndex((idx + peers_x.len()) as u32))
|
||||
.collect::<HashSet<_>>();
|
||||
state.topologies.update_topology(0_u32, topology);
|
||||
|
||||
// create a signed message by validator 0
|
||||
let payload = AvailabilityBitfield(bitvec![u8, bitvec::order::Lsb0; 1u8; 32]);
|
||||
let signed_bitfield = executor::block_on(Signed::<AvailabilityBitfield>::sign(
|
||||
&keystore,
|
||||
payload,
|
||||
&signing_context,
|
||||
ValidatorIndex(0),
|
||||
&validator,
|
||||
))
|
||||
.ok()
|
||||
.flatten()
|
||||
.expect("should be signed");
|
||||
|
||||
peers_x.iter().chain(peers_y.iter()).for_each(|peer| {
|
||||
state.peer_views.insert(peer.clone(), view![hash]);
|
||||
});
|
||||
|
||||
let msg = BitfieldGossipMessage {
|
||||
relay_parent: hash.clone(),
|
||||
signed_availability: signed_bitfield.clone(),
|
||||
};
|
||||
|
||||
let pool = sp_core::testing::TaskExecutor::new();
|
||||
let (mut ctx, mut handle) = make_subsystem_context::<BitfieldDistributionMessage, _>(pool);
|
||||
let mut rng = dummy_rng();
|
||||
|
||||
executor::block_on(async move {
|
||||
// send a first message
|
||||
launch!(handle_network_msg(
|
||||
&mut ctx,
|
||||
&mut state,
|
||||
&Default::default(),
|
||||
NetworkBridgeEvent::PeerMessage(peers_x[0].clone(), msg.clone().into_network_message(),),
|
||||
&mut rng,
|
||||
));
|
||||
|
||||
assert_matches!(
|
||||
handle.recv().await,
|
||||
AllMessages::Provisioner(ProvisionerMessage::ProvisionableData(
|
||||
_,
|
||||
ProvisionableData::Bitfield(hash, signed)
|
||||
)) => {
|
||||
assert_eq!(hash, hash);
|
||||
assert_eq!(signed, signed_bitfield)
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
handle.recv().await,
|
||||
AllMessages::NetworkBridge(
|
||||
NetworkBridgeMessage::SendValidationMessage(peers, send_msg),
|
||||
) => {
|
||||
let topology = state.topologies.get_current_topology();
|
||||
// It should send message to all peers in y direction and to 4 random peers in x direction
|
||||
assert_eq!(peers_y.len() + 4, peers.len());
|
||||
assert!(topology.peers_y.iter().all(|peer| peers.contains(&peer)));
|
||||
assert!(topology.peers_x.iter().filter(|peer| peers.contains(&peer)).count() == 4);
|
||||
// Must never include originator
|
||||
assert!(!peers.contains(&peers_x[0]));
|
||||
assert_eq!(send_msg, msg.clone().into_validation_protocol());
|
||||
}
|
||||
);
|
||||
|
||||
assert_matches!(
|
||||
handle.recv().await,
|
||||
AllMessages::NetworkBridge(
|
||||
NetworkBridgeMessage::ReportPeer(peer, rep)
|
||||
) => {
|
||||
assert_eq!(peer, peers_x[0]);
|
||||
assert_eq!(rep, BENEFIT_VALID_MESSAGE_FIRST)
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn need_message_works() {
|
||||
let validators = vec![Sr25519Keyring::Alice.pair(), Sr25519Keyring::Bob.pair()];
|
||||
|
||||
Reference in New Issue
Block a user