Skip to content

Commit 5d93c0a

Browse files
authored
Gossipsub v1.1 various improvements (libp2p#49)
* remove dbg! calls and add debug logging for peer scoring * export MessageAcceptance and rename validate_message to report_message_validation_result to also signal that this message should get called in case of invalid messages * fix double reject_message call * gossip promises are fulfilled already on receiving the message without validation * derive debug for MessageAcceptance * add helper method to get config builder from existing config * allow adding/changing TopicScoreParams during runtime * more debug output for messages from self * fixes incompatibility with anonymous PeerId in lighthouse * cargo fmt * more debug output for broken promises
1 parent 83280db commit 5d93c0a

File tree

6 files changed

+84
-61
lines changed

6 files changed

+84
-61
lines changed

protocols/gossipsub/src/behaviour.rs

Lines changed: 36 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@ use crate::handler::{GossipsubHandler, HandlerEvent};
5555
use crate::mcache::MessageCache;
5656
use crate::peer_score::{PeerScore, PeerScoreParams, PeerScoreThresholds, RejectReason};
5757
use crate::protocol::SIGNING_PREFIX;
58-
use crate::rpc_proto;
5958
use crate::time_cache::DuplicateCache;
6059
use crate::topic::{Hasher, Topic, TopicHash};
6160
use crate::types::{
6261
GossipsubControlAction, GossipsubMessage, GossipsubSubscription, GossipsubSubscriptionAction,
6362
MessageId, PeerInfo,
6463
};
6564
use crate::types::{GossipsubRpc, PeerKind};
65+
use crate::{rpc_proto, TopicScoreParams};
6666
use std::cmp::Ordering::Equal;
6767

6868
mod tests;
@@ -344,6 +344,7 @@ impl BackoffStorage {
344344
}
345345
}
346346

