Skip to content

Commit 06e4281

Browse files
AgeManningisaac.asimov
authored andcommitted
Maintain trusted peers (sigp#4159)
## Issue Addressed sigp#4150 ## Proposed Changes Maintain trusted peers in the pruning logic. ~~In principle the changes here are not necessary as a trusted peer has a max score (100) and all other peers can have at most 0 (because we don't implement positive scores). This means that we should never prune trusted peers unless we have more trusted peers than the target peer count.~~ This change shifts this logic to explicitly never prune trusted peers which I expect is the intuitive behaviour. ~~I suspect the issue in sigp#4150 arises when a trusted peer disconnects from us for one reason or another and then we remove that peer from our peerdb as it becomes stale. When it re-connects at some large time later, it is no longer a trusted peer.~~ Currently we do disconnect trusted peers, and this PR corrects this to maintain trusted peers in the pruning logic. As suggested in sigp#4150 we maintain trusted peers in the db and thus we remember them even if they disconnect from us.
1 parent 53b5157 commit 06e4281

File tree

6 files changed

+113
-22
lines changed

6 files changed

+113
-22
lines changed

beacon_node/lighthouse_network/src/peer_manager/mod.rs

Lines changed: 102 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,10 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
940940
/// MIN_SYNC_COMMITTEE_PEERS
941941
/// number should be set low as an absolute lower bound to maintain peers on the sync
942942
/// committees.
943+
/// - Do not prune trusted peers. NOTE: This means if a user has more trusted peers than the
944+
/// excess peer limit, all of the following logic is subverted as we will not prune any peers.
945+
/// Also, the more trusted peers a user has, the less room Lighthouse has to efficiently manage
946+
/// its peers across the subnets.
943947
///
944948
/// Prune peers in the following order:
945949
/// 1. Remove worst scoring peers
@@ -970,7 +974,9 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
970974
.read()
971975
.worst_connected_peers()
972976
.iter()
973-
.filter(|(_, info)| !info.has_future_duty() && $filter(*info))
977+
.filter(|(_, info)| {
978+
!info.has_future_duty() && !info.is_trusted() && $filter(*info)
979+
})
974980
{
975981
if peers_to_prune.len()
976982
>= connected_peer_count.saturating_sub(self.target_peers)
@@ -1020,8 +1026,8 @@ impl<TSpec: EthSpec> PeerManager<TSpec> {
10201026
> = HashMap::new();
10211027

10221028
for (peer_id, info) in self.network_globals.peers.read().connected_peers() {
1023-
// Ignore peers we are already pruning
1024-
if peers_to_prune.contains(peer_id) {
1029+
// Ignore peers we trust or that we are already pruning
1030+
if info.is_trusted() || peers_to_prune.contains(peer_id) {
10251031
continue;
10261032
}
10271033

@@ -1318,25 +1324,47 @@ mod tests {
13181324
..Default::default()
13191325
};
13201326
let log = build_log(slog::Level::Debug, false);
1321-
let globals = NetworkGlobals::new_test_globals(&log);
1327+
let globals = NetworkGlobals::new_test_globals(vec![], &log);
1328+
PeerManager::new(config, Arc::new(globals), &log).unwrap()
1329+
}
1330+
1331+
async fn build_peer_manager_with_trusted_peers(
1332+
trusted_peers: Vec<PeerId>,
1333+
target_peer_count: usize,
1334+
) -> PeerManager<E> {
1335+
let config = config::Config {
1336+
target_peer_count,
1337+
discovery_enabled: false,
1338+
..Default::default()
1339+
};
1340+
let log = build_log(slog::Level::Debug, false);
1341+
let globals = NetworkGlobals::new_test_globals(trusted_peers, &log);
13221342
PeerManager::new(config, Arc::new(globals), &log).unwrap()
13231343
}
13241344

13251345
#[tokio::test]
13261346
async fn test_peer_manager_disconnects_correctly_during_heartbeat() {
1327-
let mut peer_manager = build_peer_manager(3).await;
1328-
1329-
// Create 5 peers to connect to.
1347+
// Create 6 peers to connect to with a target of 3.
13301348
// 2 will be outbound-only, and have the lowest score.
1349+
// 1 will be a trusted peer.
1350+
// The other 3 will be ingoing peers.
1351+
1352+
// We expect this test to disconnect from 3 peers. 1 from the outbound peer (the other must
1353+
// remain due to the outbound peer limit) and 2 from the ingoing peers (the trusted peer
1354+
// should remain connected).
13311355
let peer0 = PeerId::random();
13321356
let peer1 = PeerId::random();
13331357
let peer2 = PeerId::random();
13341358
let outbound_only_peer1 = PeerId::random();
13351359
let outbound_only_peer2 = PeerId::random();
1360+
let trusted_peer = PeerId::random();
1361+
1362+
let mut peer_manager = build_peer_manager_with_trusted_peers(vec![trusted_peer], 3).await;
13361363

13371364
peer_manager.inject_connect_ingoing(&peer0, "/ip4/0.0.0.0".parse().unwrap(), None);
13381365
peer_manager.inject_connect_ingoing(&peer1, "/ip4/0.0.0.0".parse().unwrap(), None);
13391366
peer_manager.inject_connect_ingoing(&peer2, "/ip4/0.0.0.0".parse().unwrap(), None);
1367+
peer_manager.inject_connect_ingoing(&trusted_peer, "/ip4/0.0.0.0".parse().unwrap(), None);
13401368
peer_manager.inject_connect_outgoing(
13411369
&outbound_only_peer1,
13421370
"/ip4/0.0.0.0".parse().unwrap(),
@@ -1366,7 +1394,7 @@ mod tests {
13661394
.add_to_score(-2.0);
13671395

13681396
// Check initial connected peers.
1369-
assert_eq!(peer_manager.network_globals.connected_or_dialing_peers(), 5);
1397+
assert_eq!(peer_manager.network_globals.connected_or_dialing_peers(), 6);
13701398

13711399
peer_manager.heartbeat();
13721400

@@ -1385,8 +1413,22 @@ mod tests {
13851413
.read()
13861414
.is_connected(&outbound_only_peer2));
13871415

1416+
// The trusted peer remains connected
1417+
assert!(peer_manager
1418+
.network_globals
1419+
.peers
1420+
.read()
1421+
.is_connected(&trusted_peer));
1422+
13881423
peer_manager.heartbeat();
13891424

1425+
// The trusted peer remains connected, even after subsequent heartbeats.
1426+
assert!(peer_manager
1427+
.network_globals
1428+
.peers
1429+
.read()
1430+
.is_connected(&trusted_peer));
1431+
13901432
// Check that if we are at target number of peers, we do not disconnect any.
13911433
assert_eq!(peer_manager.network_globals.connected_or_dialing_peers(), 3);
13921434
}
@@ -2131,7 +2173,7 @@ mod tests {
21312173
#[cfg(test)]
21322174
mod property_based_tests {
21332175
use crate::peer_manager::config::DEFAULT_TARGET_PEERS;
2134-
use crate::peer_manager::tests::build_peer_manager;
2176+
use crate::peer_manager::tests::build_peer_manager_with_trusted_peers;
21352177
use crate::rpc::MetaData;
21362178
use libp2p::PeerId;
21372179
use quickcheck::{Arbitrary, Gen, TestResult};
@@ -2142,10 +2184,12 @@ mod tests {
21422184

21432185
#[derive(Clone, Debug)]
21442186
struct PeerCondition {
2187+
peer_id: PeerId,
21452188
outgoing: bool,
21462189
attestation_net_bitfield: Vec<bool>,
21472190
sync_committee_net_bitfield: Vec<bool>,
21482191
score: f64,
2192+
trusted: bool,
21492193
gossipsub_score: f64,
21502194
}
21512195

@@ -2170,10 +2214,12 @@ mod tests {
21702214
};
21712215

21722216
PeerCondition {
2217+
peer_id: PeerId::random(),
21732218
outgoing: bool::arbitrary(g),
21742219
attestation_net_bitfield,
21752220
sync_committee_net_bitfield,
21762221
score: f64::arbitrary(g),
2222+
trusted: bool::arbitrary(g),
21772223
gossipsub_score: f64::arbitrary(g),
21782224
}
21792225
}
@@ -2185,26 +2231,36 @@ mod tests {
21852231
if peer_conditions.len() < target_peer_count {
21862232
return TestResult::discard();
21872233
}
2234+
let trusted_peers: Vec<_> = peer_conditions
2235+
.iter()
2236+
.filter_map(|p| if p.trusted { Some(p.peer_id) } else { None })
2237+
.collect();
2238+
// If we have a high percentage of trusted peers, it is very difficult to reason about
2239+
// the expected results of the pruning.
2240+
if trusted_peers.len() > peer_conditions.len() / 3_usize {
2241+
return TestResult::discard();
2242+
}
21882243
let rt = Runtime::new().unwrap();
21892244

21902245
rt.block_on(async move {
2191-
let mut peer_manager = build_peer_manager(target_peer_count).await;
2246+
// Collect all the trusted peers
2247+
let mut peer_manager =
2248+
build_peer_manager_with_trusted_peers(trusted_peers, target_peer_count).await;
21922249

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

21992255
if condition.outgoing {
22002256
peer_manager.inject_connect_outgoing(
2201-
&peer,
2257+
&condition.peer_id,
22022258
"/ip4/0.0.0.0".parse().unwrap(),
22032259
None,
22042260
);
22052261
} else {
22062262
peer_manager.inject_connect_ingoing(
2207-
&peer,
2263+
&condition.peer_id,
22082264
"/ip4/0.0.0.0".parse().unwrap(),
22092265
None,
22102266
);
@@ -2225,22 +2281,51 @@ mod tests {
22252281
};
22262282

22272283
let mut peer_db = peer_manager.network_globals.peers.write();
2228-
let peer_info = peer_db.peer_info_mut(&peer).unwrap();
2284+
let peer_info = peer_db.peer_info_mut(&condition.peer_id).unwrap();
22292285
peer_info.set_meta_data(MetaData::V2(metadata));
22302286
peer_info.set_gossipsub_score(condition.gossipsub_score);
22312287
peer_info.add_to_score(condition.score);
22322288

22332289
for subnet in peer_info.long_lived_subnets() {
2234-
peer_db.add_subscription(&peer, subnet);
2290+
peer_db.add_subscription(&condition.peer_id, subnet);
22352291
}
22362292
}
22372293

22382294
// Perform the heartbeat.
22392295
peer_manager.heartbeat();
22402296

2241-
TestResult::from_bool(
2297+
// The minimum number of connected peers cannot be less than the target peer count
2298+
// or submitted peers.
2299+
2300+
let expected_peer_count = target_peer_count.min(peer_conditions.len());
2301+
// Trusted peers could make this larger however.
2302+
let no_of_trusted_peers = peer_conditions
2303+
.iter()
2304+
.filter(|condition| condition.trusted)
2305+
.count();
2306+
let expected_peer_count = expected_peer_count.max(no_of_trusted_peers);
2307+
2308+
let target_peer_condition =
22422309
peer_manager.network_globals.connected_or_dialing_peers()
2243-
== target_peer_count.min(peer_conditions.len()),
2310+
== expected_peer_count;
2311+
2312+
// It could be that we reach our target outbound limit and are unable to prune any
2313+
// extra, which violates the target_peer_condition.
2314+
let outbound_peers = peer_manager.network_globals.connected_outbound_only_peers();
2315+
let hit_outbound_limit = outbound_peers == peer_manager.target_outbound_peers();
2316+
2317+
// No trusted peers should be disconnected
2318+
let trusted_peer_disconnected = peer_conditions.iter().any(|condition| {
2319+
condition.trusted
2320+
&& !peer_manager
2321+
.network_globals
2322+
.peers
2323+
.read()
2324+
.is_connected(&condition.peer_id)
2325+
});
2326+
2327+
TestResult::from_bool(
2328+
(target_peer_condition || hit_outbound_limit) && !trusted_peer_disconnected,
22442329
)
22452330
})
22462331
}

beacon_node/lighthouse_network/src/peer_manager/peerdb.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1062,7 +1062,7 @@ impl<TSpec: EthSpec> PeerDB<TSpec> {
10621062
if let Some(to_drop) = self
10631063
.peers
10641064
.iter()
1065-
.filter(|(_, info)| info.is_disconnected())
1065+
.filter(|(_, info)| info.is_disconnected() && !info.is_trusted())
10661066
.filter_map(|(id, info)| match info.connection_status() {
10671067
PeerConnectionStatus::Disconnected { since } => Some((id, since)),
10681068
_ => None,

beacon_node/lighthouse_network/src/types/globals.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,10 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
129129
}
130130

131131
/// TESTING ONLY. Build a dummy NetworkGlobals instance.
132-
pub fn new_test_globals(log: &slog::Logger) -> NetworkGlobals<TSpec> {
132+
pub fn new_test_globals(
133+
trusted_peers: Vec<PeerId>,
134+
log: &slog::Logger,
135+
) -> NetworkGlobals<TSpec> {
133136
use crate::CombinedKeyExt;
134137
let keypair = libp2p::identity::Keypair::generate_secp256k1();
135138
let enr_key: discv5::enr::CombinedKey =
@@ -144,7 +147,7 @@ impl<TSpec: EthSpec> NetworkGlobals<TSpec> {
144147
attnets: Default::default(),
145148
syncnets: Default::default(),
146149
}),
147-
vec![],
150+
trusted_peers,
148151
false,
149152
log,
150153
)

beacon_node/network/src/sync/block_lookups/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ impl TestRig {
5050
};
5151
let bl = BlockLookups::new(log.new(slog::o!("component" => "block_lookups")));
5252
let cx = {
53-
let globals = Arc::new(NetworkGlobals::new_test_globals(&log));
53+
let globals = Arc::new(NetworkGlobals::new_test_globals(Vec::new(), &log));
5454
SyncNetworkContext::new(
5555
network_tx,
5656
globals,

beacon_node/network/src/sync/range_sync/range.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ mod tests {
599599
log.new(o!("component" => "range")),
600600
);
601601
let (network_tx, network_rx) = mpsc::unbounded_channel();
602-
let globals = Arc::new(NetworkGlobals::new_test_globals(&log));
602+
let globals = Arc::new(NetworkGlobals::new_test_globals(Vec::new(), &log));
603603
let cx = SyncNetworkContext::new(
604604
network_tx,
605605
globals.clone(),

beacon_node/src/config.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1044,6 +1044,9 @@ pub fn set_network_config(
10441044
.map_err(|_| format!("Invalid trusted peer id: {}", peer_id))
10451045
})
10461046
.collect::<Result<Vec<PeerIdSerialized>, _>>()?;
1047+
if config.trusted_peers.len() >= config.target_peers {
1048+
slog::warn!(log, "More trusted peers than the target peer limit. This will prevent efficient peer selection criteria."; "target_peers" => config.target_peers, "trusted_peers" => config.trusted_peers.len());
1049+
}
10471050
}
10481051

10491052
if let Some(enr_udp_port_str) = cli_args.value_of("enr-udp-port") {

0 commit comments

Comments
 (0)