Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion protocols/gossipsub/src/behaviour.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ use crate::{
gossip_promises::GossipPromises,
handler::{Handler, HandlerEvent, HandlerIn},
mcache::MessageCache,
peer_score::{PeerScore, PeerScoreParams, PeerScoreState, PeerScoreThresholds, RejectReason},
peer_score::{
PeerScore, PeerScoreParameters, PeerScoreParams, PeerScoreState, PeerScoreThresholds,
RejectReason,
},
protocol::SIGNING_PREFIX,
queue::Queue,
rpc_proto::proto,
Expand Down Expand Up @@ -509,6 +512,14 @@ where
}
}

/// Returns the detailed gossipsub score parameters for a given peer, if one exists.
pub fn peer_score_params(&self, peer_id: &PeerId) -> Option<PeerScoreParameters> {
match &self.peer_score {
PeerScoreState::Active(peer_score) => Some(peer_score.score_report(peer_id).params),
PeerScoreState::Disabled => None,
}
}

/// Subscribe to a topic.
///
/// Returns [`Ok(true)`] if the subscription worked. Returns [`Ok(false)`] if we were already
Expand Down
95 changes: 69 additions & 26 deletions protocols/gossipsub/src/peer_score.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,28 @@ impl PeerScoreState {
#[derive(Default)]
pub(crate) struct PeerScoreReport {
pub(crate) score: f64,
pub(crate) params: PeerScoreParameters,
#[cfg(feature = "metrics")]
pub(crate) penalties: Vec<crate::metrics::Penalty>,
}

#[derive(Copy, Clone, Default)]
pub struct PeerScoreParameters {
pub final_score: f64,
// weighted contributions per the spec.
// https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.1.md#the-score-function
pub p1: f64,
pub p2: f64,
pub p3: f64,
pub p3b: f64,
pub p4: f64,
pub p5: f64,
pub p6: f64,
pub p7: f64,
// not part of the spec, but useful.
pub slow_peer_penalty: f64,
}

pub(crate) struct PeerScore {
/// The score parameters.
pub(crate) params: PeerScoreParams,
Expand Down Expand Up @@ -279,8 +297,7 @@ impl PeerScore {
if let Some(topic_params) = self.params.topics.get(topic) {
// we are tracking the topic

// the topic score
let mut topic_score = 0.0;
let topic_weight = topic_params.topic_weight;

// P1: time in mesh
if let MeshStatus::Active { mesh_time, .. } = topic_stats.mesh_status {
Expand All @@ -293,19 +310,22 @@ impl PeerScore {
topic_params.time_in_mesh_cap
}
};
topic_score += p1 * topic_params.time_in_mesh_weight;
let p1_contrib = p1 * topic_params.time_in_mesh_weight * topic_weight;
report.params.p1 += p1_contrib;
}

// P2: first message deliveries
let p2 = {
{
let v = topic_stats.first_message_deliveries;
if v < topic_params.first_message_deliveries_cap {
let p2 = if v < topic_params.first_message_deliveries_cap {
v
} else {
topic_params.first_message_deliveries_cap
}
};
topic_score += p2 * topic_params.first_message_deliveries_weight;
};
let p2_contrib =
p2 * topic_params.first_message_deliveries_weight * topic_weight;
report.params.p2 += p2_contrib;
}

// P3: mesh message deliveries
if topic_stats.mesh_message_deliveries_active
Expand All @@ -318,7 +338,9 @@ impl PeerScore {
let p3 = deficit * deficit;
let penalty = p3 * topic_params.mesh_message_deliveries_weight;

topic_score += penalty;
// contribution includes the topic weight.
report.params.p3 += penalty * topic_weight;

#[cfg(feature = "metrics")]
report
.penalties
Expand All @@ -335,29 +357,41 @@ impl PeerScore {
// P3b:
// NOTE: the weight of P3b is negative (validated in TopicScoreParams.validate), so
// this detracts.
let p3b = topic_stats.mesh_failure_penalty;
topic_score += p3b * topic_params.mesh_failure_penalty_weight;
{
let p3b_val = topic_stats.mesh_failure_penalty;
let p3b_contrib =
p3b_val * topic_params.mesh_failure_penalty_weight * topic_weight;
report.params.p3b += p3b_contrib;
}

// P4: invalid messages
// NOTE: the weight of P4 is negative (validated in TopicScoreParams.validate), so
// this detracts.
let p4 =
topic_stats.invalid_message_deliveries * topic_stats.invalid_message_deliveries;
topic_score += p4 * topic_params.invalid_message_deliveries_weight;

// update score, mixing with topic weight
report.score += topic_score * topic_params.topic_weight;
{
let p4_val = topic_stats.invalid_message_deliveries
* topic_stats.invalid_message_deliveries;
let p4_contrib =
p4_val * topic_params.invalid_message_deliveries_weight * topic_weight;
report.params.p4 += p4_contrib;
}
}
}

// apply the topic score cap, if any
if self.params.topic_score_cap > 0f64 && report.score > self.params.topic_score_cap {
report.score = self.params.topic_score_cap;
// aggregate topic contributions.
let mut topic_total = report.params.p1
+ report.params.p2
+ report.params.p3
+ report.params.p3b
+ report.params.p4;

// apply the topic score cap, if any.
if self.params.topic_score_cap > 0f64 && topic_total > self.params.topic_score_cap {
topic_total = self.params.topic_score_cap;
}

// P5: application-specific score
let p5 = peer_stats.application_score;
report.score += p5 * self.params.app_specific_weight;
let p5 = peer_stats.application_score * self.params.app_specific_weight;
report.params.p5 = p5;

// P6: IP collocation factor
for ip in peer_stats.known_ips.iter() {
Expand All @@ -383,24 +417,33 @@ impl PeerScore {
surplus=%surplus,
"[Penalty] The peer gets penalized because of too many peers with the same ip"
);
report.score += p6 * self.params.ip_colocation_factor_weight;
report.params.p6 += p6 * self.params.ip_colocation_factor_weight;
}
}
}

// P7: behavioural pattern penalty.
if peer_stats.behaviour_penalty > self.params.behaviour_penalty_threshold {
let excess = peer_stats.behaviour_penalty - self.params.behaviour_penalty_threshold;
let p7 = excess * excess;
report.score += p7 * self.params.behaviour_penalty_weight;
let p7 = (excess * excess) * self.params.behaviour_penalty_weight;
report.params.p7 = p7;
}

// Slow peer weighting.
if peer_stats.slow_peer_penalty > self.params.slow_peer_threshold {
let excess = peer_stats.slow_peer_penalty - self.params.slow_peer_threshold;
report.score += excess * self.params.slow_peer_weight;
report.params.slow_peer_penalty = excess * self.params.slow_peer_weight;
}

// Final total score with cap applied on topic contributions.
report.score = topic_total
+ report.params.p5
+ report.params.p6
+ report.params.p7
+ report.params.slow_peer_penalty;

report.params.final_score = report.score;

report
}

Expand Down