Skip to content

Commit

Permalink
Test the pruning of excess peers using randomly generated input (#3248)
Browse files Browse the repository at this point in the history
## Issue Addressed

#3092


## Proposed Changes

Added property-based tests for the pruning implementation. A randomly generated input for the test contains connection direction, subnets, and scores.


## Additional Info

I left some comments on this PR, what I have tried, and [a question](#3248 (comment)).

Co-authored-by: Diva M <[email protected]>
  • Loading branch information
ackintosh and divagant-martian committed Jun 25, 2022
1 parent d21f083 commit 082ed35
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 0 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions beacon_node/lighthouse_network/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ slog-async = "2.5.0"
tempfile = "3.1.0"
exit-future = "0.2.0"
void = "1"
quickcheck = "0.9.2"
quickcheck_macros = "0.9.1"

[features]
libp2p-websocket = []
119 changes: 119 additions & 0 deletions beacon_node/lighthouse_network/src/peer_manager/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2048,4 +2048,123 @@ mod tests {
assert!(connected_peers.contains(&peers[6]));
assert!(connected_peers.contains(&peers[7]));
}

// Test properties PeerManager should have using randomly generated input.
#[cfg(test)]
mod property_based_tests {
use crate::peer_manager::config::DEFAULT_TARGET_PEERS;
use crate::peer_manager::tests::build_peer_manager;
use crate::rpc::MetaData;
use libp2p::PeerId;
use quickcheck::{Arbitrary, Gen, TestResult};
use quickcheck_macros::quickcheck;
use tokio::runtime::Runtime;
use types::Unsigned;
use types::{EthSpec, MainnetEthSpec as E};

#[derive(Clone, Debug)]
struct PeerCondition {
outgoing: bool,
attestation_net_bitfield: Vec<bool>,
sync_committee_net_bitfield: Vec<bool>,
score: f64,
gossipsub_score: f64,
}

impl Arbitrary for PeerCondition {
fn arbitrary<G: Gen>(g: &mut G) -> Self {
let attestation_net_bitfield = {
let len = <E as EthSpec>::SubnetBitfieldLength::to_usize();
let mut bitfield = Vec::with_capacity(len);
for _ in 0..len {
bitfield.push(bool::arbitrary(g));
}
bitfield
};

let sync_committee_net_bitfield = {
let len = <E as EthSpec>::SyncCommitteeSubnetCount::to_usize();
let mut bitfield = Vec::with_capacity(len);
for _ in 0..len {
bitfield.push(bool::arbitrary(g));
}
bitfield
};

PeerCondition {
outgoing: bool::arbitrary(g),
attestation_net_bitfield,
sync_committee_net_bitfield,
score: f64::arbitrary(g),
gossipsub_score: f64::arbitrary(g),
}
}
}

#[quickcheck]
fn prune_excess_peers(peer_conditions: Vec<PeerCondition>) -> TestResult {
let target_peer_count = DEFAULT_TARGET_PEERS;
if peer_conditions.len() < target_peer_count {
return TestResult::discard();
}
let rt = Runtime::new().unwrap();

rt.block_on(async move {
let mut peer_manager = build_peer_manager(target_peer_count).await;

// Create peers based on the randomly generated conditions.
for condition in &peer_conditions {
let peer = PeerId::random();
let mut attnets = crate::types::EnrAttestationBitfield::<E>::new();
let mut syncnets = crate::types::EnrSyncCommitteeBitfield::<E>::new();

if condition.outgoing {
peer_manager.inject_connect_outgoing(
&peer,
"/ip4/0.0.0.0".parse().unwrap(),
None,
);
} else {
peer_manager.inject_connect_ingoing(
&peer,
"/ip4/0.0.0.0".parse().unwrap(),
None,
);
}

for (i, value) in condition.attestation_net_bitfield.iter().enumerate() {
attnets.set(i, *value).unwrap();
}

for (i, value) in condition.sync_committee_net_bitfield.iter().enumerate() {
syncnets.set(i, *value).unwrap();
}

let metadata = crate::rpc::MetaDataV2 {
seq_number: 0,
attnets,
syncnets,
};

let mut peer_db = peer_manager.network_globals.peers.write();
let peer_info = peer_db.peer_info_mut(&peer).unwrap();
peer_info.set_meta_data(MetaData::V2(metadata));
peer_info.set_gossipsub_score(condition.gossipsub_score);
peer_info.add_to_score(condition.score);

for subnet in peer_info.long_lived_subnets() {
peer_db.add_subscription(&peer, subnet);
}
}

// Perform the heartbeat.
peer_manager.heartbeat();

TestResult::from_bool(
peer_manager.network_globals.connected_or_dialing_peers()
== target_peer_count.min(peer_conditions.len()),
)
})
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ impl RealScore {
#[cfg(test)]
pub fn set_gossipsub_score(&mut self, score: f64) {
self.gossipsub_score = score;
self.update_state();
}

/// Applies time-based logic such as decay rates to the score.
Expand Down

0 comments on commit 082ed35

Please sign in to comment.