use super::*;
use crate::types::RawGossipsubMessage;
use crate::{GossipsubMessage, IdentTopic as Topic};
fn within_variance(value: f64, expected: f64, variance: f64) -> bool {
if expected >= 0.0 {
return value > expected * (1.0 - variance) && value < expected * (1.0 + variance);
}
return value > expected * (1.0 + variance) && value < expected * (1.0 - variance);
}
fn make_test_message(seq: u64) -> (MessageId, RawGossipsubMessage) {
let raw_message = RawGossipsubMessage {
source: Some(PeerId::random()),
data: vec![12, 34, 56],
sequence_number: Some(seq),
topic: Topic::new("test").hash(),
signature: None,
key: None,
validated: true,
};
let message = GossipsubMessage {
source: raw_message.source.clone(),
data: raw_message.data.clone(),
sequence_number: raw_message.sequence_number,
topic: raw_message.topic.clone(),
};
let id = default_message_id()(&message);
(id, raw_message)
}
fn default_message_id() -> fn(&GossipsubMessage) -> MessageId {
|message| {
let mut source_string = if let Some(peer_id) = message.source.as_ref() {
peer_id.to_base58()
} else {
PeerId::from_bytes(&vec![0, 1, 0])
.expect("Valid peer id")
.to_base58()
};
source_string.push_str(&message.sequence_number.unwrap_or_default().to_string());
MessageId::from(source_string)
}
}
#[test]
fn test_score_time_in_mesh() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
params.topic_score_cap = 1000.0;
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 0.5;
topic_params.time_in_mesh_weight = 1.0;
topic_params.time_in_mesh_quantum = Duration::from_millis(1);
topic_params.time_in_mesh_cap = 3600.0;
params.topics.insert(topic_hash, topic_params.clone());
let peer_id = PeerId::random();
let mut peer_score = PeerScore::new(params);
peer_score.add_peer(peer_id.clone());
let score = peer_score.score(&peer_id);
assert!(
score == 0.0,
"expected score to start at zero. Score found: {}",
score
);
peer_score.graft(&peer_id, topic);
let elapsed = topic_params.time_in_mesh_quantum * 200;
std::thread::sleep(elapsed);
peer_score.refresh_scores();
let score = peer_score.score(&peer_id);
let expected = topic_params.topic_weight
* topic_params.time_in_mesh_weight
* (elapsed.as_millis() / topic_params.time_in_mesh_quantum.as_millis()) as f64;
assert!(
score >= expected,
"The score: {} should be greater than or equal to: {}",
score,
expected
);
}
#[test]
fn test_score_time_in_mesh_cap() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 0.5;
topic_params.time_in_mesh_weight = 1.0;
topic_params.time_in_mesh_quantum = Duration::from_millis(1);
topic_params.time_in_mesh_cap = 10.0;
params.topics.insert(topic_hash, topic_params.clone());
let peer_id = PeerId::random();
let mut peer_score = PeerScore::new(params);
peer_score.add_peer(peer_id.clone());
let score = peer_score.score(&peer_id);
assert!(
score == 0.0,
"expected score to start at zero. Score found: {}",
score
);
peer_score.graft(&peer_id, topic);
let elapsed = topic_params.time_in_mesh_quantum * 40;
std::thread::sleep(elapsed);
peer_score.refresh_scores();
let score = peer_score.score(&peer_id);
let expected = topic_params.topic_weight
* topic_params.time_in_mesh_weight
* topic_params.time_in_mesh_cap;
let variance = 0.5;
assert!(
within_variance(score, expected, variance),
"The score: {} should be within {} of {}",
score,
score * variance,
expected
);
}
#[test]
fn test_score_first_message_deliveries() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.first_message_deliveries_weight = 1.0;
topic_params.first_message_deliveries_decay = 1.0;
topic_params.first_message_deliveries_cap = 2000.0;
topic_params.time_in_mesh_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let peer_id = PeerId::random();
let mut peer_score = PeerScore::new(params);
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic);
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id, &id, &msg.topic);
peer_score.deliver_message(&peer_id, &id, &msg.topic);
}
peer_score.refresh_scores();
let score = peer_score.score(&peer_id);
let expected =
topic_params.topic_weight * topic_params.first_message_deliveries_weight * messages as f64;
assert!(
score == expected,
"The score: {} should be {}",
score,
expected
);
}
#[test]
fn test_score_first_message_deliveries_cap() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.first_message_deliveries_weight = 1.0;
topic_params.first_message_deliveries_decay = 1.0; topic_params.first_message_deliveries_cap = 50.0;
topic_params.time_in_mesh_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let peer_id = PeerId::random();
let mut peer_score = PeerScore::new(params);
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic);
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id, &id, &msg.topic);
peer_score.deliver_message(&peer_id, &id, &msg.topic);
}
peer_score.refresh_scores();
let score = peer_score.score(&peer_id);
let expected = topic_params.topic_weight
* topic_params.first_message_deliveries_weight
* topic_params.first_message_deliveries_cap;
assert!(
score == expected,
"The score: {} should be {}",
score,
expected
);
}
#[test]
fn test_score_first_message_deliveries_decay() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.first_message_deliveries_weight = 1.0;
topic_params.first_message_deliveries_decay = 0.9; topic_params.first_message_deliveries_cap = 2000.0;
topic_params.time_in_mesh_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let peer_id = PeerId::random();
let mut peer_score = PeerScore::new(params);
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic);
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id, &id, &msg.topic);
peer_score.deliver_message(&peer_id, &id, &msg.topic);
}
peer_score.refresh_scores();
let score = peer_score.score(&peer_id);
let mut expected = topic_params.topic_weight
* topic_params.first_message_deliveries_weight
* topic_params.first_message_deliveries_decay
* messages as f64;
assert!(
score == expected,
"The score: {} should be {}",
score,
expected
);
let decay_intervals = 10;
for _ in 0..decay_intervals {
peer_score.refresh_scores();
expected *= topic_params.first_message_deliveries_decay;
}
let score = peer_score.score(&peer_id);
assert!(
score == expected,
"The score: {} should be {}",
score,
expected
);
}
#[test]
fn test_score_mesh_message_deliveries() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = -1.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(1);
topic_params.mesh_message_deliveries_window = Duration::from_millis(10);
topic_params.mesh_message_deliveries_threshold = 20.0;
topic_params.mesh_message_deliveries_cap = 100.0;
topic_params.mesh_message_deliveries_decay = 1.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
let peer_id_b = PeerId::random();
let peer_id_c = PeerId::random();
let peers = vec![peer_id_a.clone(), peer_id_b.clone(), peer_id_c.clone()];
for peer_id in &peers {
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic.clone());
}
peer_score.refresh_scores();
for peer_id in &peers {
let score = peer_score.score(peer_id);
assert!(
score >= 0.0,
"expected no mesh delivery penalty before activation time, got score {}",
score
);
}
std::thread::sleep(topic_params.mesh_message_deliveries_activation);
let messages = 100;
let mut messages_to_send = Vec::new();
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.deliver_message(&peer_id_a, &id, &msg.topic);
peer_score.duplicated_message(&peer_id_b, &id, &msg.topic);
messages_to_send.push((id, msg));
}
std::thread::sleep(topic_params.mesh_message_deliveries_window + Duration::from_millis(20));
for (id, msg) in messages_to_send {
peer_score.duplicated_message(&peer_id_c, &id, &msg.topic);
}
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
let score_c = peer_score.score(&peer_id_c);
assert!(
score_a >= 0.0,
"expected non-negative score for Peer A, got score {}",
score_a
);
assert!(
score_b >= 0.0,
"expected non-negative score for Peer B, got score {}",
score_b
);
let penalty = topic_params.mesh_message_deliveries_threshold
* topic_params.mesh_message_deliveries_threshold;
let expected =
topic_params.topic_weight * topic_params.mesh_message_deliveries_weight * penalty;
assert!(
score_c == expected,
"Score: {}. Expected {}",
score_c,
expected
);
}
#[test]
fn test_score_mesh_message_deliveries_decay() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = -1.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(0);
topic_params.mesh_message_deliveries_window = Duration::from_millis(10);
topic_params.mesh_message_deliveries_threshold = 20.0;
topic_params.mesh_message_deliveries_cap = 100.0;
topic_params.mesh_message_deliveries_decay = 0.9;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.mesh_failure_penalty_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_peer(peer_id_a.clone());
peer_score.graft(&peer_id_a, topic.clone());
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.deliver_message(&peer_id_a, &id, &msg.topic);
}
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert!(
score_a >= 0.0,
"expected non-negative score for Peer A, got score {}",
score_a
);
let mut decayed_delivery_count = (messages as f64) * topic_params.mesh_message_deliveries_decay;
for _ in 0..20 {
peer_score.refresh_scores();
decayed_delivery_count *= topic_params.mesh_message_deliveries_decay;
}
let score_a = peer_score.score(&peer_id_a);
let deficit = topic_params.mesh_message_deliveries_threshold - decayed_delivery_count;
let penalty = deficit * deficit;
let expected =
topic_params.topic_weight * topic_params.mesh_message_deliveries_weight * penalty;
assert_eq!(score_a, expected, "Invalid score");
}
#[test]
fn test_score_mesh_failure_penalty() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(0);
topic_params.mesh_message_deliveries_window = Duration::from_millis(10);
topic_params.mesh_message_deliveries_threshold = 20.0;
topic_params.mesh_message_deliveries_cap = 100.0;
topic_params.mesh_message_deliveries_decay = 1.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.mesh_failure_penalty_weight = -1.0;
topic_params.mesh_failure_penalty_decay = 1.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
let peer_id_b = PeerId::random();
let peers = vec![peer_id_a.clone(), peer_id_b.clone()];
for peer_id in &peers {
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic.clone());
}
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.deliver_message(&peer_id_a, &id, &msg.topic);
}
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert!(
score_a >= 0.0,
"expected non-negative score for Peer A, got score {}",
score_a
);
assert!(
score_b >= 0.0,
"expected non-negative score for Peer B, got score {}",
score_b
);
peer_score.prune(&peer_id_b, topic.hash());
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, 0.0, "expected Peer A to have a 0");
let penalty = topic_params.mesh_message_deliveries_threshold
* topic_params.mesh_message_deliveries_threshold;
let expected = topic_params.topic_weight * topic_params.mesh_failure_penalty_weight * penalty;
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_b, expected, "Peer B should have expected score",);
}
#[test]
fn test_score_invalid_message_deliveries() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(1);
topic_params.mesh_message_deliveries_window = Duration::from_millis(10);
topic_params.mesh_message_deliveries_threshold = 20.0;
topic_params.mesh_message_deliveries_cap = 100.0;
topic_params.mesh_message_deliveries_decay = 1.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.invalid_message_deliveries_weight = -1.0;
topic_params.invalid_message_deliveries_decay = 1.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_peer(peer_id_a.clone());
peer_score.graft(&peer_id_a, topic.clone());
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationFailed);
}
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let expected = topic_params.topic_weight
* topic_params.invalid_message_deliveries_weight
* (messages * messages) as f64;
assert_eq!(score_a, expected, "Peer has unexpected score",);
}
#[test]
fn test_score_invalid_message_deliveris_decay() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(1);
topic_params.mesh_message_deliveries_window = Duration::from_millis(10);
topic_params.mesh_message_deliveries_threshold = 20.0;
topic_params.mesh_message_deliveries_cap = 100.0;
topic_params.mesh_message_deliveries_decay = 1.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.invalid_message_deliveries_weight = -1.0;
topic_params.invalid_message_deliveries_decay = 0.9;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_peer(peer_id_a.clone());
peer_score.graft(&peer_id_a, topic.clone());
let messages = 100;
for seq in 0..messages {
let (id, msg) = make_test_message(seq);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationFailed);
}
peer_score.refresh_scores();
let decay = topic_params.invalid_message_deliveries_decay * messages as f64;
let mut expected =
topic_params.topic_weight * topic_params.invalid_message_deliveries_weight * decay * decay;
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, expected, "Peer has unexpected score");
for _ in 0..10 {
peer_score.refresh_scores();
expected *= topic_params.invalid_message_deliveries_decay
* topic_params.invalid_message_deliveries_decay;
}
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, expected, "Peer has unexpected score");
}
#[test]
fn test_score_reject_message_deliveries() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.invalid_message_deliveries_weight = -1.0;
topic_params.invalid_message_deliveries_decay = 1.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
let peer_id_b = PeerId::random();
let peers = vec![peer_id_a.clone(), peer_id_b.clone()];
for peer_id in &peers {
peer_score.add_peer(peer_id.clone());
}
let (id, msg) = make_test_message(1);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::BlackListedPeer);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::BlackListedSource);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationIgnored);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_a, 0.0, "Should have no effect on the score");
assert_eq!(score_b, 0.0, "Should have no effect on the score");
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationIgnored);
peer_score.duplicated_message(&peer_id_b, &id, &msg.topic);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_a, 0.0, "Should have no effect on the score");
assert_eq!(score_b, 0.0, "Should have no effect on the score");
peer_score.deliveries.clear();
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationIgnored);
peer_score.duplicated_message(&peer_id_b, &id, &msg.topic);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_a, 0.0, "Should have no effect on the score");
assert_eq!(score_b, 0.0, "Should have no effect on the score");
peer_score.deliveries.clear();
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationFailed);
peer_score.duplicated_message(&peer_id_b, &id, &msg.topic);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_a, -1.0, "Score should be effected");
assert_eq!(score_b, -1.0, "Score should be effected");
peer_score.deliveries.clear();
peer_score.validate_message(&peer_id_a, &id, &msg.topic);
peer_score.duplicated_message(&peer_id_b, &id, &msg.topic);
peer_score.reject_message(&peer_id_a, &id, &msg.topic, RejectReason::ValidationFailed);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
assert_eq!(score_a, -4.0, "Score should be effected");
assert_eq!(score_b, -4.0, "Score should be effected");
}
#[test]
fn test_application_score() {
let app_specific_weight = 0.5;
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
params.app_specific_weight = app_specific_weight;
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.invalid_message_deliveries_weight = 0.0;
topic_params.invalid_message_deliveries_decay = 1.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_peer(peer_id_a.clone());
peer_score.graft(&peer_id_a, topic.clone());
let messages = 100;
for i in -100..messages {
let app_score_value = i as f64;
peer_score.set_application_score(&peer_id_a, app_score_value);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let expected = (i as f64) * app_specific_weight;
assert_eq!(score_a, expected, "Peer has unexpected score");
}
}
#[test]
fn test_score_ip_colocation() {
let ip_colocation_factor_weight = -1.0;
let ip_colocation_factor_threshold = 1.0;
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
params.ip_colocation_factor_weight = ip_colocation_factor_weight;
params.ip_colocation_factor_threshold = ip_colocation_factor_threshold;
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.invalid_message_deliveries_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
let peer_id_b = PeerId::random();
let peer_id_c = PeerId::random();
let peer_id_d = PeerId::random();
let peers = vec![
peer_id_a.clone(),
peer_id_b.clone(),
peer_id_c.clone(),
peer_id_d.clone(),
];
for peer_id in &peers {
peer_score.add_peer(peer_id.clone());
peer_score.graft(&peer_id, topic.clone());
}
peer_score.add_ip(&peer_id_a, "1.2.3.4".parse().unwrap());
peer_score.add_ip(&peer_id_b, "2.3.4.5".parse().unwrap());
peer_score.add_ip(&peer_id_c, "2.3.4.5".parse().unwrap());
peer_score.add_ip(&peer_id_c, "3.4.5.6".parse().unwrap());
peer_score.add_ip(&peer_id_d, "2.3.4.5".parse().unwrap());
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
let score_b = peer_score.score(&peer_id_b);
let score_c = peer_score.score(&peer_id_c);
let score_d = peer_score.score(&peer_id_d);
assert_eq!(score_a, 0.0, "Peer A should be unaffected");
let n_shared = 3.0;
let ip_surplus = n_shared - ip_colocation_factor_threshold;
let penalty = ip_surplus * ip_surplus;
let expected = ip_colocation_factor_weight * penalty as f64;
assert_eq!(score_b, expected, "Peer B should have expected score");
assert_eq!(score_c, expected, "Peer C should have expected score");
assert_eq!(score_d, expected, "Peer D should have expected score");
}
#[test]
fn test_score_behaviour_penality() {
let behaviour_penalty_weight = -1.0;
let behaviour_penalty_decay = 0.99;
let topic = Topic::new("test");
let topic_hash = topic.hash();
let mut params = PeerScoreParams::default();
params.behaviour_penalty_decay = behaviour_penalty_decay;
params.behaviour_penalty_weight = behaviour_penalty_weight;
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 1.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.first_message_deliveries_weight = 0.0;
topic_params.mesh_failure_penalty_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
topic_params.time_in_mesh_quantum = Duration::from_secs(1);
topic_params.invalid_message_deliveries_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_penalty(&peer_id_a, 1);
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, 0.0, "Peer A should be unaffected");
peer_score.add_peer(peer_id_a.clone());
assert_eq!(score_a, 0.0, "Peer A should be unaffected");
peer_score.add_penalty(&peer_id_a, 1);
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, -1.0, "Peer A should have been penalized");
peer_score.add_penalty(&peer_id_a, 1);
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, -4.0, "Peer A should have been penalized");
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert_eq!(score_a, -3.9204, "Peer A should have been penalized");
}
#[test]
fn test_score_retention() {
let topic = Topic::new("test");
let topic_hash = topic.hash();
let app_specific_weight = 1.0;
let app_score_value = -1000.0;
let retain_score = Duration::from_secs(1);
let mut params = PeerScoreParams::default();
params.app_specific_weight = app_specific_weight;
params.retain_score = retain_score;
let mut topic_params = TopicScoreParams::default();
topic_params.topic_weight = 0.0;
topic_params.mesh_message_deliveries_weight = 0.0;
topic_params.mesh_message_deliveries_activation = Duration::from_secs(0);
topic_params.first_message_deliveries_weight = 0.0;
topic_params.time_in_mesh_weight = 0.0;
params.topics.insert(topic_hash, topic_params.clone());
let mut peer_score = PeerScore::new(params);
let peer_id_a = PeerId::random();
peer_score.add_peer(peer_id_a.clone());
peer_score.graft(&peer_id_a, topic.clone());
peer_score.set_application_score(&peer_id_a, app_score_value);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert_eq!(
score_a, app_score_value,
"Score should be the application specific score"
);
peer_score.remove_peer(&peer_id_a);
std::thread::sleep(retain_score / 2);
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert_eq!(
score_a, app_score_value,
"Score should be the application specific score"
);
std::thread::sleep(retain_score / 2 + Duration::from_millis(50));
peer_score.refresh_scores();
let score_a = peer_score.score(&peer_id_a);
assert_eq!(
score_a, 0.0,
"Score should be the application specific score"
);
}