347+
#[derive(Debug)]
347348
pub enum MessageAcceptance {
348349
/// The message is considered valid, and it should be delivered and forwarded to the network
349350
Accept,
@@ -686,9 +687,10 @@ impl Gossipsub {
686687
}
687688

688689
/// This function should be called when `config.validate_messages()` is `true` after the
689-
/// message got validated by the caller. Messages are stored in the ['Memcache'] and validation
690-
/// is expected to be fast enough that the messages should still exist in the cache.There are
691-
/// three possible validation outcomes and the outcome is given in acceptance.
690+
/// message got validated by the caller. Messages are stored in the
691+
/// ['Memcache'] and validation is expected to be fast enough that the messages should still
692+
/// exist in the cache. There are three possible validation outcomes and the outcome is given
693+
/// in acceptance.
692694
///
693695
/// If acceptance = Accept the message will get propagated to the network. The
694696
/// `propagation_source` parameter indicates who the message was received by and will not
@@ -704,7 +706,7 @@ impl Gossipsub {
704706
/// in the cache anymore.
705707
///
706708
/// This should only be called once per message.
707-
pub fn validate_message(
709+
pub fn report_message_validation_result(
708710
&mut self,
709711
message_id: &MessageId,
710712
propagation_source: &PeerId,
@@ -730,14 +732,7 @@ impl Gossipsub {
730732
};
731733

732734
if let Some(message) = self.mcache.remove(message_id) {
733-
//tell peer_score and gossip promises about reject
734-
Self::reject_message(
735-
&mut self.peer_score,
736-
propagation_source,
737-
&message,
738-
message_id,
739-
reject_reason,
740-
);
735+
//tell peer_score about reject
741736
if let Some((peer_score, ..)) = &mut self.peer_score {
742737
peer_score.reject_message(propagation_source, &message, reject_reason);
743738
}
@@ -782,6 +777,12 @@ impl Gossipsub {
782777
Ok(())
783778
}
784779

780+
pub fn set_topic_params(&mut self, topic_hash: TopicHash, params: TopicScoreParams) {
781+
if let Some((peer_score, ..)) = &mut self.peer_score {
782+
peer_score.set_topic_params(topic_hash, params);
783+
}
784+
}
785+
785786
/// Sets the application specific score for a peer. Returns true if scoring is active and
786787
/// the peer is connected or if the score of the peer is not yet expired, false otherwise.
787788
pub fn set_application_score(&mut self, peer_id: &PeerId, new_score: f64) -> bool {
@@ -1315,20 +1316,6 @@ impl Gossipsub {
13151316
}
13161317
}
13171318

1318-
/// informs peer score and gossip_promises about a rejected message
1319-
fn reject_message(
1320-
peer_score: &mut Option<(PeerScore, PeerScoreThresholds, Interval, GossipPromises)>,
1321-
from: &PeerId,
1322-
msg: &GossipsubMessage,
1323-
id: &MessageId,
1324-
reason: RejectReason,
1325-
) {
1326-
if let Some((peer_score, _, _, gossip_promises)) = peer_score {
1327-
peer_score.reject_message(from, &msg, reason);
1328-
gossip_promises.reject_message(id, &reason);
1329-
}
1330-
}
1331-
13321319
/// Handles a newly received GossipsubMessage.
13331320
/// Forwards the message to all peers in the mesh.
13341321
fn handle_received_message(&mut self, mut msg: GossipsubMessage, propagation_source: &PeerId) {
@@ -1348,18 +1335,21 @@ impl Gossipsub {
13481335

13491336
// reject messages claiming to be from ourselves but not locally published
13501337
if let Some(own_id) = self.publish_config.get_own_id() {
1351-
if own_id != propagation_source && msg.source.as_ref().map_or(false, |s| s == own_id) {
1338+
//TODO remove this "hack" as soon as lighthouse uses Anonymous instead of this fixed
1339+
// PeerId.
1340+
let lighthouse_anonymous_id = PeerId::from_bytes(vec![0, 1, 0]).expect("Valid peer id");
1341+
if own_id != &lighthouse_anonymous_id
1342+
&& own_id != propagation_source
1343+
&& msg.source.as_ref().map_or(false, |s| s == own_id)
1344+
{
13521345
debug!(
1353-
"Dropping message claiming to be from self but forwarded from {:?}",
1354-
propagation_source
1355-
);
1356-
Self::reject_message(
1357-
&mut self.peer_score,
1358-
propagation_source,
1359-
&msg,
1360-
&msg_id,
1361-
RejectReason::SelfOrigin,
1346+
"Dropping message {:?} claiming to be from self but forwarded from {:?}",
1347+
msg_id, propagation_source
13621348
);
1349+
if let Some((peer_score, _, _, gossip_promises)) = &mut self.peer_score {
1350+
peer_score.reject_message(propagation_source, &msg, RejectReason::SelfOrigin);
1351+
gossip_promises.reject_message(&msg_id, &RejectReason::SelfOrigin);
1352+
}
13631353
return;
13641354
}
13651355
}
@@ -1374,8 +1364,10 @@ impl Gossipsub {
13741364
}
13751365

13761366
//tells score that message arrived (but is maybe not fully validated yet)
1377-
if let Some((peer_score, ..)) = &mut self.peer_score {
1367+
//Consider message as delivered for gossip promises
1368+
if let Some((peer_score, .., gossip_promises)) = &mut self.peer_score {
13781369
peer_score.validate_message(propagation_source, &msg);
1370+
gossip_promises.deliver_message(&msg_id);
13791371
}
13801372

13811373
self.mcache.put(msg.clone());
@@ -1885,6 +1877,7 @@ impl Gossipsub {
18851877
self.mcache.shift();
18861878

18871879
debug!("Completed Heartbeat");
1880+
debug!("peer_scores: {:?}", scores);
18881881
}
18891882

18901883
/// Emits gossip - Send IHAVE messages to a random set of gossip peers. This is applied to mesh
@@ -2032,12 +2025,11 @@ impl Gossipsub {
20322025
fn forward_msg(&mut self, message: GossipsubMessage, source: Option<&PeerId>) -> bool {
20332026
let msg_id = (self.config.message_id_fn())(&message);
20342027

2035-
//message is fully validated, inform peer_score and gossip promises
2028+
//message is fully validated inform peer_score
20362029
if let Some((peer_score, _, _, gossip_promises)) = &mut self.peer_score {
20372030
if let Some(peer) = source {
20382031
peer_score.deliver_message(peer, &message);
20392032
}
2040-
gossip_promises.deliver_message(&msg_id);
20412033
}
20422034

20432035
debug!("Forwarding message: {:?}", msg_id);
@@ -2058,7 +2050,7 @@ impl Gossipsub {
20582050
//add explicit peers
20592051
for p in &self.explicit_peers {
20602052
if let Some(topics) = self.peer_topics.get(p) {
2061-
if message.topics.iter().any(|t| topics.contains(t)) {
2053+
if Some(p) != source && message.topics.iter().any(|t| topics.contains(t)) {
20622054
recipient_peers.insert(p.clone());
20632055
}
20642056
}
@@ -2475,10 +2467,12 @@ impl NetworkBehaviour for Gossipsub {
24752467
invalid_messages,
24762468
} => {
24772469
// Handle any invalid messages from this peer
2478-
if let Some((peer_score, ..)) = &mut self.peer_score {
2470+
if let Some((peer_score, .., gossip_promises)) = &mut self.peer_score {
2471+
let mut id_fn = self.config.message_id_fn();
24792472
for (_message, validation_error) in invalid_messages {
24802473
let reason = RejectReason::ProtocolValidationError(validation_error);
24812474
peer_score.reject_message(&propagation_source, &_message, reason);
2475+
gossip_promises.reject_message(&id_fn(&_message), &reason);
24822476
}
24832477
}
24842478

protocols/gossipsub/src/behaviour/tests.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3186,7 +3186,7 @@ mod tests {
31863186
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
31873187

31883188
//message m1 gets validated
3189-
gs.validate_message(&id(&m1), &peers[0], MessageAcceptance::Accept);
3189+
gs.report_message_validation_result(&id(&m1), &peers[0], MessageAcceptance::Accept);
31903190

31913191
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
31923192
}
@@ -3348,7 +3348,7 @@ mod tests {
33483348
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
33493349

33503350
//message m1 gets ignored
3351-
gs.validate_message(
3351+
gs.report_message_validation_result(
33523352
&(config.message_id_fn())(&m1),
33533353
&peers[0],
33543354
MessageAcceptance::Ignore,
@@ -3404,7 +3404,7 @@ mod tests {
34043404
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
34053405

34063406
//message m1 gets rejected
3407-
gs.validate_message(
3407+
gs.report_message_validation_result(
34083408
&(config.message_id_fn())(&m1),
34093409
&peers[0],
34103410
MessageAcceptance::Reject,
@@ -3467,7 +3467,7 @@ mod tests {
34673467
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[1]), 0.0);
34683468

34693469
//message m1 gets rejected
3470-
gs.validate_message(
3470+
gs.report_message_validation_result(
34713471
&(config.message_id_fn())(&m1),
34723472
&peers[0],
34733473
MessageAcceptance::Reject,
@@ -3534,17 +3534,17 @@ mod tests {
35343534
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
35353535

35363536
//messages gets rejected
3537-
gs.validate_message(
3537+
gs.report_message_validation_result(
35383538
&(config.message_id_fn())(&m1),
35393539
&peers[0],
35403540
MessageAcceptance::Reject,
35413541
);
3542-
gs.validate_message(
3542+
gs.report_message_validation_result(
35433543
&(config.message_id_fn())(&m2),
35443544
&peers[0],
35453545
MessageAcceptance::Reject,
35463546
);
3547-
gs.validate_message(
3547+
gs.report_message_validation_result(
35483548
&(config.message_id_fn())(&m3),
35493549
&peers[0],
35503550
MessageAcceptance::Reject,
@@ -3604,7 +3604,7 @@ mod tests {
36043604
assert_eq!(gs.peer_score.as_ref().unwrap().0.score(&peers[0]), 0.0);
36053605

36063606
//message m1 gets rejected
3607-
gs.validate_message(
3607+
gs.report_message_validation_result(
36083608
&(config.message_id_fn())(&m1),
36093609
&peers[0],
36103610
MessageAcceptance::Reject,

protocols/gossipsub/src/config.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,12 @@ impl Default for GossipsubConfigBuilder {
457457
}
458458
}
459459

460+
impl From<GossipsubConfig> for GossipsubConfigBuilder {
461+
fn from(config: GossipsubConfig) -> Self {
462+
GossipsubConfigBuilder { config }
463+
}
464+
}
465+
460466
impl GossipsubConfigBuilder {
461467
// set default values
462468
pub fn new() -> GossipsubConfigBuilder {

protocols/gossipsub/src/gossip_promises.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::error::ValidationError;
22
use crate::peer_score::RejectReason;
33
use crate::MessageId;
44
use libp2p_core::PeerId;
5+
use log::debug;
56
use rand::seq::SliceRandom;
67
use rand::thread_rng;
78
use std::collections::HashMap;
@@ -56,11 +57,15 @@ impl GossipPromises {
5657
pub fn get_broken_promises(&mut self) -> HashMap<PeerId, usize> {
5758
let now = Instant::now();
5859
let mut result = HashMap::new();
59-
self.promises.retain(|_, peers| {
60+
self.promises.retain(|msg, peers| {
6061
peers.retain(|peer_id, expires| {
6162
if *expires < now {
6263
let count = result.entry(peer_id.clone()).or_insert(0);
6364
*count += 1;
65+
debug!(
66+
"The peer {} broke the promise to deliver message {} in time!",
67+
peer_id, msg
68+
);
6469
false
6570
} else {
6671
true

protocols/gossipsub/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ mod rpc_proto {
152152
include!(concat!(env!("OUT_DIR"), "/gossipsub.pb.rs"));
153153
}
154154

155-
pub use self::behaviour::{Gossipsub, GossipsubEvent, MessageAuthenticity};
155+
pub use self::behaviour::{Gossipsub, GossipsubEvent, MessageAcceptance, MessageAuthenticity};
156156
pub use self::config::{GossipsubConfig, GossipsubConfigBuilder, ValidationMode};
157157
pub use self::peer_score::{
158158
score_parameter_decay, score_parameter_decay_with_base, PeerScoreParams, PeerScoreThresholds,

0 commit comments

Comments
 (0)