mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-29 09:41:34 +00:00
protocols/gossipsub: Add message signing and verification configuration (#1583)
This adds optional message signing and verification to the gossipsub protocol as per the libp2p specifications. In addition this commit: - Removes the LruCache received cache and simply uses the memcache in it's place. - Send subscriptions to all peers - Prevent invalid messages from being gossiped - Send grafts when subscriptions are added to the mesh Co-authored-by: Max Inden <mail@max-inden.de> Co-authored-by: Rüdiger Klaehn <rklaehn@protonmail.com> Co-authored-by: Rüdiger Klaehn <rklaehn@gmail.com>
This commit is contained in:
@ -65,34 +65,22 @@ impl MessageCache {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a `MessageCache` with a default message id function.
|
||||
#[allow(dead_code)]
|
||||
pub fn new_default(gossip: usize, history_capacity: usize) -> MessageCache {
|
||||
let default_id = |message: &GossipsubMessage| {
|
||||
// default message id is: source + sequence number
|
||||
let mut source_string = message.source.to_base58();
|
||||
source_string.push_str(&message.sequence_number.to_string());
|
||||
MessageId(source_string)
|
||||
};
|
||||
MessageCache {
|
||||
gossip,
|
||||
msgs: HashMap::default(),
|
||||
history: vec![Vec::new(); history_capacity],
|
||||
msg_id: default_id,
|
||||
}
|
||||
}
|
||||
|
||||
/// Put a message into the memory cache
|
||||
pub fn put(&mut self, msg: GossipsubMessage) {
|
||||
/// Put a message into the memory cache.
|
||||
///
|
||||
/// Returns the message if it already exists.
|
||||
pub fn put(&mut self, msg: GossipsubMessage) -> Option<GossipsubMessage> {
|
||||
let message_id = (self.msg_id)(&msg);
|
||||
let cache_entry = CacheEntry {
|
||||
mid: message_id.clone(),
|
||||
topics: msg.topics.clone(),
|
||||
};
|
||||
|
||||
self.msgs.insert(message_id, msg);
|
||||
|
||||
self.history[0].push(cache_entry);
|
||||
let seen_message = self.msgs.insert(message_id, msg);
|
||||
if seen_message.is_none() {
|
||||
// Don't add duplicate entries to the cache.
|
||||
self.history[0].push(cache_entry);
|
||||
}
|
||||
seen_message
|
||||
}
|
||||
|
||||
/// Get a message with `message_id`
|
||||
@ -100,6 +88,14 @@ impl MessageCache {
|
||||
self.msgs.get(message_id)
|
||||
}
|
||||
|
||||
/// Gets and validates a message with `message_id`.
|
||||
pub fn validate(&mut self, message_id: &MessageId) -> Option<&GossipsubMessage> {
|
||||
self.msgs.get_mut(message_id).map(|message| {
|
||||
message.validated = true;
|
||||
&*message
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a list of GossipIds for a given topic
|
||||
pub fn get_gossip_ids(&self, topic: &TopicHash) -> Vec<MessageId> {
|
||||
self.history[..self.gossip]
|
||||
@ -110,7 +106,13 @@ impl MessageCache {
|
||||
.iter()
|
||||
.filter_map(|entry| {
|
||||
if entry.topics.iter().any(|t| t == topic) {
|
||||
Some(entry.mid.clone())
|
||||
let mid = &entry.mid;
|
||||
// Only gossip validated messages
|
||||
if let Some(true) = self.msgs.get(mid).map(|msg| msg.validated) {
|
||||
Some(mid.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -143,30 +145,38 @@ mod tests {
|
||||
|
||||
fn gen_testm(x: u64, topics: Vec<TopicHash>) -> GossipsubMessage {
|
||||
let u8x: u8 = x as u8;
|
||||
let source = PeerId::random();
|
||||
let source = Some(PeerId::random());
|
||||
let data: Vec<u8> = vec![u8x];
|
||||
let sequence_number = x;
|
||||
let sequence_number = Some(x);
|
||||
|
||||
let m = GossipsubMessage {
|
||||
source,
|
||||
data,
|
||||
sequence_number,
|
||||
topics,
|
||||
signature: None,
|
||||
key: None,
|
||||
validated: true,
|
||||
};
|
||||
m
|
||||
}
|
||||
|
||||
fn new_cache(gossip_size: usize, history: usize) -> MessageCache {
|
||||
let default_id = |message: &GossipsubMessage| {
|
||||
// default message id is: source + sequence number
|
||||
let mut source_string = message.source.as_ref().unwrap().to_base58();
|
||||
source_string.push_str(&message.sequence_number.unwrap().to_string());
|
||||
MessageId::from(source_string)
|
||||
};
|
||||
|
||||
MessageCache::new(gossip_size, history, default_id)
|
||||
}
|
||||
|
||||
#[test]
|
||||
/// Test that the message cache can be created.
|
||||
fn test_new_cache() {
|
||||
let default_id = |message: &GossipsubMessage| {
|
||||
// default message id is: source + sequence number
|
||||
let mut source_string = message.source.to_base58();
|
||||
source_string.push_str(&message.sequence_number.to_string());
|
||||
MessageId(source_string)
|
||||
};
|
||||
let x: usize = 3;
|
||||
let mc = MessageCache::new(x, 5, default_id);
|
||||
let mc = new_cache(x, 5);
|
||||
|
||||
assert_eq!(mc.gossip, x);
|
||||
}
|
||||
@ -174,7 +184,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test you can put one message and get one.
|
||||
fn test_put_get_one() {
|
||||
let mut mc = MessageCache::new_default(10, 15);
|
||||
let mut mc = new_cache(10, 15);
|
||||
|
||||
let topic1_hash = Topic::new("topic1".into()).no_hash().clone();
|
||||
let topic2_hash = Topic::new("topic2".into()).no_hash().clone();
|
||||
@ -200,7 +210,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test attempting to 'get' with a wrong id.
|
||||
fn test_get_wrong() {
|
||||
let mut mc = MessageCache::new_default(10, 15);
|
||||
let mut mc = new_cache(10, 15);
|
||||
|
||||
let topic1_hash = Topic::new("topic1".into()).no_hash().clone();
|
||||
let topic2_hash = Topic::new("topic2".into()).no_hash().clone();
|
||||
@ -210,7 +220,7 @@ mod tests {
|
||||
mc.put(m.clone());
|
||||
|
||||
// Try to get an incorrect ID
|
||||
let wrong_id = MessageId(String::from("wrongid"));
|
||||
let wrong_id = MessageId::new(b"wrongid");
|
||||
let fetched = mc.get(&wrong_id);
|
||||
assert_eq!(fetched.is_none(), true);
|
||||
}
|
||||
@ -218,10 +228,10 @@ mod tests {
|
||||
#[test]
|
||||
/// Test attempting to 'get' empty message cache.
|
||||
fn test_get_empty() {
|
||||
let mc = MessageCache::new_default(10, 15);
|
||||
let mc = new_cache(10, 15);
|
||||
|
||||
// Try to get an incorrect ID
|
||||
let wrong_string = MessageId(String::from("imempty"));
|
||||
let wrong_string = MessageId::new(b"imempty");
|
||||
let fetched = mc.get(&wrong_string);
|
||||
assert_eq!(fetched.is_none(), true);
|
||||
}
|
||||
@ -229,7 +239,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test adding a message with no topics.
|
||||
fn test_no_topic_put() {
|
||||
let mut mc = MessageCache::new_default(3, 5);
|
||||
let mut mc = new_cache(3, 5);
|
||||
|
||||
// Build the message
|
||||
let m = gen_testm(1, vec![]);
|
||||
@ -247,7 +257,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test shift mechanism.
|
||||
fn test_shift() {
|
||||
let mut mc = MessageCache::new_default(1, 5);
|
||||
let mut mc = new_cache(1, 5);
|
||||
|
||||
let topic1_hash = Topic::new("topic1".into()).no_hash().clone();
|
||||
let topic2_hash = Topic::new("topic2".into()).no_hash().clone();
|
||||
@ -271,7 +281,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test Shift with no additions.
|
||||
fn test_empty_shift() {
|
||||
let mut mc = MessageCache::new_default(1, 5);
|
||||
let mut mc = new_cache(1, 5);
|
||||
|
||||
let topic1_hash = Topic::new("topic1".into()).no_hash().clone();
|
||||
let topic2_hash = Topic::new("topic2".into()).no_hash().clone();
|
||||
@ -297,7 +307,7 @@ mod tests {
|
||||
#[test]
|
||||
/// Test shift to see if the last history messages are removed.
|
||||
fn test_remove_last_from_shift() {
|
||||
let mut mc = MessageCache::new_default(4, 5);
|
||||
let mut mc = new_cache(4, 5);
|
||||
|
||||
let topic1_hash = Topic::new("topic1".into()).no_hash().clone();
|
||||
let topic2_hash = Topic::new("topic2".into()).no_hash().clone();
|
||||
|
Reference in New Issue
Block a user