mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-13 01:51:23 +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:
@ -19,12 +19,31 @@
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use crate::protocol::{GossipsubMessage, MessageId};
|
||||
use libp2p_core::PeerId;
|
||||
use std::borrow::Cow;
|
||||
use std::time::Duration;
|
||||
|
||||
/// If the `no_source_id` flag is set, the IDENTITY_SOURCE value is used as the source of the
|
||||
/// packet.
|
||||
pub const IDENTITY_SOURCE: [u8; 3] = [0, 1, 0];
|
||||
/// The types of message validation that can be employed by gossipsub.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ValidationMode {
|
||||
/// This is the default setting. This requires the message author to be a valid `PeerId` and to
|
||||
/// be present as well as the sequence number. All messages must have valid signatures.
|
||||
///
|
||||
/// NOTE: This setting will reject messages from nodes using `PrivacyMode::Anonymous` and
|
||||
/// all messages that do not have signatures.
|
||||
Strict,
|
||||
/// This setting permits messages that have no author, sequence number or signature. If any of
|
||||
/// these fields exist in the message these are validated.
|
||||
Permissive,
|
||||
/// This setting requires the author, sequence number and signature fields of a message to be
|
||||
/// empty. Any message that contains these fields is considered invalid.
|
||||
Anonymous,
|
||||
/// This setting does not check the author, sequence number or signature fields of incoming
|
||||
/// messages. If these fields contain data, they are simply ignored.
|
||||
///
|
||||
/// NOTE: This setting will consider messages with invalid signatures as valid messages.
|
||||
None,
|
||||
}
|
||||
|
||||
/// Configuration parameters that define the performance of the gossipsub network.
|
||||
#[derive(Clone)]
|
||||
@ -42,7 +61,7 @@ pub struct GossipsubConfig {
|
||||
/// Target number of peers for the mesh network (D in the spec, default is 6).
|
||||
pub mesh_n: usize,
|
||||
|
||||
/// Minimum number of peers in mesh network before adding more (D_lo in the spec, default is 4).
|
||||
/// Minimum number of peers in mesh network before adding more (D_lo in the spec, default is 5).
|
||||
pub mesh_n_low: usize,
|
||||
|
||||
/// Maximum number of peers in mesh network before removing some (D_high in the spec, default
|
||||
@ -64,17 +83,26 @@ pub struct GossipsubConfig {
|
||||
/// The maximum byte size for each gossip (default is 2048 bytes).
|
||||
pub max_transmit_size: usize,
|
||||
|
||||
/// Duplicates are prevented by storing message id's of known messages in an LRU time cache.
|
||||
/// This settings sets the time period that messages are stored in the cache. Duplicates can be
|
||||
/// received if duplicate messages are sent at a time greater than this setting apart. The
|
||||
/// default is 1 minute.
|
||||
pub duplicate_cache_time: Duration,
|
||||
|
||||
/// Flag determining if gossipsub topics are hashed or sent as plain strings (default is false).
|
||||
pub hash_topics: bool,
|
||||
|
||||
/// When set, all published messages will have a 0 source `PeerId` (default is false).
|
||||
pub no_source_id: bool,
|
||||
|
||||
/// When set to `true`, prevents automatic forwarding of all received messages. This setting
|
||||
/// allows a user to validate the messages before propagating them to their peers. If set to
|
||||
/// true, the user must manually call `propagate_message()` on the behaviour to forward message
|
||||
/// once validated (default is false).
|
||||
pub manual_propagation: bool,
|
||||
/// true, the user must manually call `validate_message()` on the behaviour to forward message
|
||||
/// once validated (default is `false`). Furthermore, the application may optionally call
|
||||
/// `invalidate_message()` on the behaviour to remove the message from the memcache. The
|
||||
/// default is false.
|
||||
pub validate_messages: bool,
|
||||
|
||||
/// Determines the level of validation used when receiving messages. See [`ValidationMode`]
|
||||
/// for the available types. The default is ValidationMode::Strict.
|
||||
pub validation_mode: ValidationMode,
|
||||
|
||||
/// A user-defined function allowing the user to specify the message id of a gossipsub message.
|
||||
/// The default value is to concatenate the source peer id with a sequence number. Setting this
|
||||
@ -94,26 +122,35 @@ impl Default for GossipsubConfig {
|
||||
history_length: 5,
|
||||
history_gossip: 3,
|
||||
mesh_n: 6,
|
||||
mesh_n_low: 4,
|
||||
mesh_n_low: 5,
|
||||
mesh_n_high: 12,
|
||||
gossip_lazy: 6, // default to mesh_n
|
||||
heartbeat_initial_delay: Duration::from_secs(5),
|
||||
heartbeat_interval: Duration::from_secs(1),
|
||||
fanout_ttl: Duration::from_secs(60),
|
||||
max_transmit_size: 2048,
|
||||
duplicate_cache_time: Duration::from_secs(60),
|
||||
hash_topics: false, // default compatibility with floodsub
|
||||
no_source_id: false,
|
||||
manual_propagation: false,
|
||||
validate_messages: false,
|
||||
validation_mode: ValidationMode::Strict,
|
||||
message_id_fn: |message| {
|
||||
// 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)
|
||||
// NOTE: If either the peer_id or source is not provided, we set to 0;
|
||||
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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The builder struct for constructing a gossipsub configuration.
|
||||
pub struct GossipsubConfigBuilder {
|
||||
config: GossipsubConfig,
|
||||
}
|
||||
@ -129,14 +166,18 @@ impl Default for GossipsubConfigBuilder {
|
||||
impl GossipsubConfigBuilder {
|
||||
// set default values
|
||||
pub fn new() -> GossipsubConfigBuilder {
|
||||
GossipsubConfigBuilder::default()
|
||||
GossipsubConfigBuilder {
|
||||
config: GossipsubConfig::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// The protocol id to negotiate this protocol (default is `/meshsub/1.0.0`).
|
||||
pub fn protocol_id(&mut self, protocol_id: impl Into<Cow<'static, [u8]>>) -> &mut Self {
|
||||
self.config.protocol_id = protocol_id.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of heartbeats to keep in the `memcache` (default is 5).
|
||||
pub fn history_length(&mut self, history_length: usize) -> &mut Self {
|
||||
assert!(
|
||||
history_length >= self.config.history_gossip,
|
||||
@ -146,6 +187,7 @@ impl GossipsubConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of past heartbeats to gossip about (default is 3).
|
||||
pub fn history_gossip(&mut self, history_gossip: usize) -> &mut Self {
|
||||
assert!(
|
||||
self.config.history_length >= history_gossip,
|
||||
@ -155,6 +197,7 @@ impl GossipsubConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Target number of peers for the mesh network (D in the spec, default is 6).
|
||||
pub fn mesh_n(&mut self, mesh_n: usize) -> &mut Self {
|
||||
assert!(
|
||||
self.config.mesh_n_low <= mesh_n && mesh_n <= self.config.mesh_n_high,
|
||||
@ -164,6 +207,7 @@ impl GossipsubConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Minimum number of peers in mesh network before adding more (D_lo in the spec, default is 4).
|
||||
pub fn mesh_n_low(&mut self, mesh_n_low: usize) -> &mut Self {
|
||||
assert!(
|
||||
mesh_n_low <= self.config.mesh_n && self.config.mesh_n <= self.config.mesh_n_high,
|
||||
@ -173,6 +217,8 @@ impl GossipsubConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Maximum number of peers in mesh network before removing some (D_high in the spec, default
|
||||
/// is 12).
|
||||
pub fn mesh_n_high(&mut self, mesh_n_high: usize) -> &mut Self {
|
||||
assert!(
|
||||
self.config.mesh_n_low <= self.config.mesh_n && self.config.mesh_n <= mesh_n_high,
|
||||
@ -182,48 +228,81 @@ impl GossipsubConfigBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Number of peers to emit gossip to during a heartbeat (D_lazy in the spec, default is 6).
|
||||
pub fn gossip_lazy(&mut self, gossip_lazy: usize) -> &mut Self {
|
||||
self.config.gossip_lazy = gossip_lazy;
|
||||
self
|
||||
}
|
||||
|
||||
/// Initial delay in each heartbeat (default is 5 seconds).
|
||||
pub fn heartbeat_initial_delay(&mut self, heartbeat_initial_delay: Duration) -> &mut Self {
|
||||
self.config.heartbeat_initial_delay = heartbeat_initial_delay;
|
||||
self
|
||||
}
|
||||
|
||||
/// Time between each heartbeat (default is 1 second).
|
||||
pub fn heartbeat_interval(&mut self, heartbeat_interval: Duration) -> &mut Self {
|
||||
self.config.heartbeat_interval = heartbeat_interval;
|
||||
self
|
||||
}
|
||||
|
||||
/// Time to live for fanout peers (default is 60 seconds).
|
||||
pub fn fanout_ttl(&mut self, fanout_ttl: Duration) -> &mut Self {
|
||||
self.config.fanout_ttl = fanout_ttl;
|
||||
self
|
||||
}
|
||||
|
||||
/// The maximum byte size for each gossip (default is 2048 bytes).
|
||||
pub fn max_transmit_size(&mut self, max_transmit_size: usize) -> &mut Self {
|
||||
self.config.max_transmit_size = max_transmit_size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Duplicates are prevented by storing message id's of known messages in an LRU time cache.
|
||||
/// This settings sets the time period that messages are stored in the cache. Duplicates can be
|
||||
/// received if duplicate messages are sent at a time greater than this setting apart. The
|
||||
/// default is 1 minute.
|
||||
pub fn duplicate_cache_time(&mut self, cache_size: Duration) -> &mut Self {
|
||||
self.config.duplicate_cache_time = cache_size;
|
||||
self
|
||||
}
|
||||
|
||||
/// When set, gossipsub topics are hashed instead of being sent as plain strings.
|
||||
pub fn hash_topics(&mut self) -> &mut Self {
|
||||
self.config.hash_topics = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn no_source_id(&mut self) -> &mut Self {
|
||||
self.config.no_source_id = true;
|
||||
/// When set, prevents automatic forwarding of all received messages. This setting
|
||||
/// allows a user to validate the messages before propagating them to their peers. If set,
|
||||
/// the user must manually call `validate_message()` on the behaviour to forward a message
|
||||
/// once validated.
|
||||
pub fn validate_messages(&mut self) -> &mut Self {
|
||||
self.config.validate_messages = true;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn manual_propagation(&mut self) -> &mut Self {
|
||||
self.config.manual_propagation = true;
|
||||
/// Determines the level of validation used when receiving messages. See [`ValidationMode`]
|
||||
/// for the available types. The default is ValidationMode::Strict.
|
||||
pub fn validation_mode(&mut self, validation_mode: ValidationMode) -> &mut Self {
|
||||
self.config.validation_mode = validation_mode;
|
||||
self
|
||||
}
|
||||
|
||||
/// A user-defined function allowing the user to specify the message id of a gossipsub message.
|
||||
/// The default value is to concatenate the source peer id with a sequence number. Setting this
|
||||
/// parameter allows the user to address packets arbitrarily. One example is content based
|
||||
/// addressing, where this function may be set to `hash(message)`. This would prevent messages
|
||||
/// of the same content from being duplicated.
|
||||
///
|
||||
/// The function takes a `GossipsubMessage` as input and outputs a String to be interpreted as
|
||||
/// the message id.
|
||||
pub fn message_id_fn(&mut self, id_fn: fn(&GossipsubMessage) -> MessageId) -> &mut Self {
|
||||
self.config.message_id_fn = id_fn;
|
||||
self
|
||||
}
|
||||
|
||||
/// Constructs a `GossipsubConfig` from the given configuration.
|
||||
pub fn build(&self) -> GossipsubConfig {
|
||||
self.config.clone()
|
||||
}
|
||||
@ -247,9 +326,9 @@ impl std::fmt::Debug for GossipsubConfig {
|
||||
let _ = builder.field("heartbeat_interval", &self.heartbeat_interval);
|
||||
let _ = builder.field("fanout_ttl", &self.fanout_ttl);
|
||||
let _ = builder.field("max_transmit_size", &self.max_transmit_size);
|
||||
let _ = builder.field("duplicate_cache_time", &self.duplicate_cache_time);
|
||||
let _ = builder.field("hash_topics", &self.hash_topics);
|
||||
let _ = builder.field("no_source_id", &self.no_source_id);
|
||||
let _ = builder.field("manual_propagation", &self.manual_propagation);
|
||||
let _ = builder.field("validate_messages", &self.validate_messages);
|
||||
builder.finish()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user