mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-30 02:01:35 +00:00
feat: replace ProtocolName
with AsRef<str>
Previously, a protocol could be any sequence of bytes as long as it started with `/`. Now, we directly parse a protocol as `String` which enforces it to be valid UTF8. To notify users of this change, we delete the `ProtocolName` trait. The new requirement is that users need to provide a type that implements `AsRef<str>`. We also add a `StreamProtocol` newtype in `libp2p-swarm` which provides an easy way for users to ensure their protocol strings are compliant. The newtype enforces that protocol strings start with `/`. `StreamProtocol` also implements `AsRef<str>`, meaning users can directly use it in their upgrades. `multistream-select` by itself only changes marginally with this patch. The only thing we enforce in the type-system is that protocols must implement `AsRef<str>`. Resolves: #2831. Pull-Request: #3746.
This commit is contained in:
@ -22,9 +22,11 @@ use std::borrow::Cow;
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use libp2p_identity::PeerId;
|
||||
use crate::protocol::{ProtocolConfig, ProtocolId, FLOODSUB_PROTOCOL};
|
||||
use crate::types::{FastMessageId, Message, MessageId, PeerKind, RawMessage};
|
||||
|
||||
use crate::types::{FastMessageId, Message, MessageId, RawMessage};
|
||||
use libp2p_identity::PeerId;
|
||||
use libp2p_swarm::StreamProtocol;
|
||||
|
||||
/// The types of message validation that can be employed by gossipsub.
|
||||
#[derive(Debug, Clone)]
|
||||
@ -59,8 +61,7 @@ pub enum Version {
|
||||
/// Configuration parameters that define the performance of the gossipsub network.
|
||||
#[derive(Clone)]
|
||||
pub struct Config {
|
||||
protocol_id: Cow<'static, str>,
|
||||
custom_id_version: Option<Version>,
|
||||
protocol: ProtocolConfig,
|
||||
history_length: usize,
|
||||
history_gossip: usize,
|
||||
mesh_n: usize,
|
||||
@ -73,11 +74,9 @@ pub struct Config {
|
||||
heartbeat_interval: Duration,
|
||||
fanout_ttl: Duration,
|
||||
check_explicit_peers_ticks: u64,
|
||||
max_transmit_size: usize,
|
||||
idle_timeout: Duration,
|
||||
duplicate_cache_time: Duration,
|
||||
validate_messages: bool,
|
||||
validation_mode: ValidationMode,
|
||||
message_id_fn: Arc<dyn Fn(&Message) -> MessageId + Send + Sync + 'static>,
|
||||
fast_message_id_fn: Option<Arc<dyn Fn(&RawMessage) -> FastMessageId + Send + Sync + 'static>>,
|
||||
allow_self_origin: bool,
|
||||
@ -96,27 +95,12 @@ pub struct Config {
|
||||
max_ihave_length: usize,
|
||||
max_ihave_messages: usize,
|
||||
iwant_followup_time: Duration,
|
||||
support_floodsub: bool,
|
||||
published_message_ids_cache_time: Duration,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// All the getters
|
||||
|
||||
/// The protocol id to negotiate this protocol. By default, the resulting protocol id has the form
|
||||
/// `/<prefix>/<supported-versions>`, but can optionally be changed to a literal form by providing some Version as custom_id_version.
|
||||
/// As gossipsub supports version 1.0 and 1.1, there are two suffixes supported for the resulting protocol id.
|
||||
///
|
||||
/// Calling [`ConfigBuilder::protocol_id_prefix`] will set a new prefix and retain the prefix logic.
|
||||
/// Calling [`ConfigBuilder::protocol_id`] will set a custom `protocol_id` and disable the prefix logic.
|
||||
///
|
||||
/// The default prefix is `meshsub`, giving the supported protocol ids: `/meshsub/1.1.0` and `/meshsub/1.0.0`, negotiated in that order.
|
||||
pub fn protocol_id(&self) -> &Cow<'static, str> {
|
||||
&self.protocol_id
|
||||
}
|
||||
|
||||
pub fn custom_id_version(&self) -> &Option<Version> {
|
||||
&self.custom_id_version
|
||||
pub(crate) fn protocol_config(&self) -> ProtocolConfig {
|
||||
self.protocol.clone()
|
||||
}
|
||||
|
||||
// Overlay network parameters.
|
||||
@ -196,7 +180,7 @@ impl Config {
|
||||
/// must be large enough to transmit the desired peer information on pruning. It must be at
|
||||
/// least 100 bytes. Default is 65536 bytes.
|
||||
pub fn max_transmit_size(&self) -> usize {
|
||||
self.max_transmit_size
|
||||
self.protocol.max_transmit_size
|
||||
}
|
||||
|
||||
/// The time a connection is maintained to a peer without being in the mesh and without
|
||||
@ -226,7 +210,7 @@ impl Config {
|
||||
/// Determines the level of validation used when receiving messages. See [`ValidationMode`]
|
||||
/// for the available types. The default is ValidationMode::Strict.
|
||||
pub fn validation_mode(&self) -> &ValidationMode {
|
||||
&self.validation_mode
|
||||
&self.protocol.validation_mode
|
||||
}
|
||||
|
||||
/// A user-defined function allowing the user to specify the message id of a gossipsub message.
|
||||
@ -381,7 +365,7 @@ impl Config {
|
||||
|
||||
/// Enable support for flooodsub peers. Default false.
|
||||
pub fn support_floodsub(&self) -> bool {
|
||||
self.support_floodsub
|
||||
self.protocol.protocol_ids.contains(&FLOODSUB_PROTOCOL)
|
||||
}
|
||||
|
||||
/// Published message ids time cache duration. The default is 10 seconds.
|
||||
@ -402,14 +386,14 @@ impl Default for Config {
|
||||
/// The builder struct for constructing a gossipsub configuration.
|
||||
pub struct ConfigBuilder {
|
||||
config: Config,
|
||||
invalid_protocol: bool, // This is a bit of a hack to only expose one error to the user.
|
||||
}
|
||||
|
||||
impl Default for ConfigBuilder {
|
||||
fn default() -> Self {
|
||||
ConfigBuilder {
|
||||
config: Config {
|
||||
protocol_id: Cow::Borrowed("meshsub"),
|
||||
custom_id_version: None,
|
||||
protocol: ProtocolConfig::default(),
|
||||
history_length: 5,
|
||||
history_gossip: 3,
|
||||
mesh_n: 6,
|
||||
@ -422,11 +406,9 @@ impl Default for ConfigBuilder {
|
||||
heartbeat_interval: Duration::from_secs(1),
|
||||
fanout_ttl: Duration::from_secs(60),
|
||||
check_explicit_peers_ticks: 300,
|
||||
max_transmit_size: 65536,
|
||||
idle_timeout: Duration::from_secs(120),
|
||||
duplicate_cache_time: Duration::from_secs(60),
|
||||
validate_messages: false,
|
||||
validation_mode: ValidationMode::Strict,
|
||||
message_id_fn: Arc::new(|message| {
|
||||
// default message id is: source + sequence number
|
||||
// NOTE: If either the peer_id or source is not provided, we set to 0;
|
||||
@ -458,27 +440,51 @@ impl Default for ConfigBuilder {
|
||||
max_ihave_length: 5000,
|
||||
max_ihave_messages: 10,
|
||||
iwant_followup_time: Duration::from_secs(3),
|
||||
support_floodsub: false,
|
||||
published_message_ids_cache_time: Duration::from_secs(10),
|
||||
},
|
||||
invalid_protocol: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Config> for ConfigBuilder {
|
||||
fn from(config: Config) -> Self {
|
||||
ConfigBuilder { config }
|
||||
ConfigBuilder {
|
||||
config,
|
||||
invalid_protocol: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigBuilder {
|
||||
/// The protocol id prefix to negotiate this protocol (default is `/meshsub/1.0.0`).
|
||||
/// The protocol id prefix to negotiate this protocol (default is `/meshsub/1.1.0` and `/meshsub/1.0.0`).
|
||||
pub fn protocol_id_prefix(
|
||||
&mut self,
|
||||
protocol_id_prefix: impl Into<Cow<'static, str>>,
|
||||
) -> &mut Self {
|
||||
self.config.custom_id_version = None;
|
||||
self.config.protocol_id = protocol_id_prefix.into();
|
||||
let cow = protocol_id_prefix.into();
|
||||
|
||||
match (
|
||||
StreamProtocol::try_from_owned(format!("{}/1.1.0", cow)),
|
||||
StreamProtocol::try_from_owned(format!("{}/1.0.0", cow)),
|
||||
) {
|
||||
(Ok(p1), Ok(p2)) => {
|
||||
self.config.protocol.protocol_ids = vec![
|
||||
ProtocolId {
|
||||
protocol: p1,
|
||||
kind: PeerKind::Gossipsubv1_1,
|
||||
},
|
||||
ProtocolId {
|
||||
protocol: p2,
|
||||
kind: PeerKind::Gossipsub,
|
||||
},
|
||||
]
|
||||
}
|
||||
_ => {
|
||||
self.invalid_protocol = true;
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@ -488,8 +494,23 @@ impl ConfigBuilder {
|
||||
protocol_id: impl Into<Cow<'static, str>>,
|
||||
custom_id_version: Version,
|
||||
) -> &mut Self {
|
||||
self.config.custom_id_version = Some(custom_id_version);
|
||||
self.config.protocol_id = protocol_id.into();
|
||||
let cow = protocol_id.into();
|
||||
|
||||
match StreamProtocol::try_from_owned(cow.to_string()) {
|
||||
Ok(protocol) => {
|
||||
self.config.protocol.protocol_ids = vec![ProtocolId {
|
||||
protocol,
|
||||
kind: match custom_id_version {
|
||||
Version::V1_1 => PeerKind::Gossipsubv1_1,
|
||||
Version::V1_0 => PeerKind::Gossipsub,
|
||||
},
|
||||
}]
|
||||
}
|
||||
_ => {
|
||||
self.invalid_protocol = true;
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
@ -576,7 +597,7 @@ impl ConfigBuilder {
|
||||
|
||||
/// 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.config.protocol.max_transmit_size = max_transmit_size;
|
||||
self
|
||||
}
|
||||
|
||||
@ -609,7 +630,7 @@ impl ConfigBuilder {
|
||||
/// 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.config.protocol.validation_mode = validation_mode;
|
||||
self
|
||||
}
|
||||
|
||||
@ -787,7 +808,16 @@ impl ConfigBuilder {
|
||||
|
||||
/// Enable support for flooodsub peers.
|
||||
pub fn support_floodsub(&mut self) -> &mut Self {
|
||||
self.config.support_floodsub = true;
|
||||
if self
|
||||
.config
|
||||
.protocol
|
||||
.protocol_ids
|
||||
.contains(&FLOODSUB_PROTOCOL)
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
self.config.protocol.protocol_ids.push(FLOODSUB_PROTOCOL);
|
||||
self
|
||||
}
|
||||
|
||||
@ -804,7 +834,7 @@ impl ConfigBuilder {
|
||||
pub fn build(&self) -> Result<Config, &'static str> {
|
||||
// check all constraints on config
|
||||
|
||||
if self.config.max_transmit_size < 100 {
|
||||
if self.config.protocol.max_transmit_size < 100 {
|
||||
return Err("The maximum transmission size must be greater than 100 to permit basic control messages");
|
||||
}
|
||||
|
||||
@ -833,6 +863,10 @@ impl ConfigBuilder {
|
||||
return Err("The unsubscribe_backoff parameter should be positive.");
|
||||
}
|
||||
|
||||
if self.invalid_protocol {
|
||||
return Err("The provided protocol is invalid, it must start with a forward-slash");
|
||||
}
|
||||
|
||||
Ok(self.config.clone())
|
||||
}
|
||||
}
|
||||
@ -840,8 +874,7 @@ impl ConfigBuilder {
|
||||
impl std::fmt::Debug for Config {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let mut builder = f.debug_struct("GossipsubConfig");
|
||||
let _ = builder.field("protocol_id", &self.protocol_id);
|
||||
let _ = builder.field("custom_id_version", &self.custom_id_version);
|
||||
let _ = builder.field("protocol", &self.protocol);
|
||||
let _ = builder.field("history_length", &self.history_length);
|
||||
let _ = builder.field("history_gossip", &self.history_gossip);
|
||||
let _ = builder.field("mesh_n", &self.mesh_n);
|
||||
@ -853,11 +886,9 @@ impl std::fmt::Debug for Config {
|
||||
let _ = builder.field("heartbeat_initial_delay", &self.heartbeat_initial_delay);
|
||||
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("idle_timeout", &self.idle_timeout);
|
||||
let _ = builder.field("duplicate_cache_time", &self.duplicate_cache_time);
|
||||
let _ = builder.field("validate_messages", &self.validate_messages);
|
||||
let _ = builder.field("validation_mode", &self.validation_mode);
|
||||
let _ = builder.field("allow_self_origin", &self.allow_self_origin);
|
||||
let _ = builder.field("do_px", &self.do_px);
|
||||
let _ = builder.field("prune_peers", &self.prune_peers);
|
||||
@ -872,7 +903,6 @@ impl std::fmt::Debug for Config {
|
||||
let _ = builder.field("max_ihave_length", &self.max_ihave_length);
|
||||
let _ = builder.field("max_ihave_messages", &self.max_ihave_messages);
|
||||
let _ = builder.field("iwant_followup_time", &self.iwant_followup_time);
|
||||
let _ = builder.field("support_floodsub", &self.support_floodsub);
|
||||
let _ = builder.field(
|
||||
"published_message_ids_cache_time",
|
||||
&self.published_message_ids_cache_time,
|
||||
@ -884,11 +914,11 @@ impl std::fmt::Debug for Config {
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use crate::protocol::ProtocolConfig;
|
||||
use crate::topic::IdentityHash;
|
||||
use crate::types::PeerKind;
|
||||
use crate::Topic;
|
||||
use libp2p_core::UpgradeInfo;
|
||||
use libp2p_swarm::StreamProtocol;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
@ -944,38 +974,42 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn create_config_with_protocol_id_prefix() {
|
||||
let protocol_config = ProtocolConfig::new(
|
||||
&ConfigBuilder::default()
|
||||
.protocol_id_prefix("purple")
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
let protocol_config = ConfigBuilder::default()
|
||||
.protocol_id_prefix("/purple")
|
||||
.build()
|
||||
.unwrap()
|
||||
.protocol_config();
|
||||
|
||||
let protocol_ids = protocol_config.protocol_info();
|
||||
|
||||
assert_eq!(protocol_ids.len(), 2);
|
||||
|
||||
assert_eq!(protocol_ids[0].protocol_id, b"/purple/1.1.0".to_vec());
|
||||
assert_eq!(
|
||||
protocol_ids[0].protocol,
|
||||
StreamProtocol::new("/purple/1.1.0")
|
||||
);
|
||||
assert_eq!(protocol_ids[0].kind, PeerKind::Gossipsubv1_1);
|
||||
|
||||
assert_eq!(protocol_ids[1].protocol_id, b"/purple/1.0.0".to_vec());
|
||||
assert_eq!(
|
||||
protocol_ids[1].protocol,
|
||||
StreamProtocol::new("/purple/1.0.0")
|
||||
);
|
||||
assert_eq!(protocol_ids[1].kind, PeerKind::Gossipsub);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_config_with_custom_protocol_id() {
|
||||
let protocol_config = ProtocolConfig::new(
|
||||
&ConfigBuilder::default()
|
||||
.protocol_id("purple", Version::V1_0)
|
||||
.build()
|
||||
.unwrap(),
|
||||
);
|
||||
let protocol_config = ConfigBuilder::default()
|
||||
.protocol_id("/purple", Version::V1_0)
|
||||
.build()
|
||||
.unwrap()
|
||||
.protocol_config();
|
||||
|
||||
let protocol_ids = protocol_config.protocol_info();
|
||||
|
||||
assert_eq!(protocol_ids.len(), 1);
|
||||
|
||||
assert_eq!(protocol_ids[0].protocol_id, b"purple".to_vec());
|
||||
assert_eq!(protocol_ids[0].protocol, "/purple");
|
||||
assert_eq!(protocol_ids[0].kind, PeerKind::Gossipsub);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user