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:
Age Manning
2020-08-03 18:13:43 +10:00
committed by GitHub
parent d0ad105433
commit d8ad7bddf5
14 changed files with 1358 additions and 568 deletions

View File

@ -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();