libp2p_gossipsub/config.rs
1// Copyright 2020 Sigma Prime Pty Ltd.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a
4// copy of this software and associated documentation files (the "Software"),
5// to deal in the Software without restriction, including without limitation
6// the rights to use, copy, modify, merge, publish, distribute, sublicense,
7// and/or sell copies of the Software, and to permit persons to whom the
8// Software is furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in
11// all copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
14// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19// DEALINGS IN THE SOFTWARE.
20
21use std::{borrow::Cow, collections::HashMap, sync::Arc, time::Duration};
22
23use libp2p_identity::PeerId;
24use libp2p_swarm::StreamProtocol;
25
26use crate::{
27    error::ConfigBuilderError,
28    protocol::{ProtocolConfig, ProtocolId, FLOODSUB_PROTOCOL},
29    types::{Message, MessageId, PeerKind},
30    TopicHash,
31};
32
33/// The types of message validation that can be employed by gossipsub.
34#[derive(Debug, Clone)]
35pub enum ValidationMode {
36    /// This is the default setting. This requires the message author to be a valid [`PeerId`] and
37    /// to be present as well as the sequence number. All messages must have valid signatures.
38    ///
39    /// NOTE: This setting will reject messages from nodes using
40    /// [`crate::behaviour::MessageAuthenticity::Anonymous`] and all messages that do not have
41    /// signatures.
42    Strict,
43    /// This setting permits messages that have no author, sequence number or signature. If any of
44    /// these fields exist in the message these are validated.
45    Permissive,
46    /// This setting requires the author, sequence number and signature fields of a message to be
47    /// empty. Any message that contains these fields is considered invalid.
48    Anonymous,
49    /// This setting does not check the author, sequence number or signature fields of incoming
50    /// messages. If these fields contain data, they are simply ignored.
51    ///
52    /// NOTE: This setting will consider messages with invalid signatures as valid messages.
53    None,
54}
55
56/// Selector for custom Protocol Id
57#[derive(Clone, Debug, PartialEq, Eq)]
58pub enum Version {
59    V1_0,
60    V1_1,
61}
62
63/// Defines the overall configuration for mesh parameters and max transmit sizes
64/// for topics.
65#[derive(Default, Clone, PartialEq, Eq)]
66pub(crate) struct TopicConfigs {
67    pub(crate) topic_mesh_params: HashMap<TopicHash, TopicMeshConfig>,
68    pub(crate) default_mesh_params: TopicMeshConfig,
69}
70
71/// Defines the mesh network parameters for a given topic.
72#[derive(Clone, Debug, Eq, PartialEq)]
73pub struct TopicMeshConfig {
74    pub mesh_n: usize,
75    pub mesh_n_low: usize,
76    pub mesh_n_high: usize,
77    pub mesh_outbound_min: usize,
78}
79
80impl Default for TopicMeshConfig {
81    fn default() -> Self {
82        Self {
83            mesh_n: 6,
84            mesh_n_low: 5,
85            mesh_n_high: 12,
86            mesh_outbound_min: 2,
87        }
88    }
89}
90
91impl std::fmt::Debug for TopicConfigs {
92    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
93        let mut builder = f.debug_struct("TopicConfigs");
94        let _ = builder.field("topic_mesh_params", &self.topic_mesh_params);
95        builder.finish()
96    }
97}
98
99/// Configuration parameters that define the performance of the gossipsub network.
100#[derive(Clone)]
101pub struct Config {
102    protocol: ProtocolConfig,
103    history_length: usize,
104    history_gossip: usize,
105    retain_scores: usize,
106    gossip_lazy: usize,
107    gossip_factor: f64,
108    heartbeat_initial_delay: Duration,
109    heartbeat_interval: Duration,
110    fanout_ttl: Duration,
111    check_explicit_peers_ticks: u64,
112    duplicate_cache_time: Duration,
113    validate_messages: bool,
114    message_id_fn: Arc<dyn Fn(&Message) -> MessageId + Send + Sync + 'static>,
115    allow_self_origin: bool,
116    do_px: bool,
117    prune_peers: usize,
118    prune_backoff: Duration,
119    unsubscribe_backoff: Duration,
120    backoff_slack: u32,
121    flood_publish: bool,
122    graft_flood_threshold: Duration,
123    opportunistic_graft_ticks: u64,
124    opportunistic_graft_peers: usize,
125    gossip_retransimission: u32,
126    max_messages_per_rpc: Option<usize>,
127    max_ihave_length: usize,
128    max_ihave_messages: usize,
129    iwant_followup_time: Duration,
130    connection_handler_queue_len: usize,
131    connection_handler_publish_duration: Duration,
132    connection_handler_forward_duration: Duration,
133    idontwant_message_size_threshold: usize,
134    idontwant_on_publish: bool,
135    topic_configuration: TopicConfigs,
136}
137
138impl Config {
139    pub(crate) fn protocol_config(&self) -> ProtocolConfig {
140        self.protocol.clone()
141    }
142
143    // Overlay network parameters.
144    /// Number of heartbeats to keep in the `memcache` (default is 5).
145    pub fn history_length(&self) -> usize {
146        self.history_length
147    }
148
149    /// Number of past heartbeats to gossip about (default is 3).
150    pub fn history_gossip(&self) -> usize {
151        self.history_gossip
152    }
153
154    /// Target number of peers for the mesh network (D in the spec, default is 6).
155    pub fn mesh_n(&self) -> usize {
156        self.topic_configuration.default_mesh_params.mesh_n
157    }
158
159    /// Minimum number of peers in mesh network before adding more (D_lo in the spec, default is 5).
160    pub fn mesh_n_low(&self) -> usize {
161        self.topic_configuration.default_mesh_params.mesh_n_low
162    }
163
164    /// Maximum number of peers in mesh network before removing some (D_high in the spec, default
165    /// is 12).
166    pub fn mesh_n_high(&self) -> usize {
167        self.topic_configuration.default_mesh_params.mesh_n_high
168    }
169
170    /// Target number of peers for the mesh network for a given topic (D in the spec, default is 6).
171    pub fn mesh_n_for_topic(&self, topic_hash: &TopicHash) -> usize {
172        self.topic_configuration
173            .topic_mesh_params
174            .get(topic_hash)
175            .unwrap_or(&self.topic_configuration.default_mesh_params)
176            .mesh_n
177    }
178
179    /// Minimum number of peers in mesh network for a given topic before adding more (D_lo in the
180    /// spec, default is 5).
181    pub fn mesh_n_low_for_topic(&self, topic_hash: &TopicHash) -> usize {
182        self.topic_configuration
183            .topic_mesh_params
184            .get(topic_hash)
185            .unwrap_or(&self.topic_configuration.default_mesh_params)
186            .mesh_n_low
187    }
188
189    /// Maximum number of peers in mesh network before removing some (D_high in the spec, default
190    /// is 12).
191    pub fn mesh_n_high_for_topic(&self, topic_hash: &TopicHash) -> usize {
192        self.topic_configuration
193            .topic_mesh_params
194            .get(topic_hash)
195            .unwrap_or(&self.topic_configuration.default_mesh_params)
196            .mesh_n_high
197    }
198
199    /// Affects how peers are selected when pruning a mesh due to over subscription.
200    ///
201    ///  At least `retain_scores` of the retained peers will be high-scoring, while the remainder
202    /// are  chosen randomly (D_score in the spec, default is 4).
203    pub fn retain_scores(&self) -> usize {
204        self.retain_scores
205    }
206
207    /// Minimum number of peers to emit gossip to during a heartbeat (D_lazy in the spec,
208    /// default is 6).
209    pub fn gossip_lazy(&self) -> usize {
210        self.gossip_lazy
211    }
212
213    /// Affects how many peers we will emit gossip to at each heartbeat.
214    ///
215    /// We will send gossip to `gossip_factor * (total number of non-mesh peers)`, or
216    /// `gossip_lazy`, whichever is greater. The default is 0.25.
217    pub fn gossip_factor(&self) -> f64 {
218        self.gossip_factor
219    }
220
221    /// Initial delay in each heartbeat (default is 5 seconds).
222    pub fn heartbeat_initial_delay(&self) -> Duration {
223        self.heartbeat_initial_delay
224    }
225
226    /// Time between each heartbeat (default is 1 second).
227    pub fn heartbeat_interval(&self) -> Duration {
228        self.heartbeat_interval
229    }
230
231    /// Time to live for fanout peers (default is 60 seconds).
232    pub fn fanout_ttl(&self) -> Duration {
233        self.fanout_ttl
234    }
235
236    /// The number of heartbeat ticks until we recheck the connection to explicit peers and
237    /// reconnecting if necessary (default 300).
238    pub fn check_explicit_peers_ticks(&self) -> u64 {
239        self.check_explicit_peers_ticks
240    }
241
242    /// The default global max transmit size for topics.
243    pub fn default_max_transmit_size() -> usize {
244        65536
245    }
246
247    /// The maximum byte size for each gossipsub RPC (default is 65536 bytes).
248    ///
249    /// This represents the maximum size of the published message. It is additionally wrapped
250    /// in a protobuf struct, so the actual wire size may be a bit larger. It must be at least
251    /// large enough to support basic control messages. If Peer eXchange is enabled, this
252    /// must be large enough to transmit the desired peer information on pruning. It must be at
253    /// least 100 bytes. Default is 65536 bytes.
254    pub fn max_transmit_size(&self) -> usize {
255        self.protocol.default_max_transmit_size
256    }
257
258    /// The maximum byte size for each gossipsub RPC for a given topic (default is 65536 bytes).
259    ///
260    /// This represents the maximum size of the published message. It is additionally wrapped
261    /// in a protobuf struct, so the actual wire size may be a bit larger. It must be at least
262    /// large enough to support basic control messages. If Peer eXchange is enabled, this
263    /// must be large enough to transmit the desired peer information on pruning. It must be at
264    /// least 100 bytes. Default is 65536 bytes.
265    pub fn max_transmit_size_for_topic(&self, topic: &TopicHash) -> usize {
266        self.protocol_config().max_transmit_size_for_topic(topic)
267    }
268
269    /// Duplicates are prevented by storing message id's of known messages in an LRU time cache.
270    /// This settings sets the time period that messages are stored in the cache. Duplicates can be
271    /// received if duplicate messages are sent at a time greater than this setting apart. The
272    /// default is 1 minute.
273    pub fn duplicate_cache_time(&self) -> Duration {
274        self.duplicate_cache_time
275    }
276
277    /// When set to `true`, prevents automatic forwarding of all received messages. This setting
278    /// allows a user to validate the messages before propagating them to their peers. If set to
279    /// true, the user must manually call [`crate::Behaviour::report_message_validation_result()`]
280    /// on the behaviour to forward message once validated (default is `false`).
281    /// The default is `false`.
282    pub fn validate_messages(&self) -> bool {
283        self.validate_messages
284    }
285
286    /// Determines the level of validation used when receiving messages. See [`ValidationMode`]
287    /// for the available types. The default is ValidationMode::Strict.
288    pub fn validation_mode(&self) -> &ValidationMode {
289        &self.protocol.validation_mode
290    }
291
292    /// A user-defined function allowing the user to specify the message id of a gossipsub message.
293    /// The default value is to concatenate the source peer id with a sequence number. Setting this
294    /// parameter allows the user to address packets arbitrarily. One example is content based
295    /// addressing, where this function may be set to `hash(message)`. This would prevent messages
296    /// of the same content from being duplicated.
297    ///
298    /// The function takes a [`Message`] as input and outputs a String to be interpreted as
299    /// the message id.
300    pub fn message_id(&self, message: &Message) -> MessageId {
301        (self.message_id_fn)(message)
302    }
303
304    /// By default, gossipsub will reject messages that are sent to us that have the same message
305    /// source as we have specified locally. Enabling this, allows these messages and prevents
306    /// penalizing the peer that sent us the message. Default is false.
307    pub fn allow_self_origin(&self) -> bool {
308        self.allow_self_origin
309    }
310
311    /// Whether Peer eXchange is enabled; this should be enabled in bootstrappers and other well
312    /// connected/trusted nodes. The default is false.
313    ///
314    /// Note: Peer exchange is not implemented today, see
315    /// <https://github.com/libp2p/rust-libp2p/issues/2398>.
316    pub fn do_px(&self) -> bool {
317        self.do_px
318    }
319
320    /// Controls the number of peers to include in prune Peer eXchange.
321    /// When we prune a peer that's eligible for PX (has a good score, etc), we will try to
322    /// send them signed peer records for up to `prune_peers` other peers that we
323    /// know of. It is recommended that this value is larger than `mesh_n_high` so that the pruned
324    /// peer can reliably form a full mesh. The default is typically 16 however until signed
325    /// records are spec'd this is disabled and set to 0.
326    pub fn prune_peers(&self) -> usize {
327        self.prune_peers
328    }
329
330    /// Controls the backoff time for pruned peers. This is how long
331    /// a peer must wait before attempting to graft into our mesh again after being pruned.
332    /// When pruning a peer, we send them our value of `prune_backoff` so they know
333    /// the minimum time to wait. Peers running older versions may not send a backoff time,
334    /// so if we receive a prune message without one, we will wait at least `prune_backoff`
335    /// before attempting to re-graft. The default is one minute.
336    pub fn prune_backoff(&self) -> Duration {
337        self.prune_backoff
338    }
339
340    /// Controls the backoff time when unsubscribing from a topic.
341    ///
342    /// This is how long to wait before resubscribing to the topic. A short backoff period in case
343    /// of an unsubscribe event allows reaching a healthy mesh in a more timely manner. The default
344    /// is 10 seconds.
345    pub fn unsubscribe_backoff(&self) -> Duration {
346        self.unsubscribe_backoff
347    }
348
349    /// Number of heartbeat slots considered as slack for backoffs. This guarantees that we wait
350    /// at least backoff_slack heartbeats after a backoff is over before we try to graft. This
351    /// solves problems occurring through high latencies. In particular if
352    /// `backoff_slack * heartbeat_interval` is longer than any latencies between processing
353    /// prunes on our side and processing prunes on the receiving side this guarantees that we
354    /// get not punished for too early grafting. The default is 1.
355    pub fn backoff_slack(&self) -> u32 {
356        self.backoff_slack
357    }
358
359    /// Whether to do flood publishing or not. If enabled newly created messages will always be
360    /// sent to all peers that are subscribed to the topic and have a good enough score.
361    /// The default is true.
362    pub fn flood_publish(&self) -> bool {
363        self.flood_publish
364    }
365
366    /// If a GRAFT comes before `graft_flood_threshold` has elapsed since the last PRUNE,
367    /// then there is an extra score penalty applied to the peer through P7.
368    pub fn graft_flood_threshold(&self) -> Duration {
369        self.graft_flood_threshold
370    }
371
372    /// Minimum number of outbound peers in the mesh network before adding more (D_out in the spec).
373    /// This value must be smaller or equal than `mesh_n / 2` and smaller than `mesh_n_low`.
374    /// The default is 2.
375    pub fn mesh_outbound_min(&self) -> usize {
376        self.topic_configuration
377            .default_mesh_params
378            .mesh_outbound_min
379    }
380
381    /// Minimum number of outbound peers in the mesh network before adding more (D_out in the spec).
382    /// This value must be smaller or equal than `mesh_n / 2` and smaller than `mesh_n_low`.
383    /// The default is 2.
384    pub fn mesh_outbound_min_for_topic(&self, topic_hash: &TopicHash) -> usize {
385        self.topic_configuration
386            .topic_mesh_params
387            .get(topic_hash)
388            .unwrap_or(&self.topic_configuration.default_mesh_params)
389            .mesh_outbound_min
390    }
391
392    /// Number of heartbeat ticks that specify the interval in which opportunistic grafting is
393    /// applied. Every `opportunistic_graft_ticks` we will attempt to select some high-scoring mesh
394    /// peers to replace lower-scoring ones, if the median score of our mesh peers falls below a
395    /// threshold (see <https://godoc.org/github.com/libp2p/go-libp2p-pubsub#PeerScoreThresholds>).
396    /// The default is 60.
397    pub fn opportunistic_graft_ticks(&self) -> u64 {
398        self.opportunistic_graft_ticks
399    }
400
401    /// Controls how many times we will allow a peer to request the same message id through IWANT
402    /// gossip before we start ignoring them. This is designed to prevent peers from spamming us
403    /// with requests and wasting our resources. The default is 3.
404    pub fn gossip_retransimission(&self) -> u32 {
405        self.gossip_retransimission
406    }
407
408    /// The maximum number of new peers to graft to during opportunistic grafting. The default is 2.
409    pub fn opportunistic_graft_peers(&self) -> usize {
410        self.opportunistic_graft_peers
411    }
412
413    /// The maximum number of messages we will process in a given RPC. If this is unset, there is
414    /// no limit. The default is None.
415    pub fn max_messages_per_rpc(&self) -> Option<usize> {
416        self.max_messages_per_rpc
417    }
418
419    /// The maximum number of messages to include in an IHAVE message.
420    /// Also controls the maximum number of IHAVE ids we will accept and request with IWANT from a
421    /// peer within a heartbeat, to protect from IHAVE floods. You should adjust this value from the
422    /// default if your system is pushing more than 5000 messages in GossipSubHistoryGossip
423    /// heartbeats; with the defaults this is 1666 messages/s. The default is 5000.
424    pub fn max_ihave_length(&self) -> usize {
425        self.max_ihave_length
426    }
427
428    /// GossipSubMaxIHaveMessages is the maximum number of IHAVE messages to accept from a peer
429    /// within a heartbeat.
430    pub fn max_ihave_messages(&self) -> usize {
431        self.max_ihave_messages
432    }
433
434    /// Time to wait for a message requested through IWANT following an IHAVE advertisement.
435    /// If the message is not received within this window, a broken promise is declared and
436    /// the router may apply behavioural penalties. The default is 3 seconds.
437    pub fn iwant_followup_time(&self) -> Duration {
438        self.iwant_followup_time
439    }
440
441    /// Enable support for flooodsub peers. Default false.
442    pub fn support_floodsub(&self) -> bool {
443        self.protocol.protocol_ids.contains(&FLOODSUB_PROTOCOL)
444    }
445
446    /// The max number of messages a `ConnectionHandler` can buffer. The default is 5000.
447    pub fn connection_handler_queue_len(&self) -> usize {
448        self.connection_handler_queue_len
449    }
450
451    /// The duration a message to be published can wait to be sent before it is abandoned. The
452    /// default is 5 seconds.
453    pub fn publish_queue_duration(&self) -> Duration {
454        self.connection_handler_publish_duration
455    }
456
457    /// The duration a message to be forwarded can wait to be sent before it is abandoned. The
458    /// default is 1s.
459    pub fn forward_queue_duration(&self) -> Duration {
460        self.connection_handler_forward_duration
461    }
462
463    /// The message size threshold for which IDONTWANT messages are sent.
464    /// Sending IDONTWANT messages for small messages can have a negative effect to the overall
465    /// traffic and CPU load. This acts as a lower bound cutoff for the message size to which
466    /// IDONTWANT won't be sent to peers. Only works if the peers support Gossipsub1.2
467    /// (see <https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md#idontwant-message>)
468    /// default is 1kB
469    pub fn idontwant_message_size_threshold(&self) -> usize {
470        self.idontwant_message_size_threshold
471    }
472
473    /// Send IDONTWANT messages after publishing message on gossip. This is an optimisation
474    /// to avoid bandwidth consumption by downloading the published message over gossip.
475    /// By default it is false.
476    pub fn idontwant_on_publish(&self) -> bool {
477        self.idontwant_on_publish
478    }
479}
480
481impl Default for Config {
482    fn default() -> Self {
483        // use ConfigBuilder to also validate defaults
484        ConfigBuilder::default()
485            .build()
486            .expect("Default config parameters should be valid parameters")
487    }
488}
489
490/// The builder struct for constructing a gossipsub configuration.
491pub struct ConfigBuilder {
492    config: Config,
493    invalid_protocol: bool, // This is a bit of a hack to only expose one error to the user.
494}
495
496impl Default for ConfigBuilder {
497    fn default() -> Self {
498        ConfigBuilder {
499            config: Config {
500                protocol: ProtocolConfig::default(),
501                history_length: 5,
502                history_gossip: 3,
503                retain_scores: 4,
504                gossip_lazy: 6, // default to mesh_n
505                gossip_factor: 0.25,
506                heartbeat_initial_delay: Duration::from_secs(5),
507                heartbeat_interval: Duration::from_secs(1),
508                fanout_ttl: Duration::from_secs(60),
509                check_explicit_peers_ticks: 300,
510                duplicate_cache_time: Duration::from_secs(60),
511                validate_messages: false,
512                message_id_fn: Arc::new(|message| {
513                    // default message id is: source + sequence number
514                    // NOTE: If either the peer_id or source is not provided, we set to 0;
515                    let mut source_string = if let Some(peer_id) = message.source.as_ref() {
516                        peer_id.to_base58()
517                    } else {
518                        PeerId::from_bytes(&[0, 1, 0])
519                            .expect("Valid peer id")
520                            .to_base58()
521                    };
522                    source_string
523                        .push_str(&message.sequence_number.unwrap_or_default().to_string());
524                    MessageId::from(source_string)
525                }),
526                allow_self_origin: false,
527                do_px: false,
528                // NOTE: Increasing this currently has little effect until Signed
529                // records are implemented.
530                prune_peers: 0,
531                prune_backoff: Duration::from_secs(60),
532                unsubscribe_backoff: Duration::from_secs(10),
533                backoff_slack: 1,
534                flood_publish: true,
535                graft_flood_threshold: Duration::from_secs(10),
536                opportunistic_graft_ticks: 60,
537                opportunistic_graft_peers: 2,
538                gossip_retransimission: 3,
539                max_messages_per_rpc: None,
540                max_ihave_length: 5000,
541                max_ihave_messages: 10,
542                iwant_followup_time: Duration::from_secs(3),
543                connection_handler_queue_len: 5000,
544                connection_handler_publish_duration: Duration::from_secs(5),
545                connection_handler_forward_duration: Duration::from_secs(1),
546                idontwant_message_size_threshold: 1000,
547                idontwant_on_publish: false,
548                topic_configuration: TopicConfigs::default(),
549            },
550            invalid_protocol: false,
551        }
552    }
553}
554
555impl From<Config> for ConfigBuilder {
556    fn from(config: Config) -> Self {
557        ConfigBuilder {
558            config,
559            invalid_protocol: false,
560        }
561    }
562}
563
564impl ConfigBuilder {
565    /// The protocol id prefix to negotiate this protocol (default is `/meshsub/1.1.0` and
566    /// `/meshsub/1.0.0`).
567    pub fn protocol_id_prefix(
568        &mut self,
569        protocol_id_prefix: impl Into<Cow<'static, str>>,
570    ) -> &mut Self {
571        let cow = protocol_id_prefix.into();
572
573        match (
574            StreamProtocol::try_from_owned(format!("{cow}/1.1.0")),
575            StreamProtocol::try_from_owned(format!("{cow}/1.0.0")),
576        ) {
577            (Ok(p1), Ok(p2)) => {
578                self.config.protocol.protocol_ids = vec![
579                    ProtocolId {
580                        protocol: p1,
581                        kind: PeerKind::Gossipsubv1_1,
582                    },
583                    ProtocolId {
584                        protocol: p2,
585                        kind: PeerKind::Gossipsub,
586                    },
587                ]
588            }
589            _ => {
590                self.invalid_protocol = true;
591            }
592        }
593
594        self
595    }
596
597    /// The full protocol id to negotiate this protocol (does not append `/1.0.0` or `/1.1.0`).
598    pub fn protocol_id(
599        &mut self,
600        protocol_id: impl Into<Cow<'static, str>>,
601        custom_id_version: Version,
602    ) -> &mut Self {
603        let cow = protocol_id.into();
604
605        match StreamProtocol::try_from_owned(cow.to_string()) {
606            Ok(protocol) => {
607                self.config.protocol.protocol_ids = vec![ProtocolId {
608                    protocol,
609                    kind: match custom_id_version {
610                        Version::V1_1 => PeerKind::Gossipsubv1_1,
611                        Version::V1_0 => PeerKind::Gossipsub,
612                    },
613                }]
614            }
615            _ => {
616                self.invalid_protocol = true;
617            }
618        }
619
620        self
621    }
622
623    /// Number of heartbeats to keep in the `memcache` (default is 5).
624    pub fn history_length(&mut self, history_length: usize) -> &mut Self {
625        self.config.history_length = history_length;
626        self
627    }
628
629    /// Number of past heartbeats to gossip about (default is 3).
630    pub fn history_gossip(&mut self, history_gossip: usize) -> &mut Self {
631        self.config.history_gossip = history_gossip;
632        self
633    }
634
635    /// Target number of peers for the mesh network (D in the spec, default is 6).
636    pub fn mesh_n(&mut self, mesh_n: usize) -> &mut Self {
637        self.config.topic_configuration.default_mesh_params.mesh_n = mesh_n;
638        self
639    }
640
641    /// Target number of peers for the mesh network for a topic (D in the spec, default is 6).
642    pub fn mesh_n_for_topic(&mut self, mesh_n: usize, topic_hash: TopicHash) -> &mut Self {
643        self.config
644            .topic_configuration
645            .topic_mesh_params
646            .entry(topic_hash)
647            .and_modify(|existing_config| {
648                existing_config.mesh_n = mesh_n;
649            })
650            .or_insert_with(|| TopicMeshConfig {
651                mesh_n,
652                ..TopicMeshConfig::default()
653            });
654        self
655    }
656
657    /// Maximum number of peers in mesh network before removing some (D_high in the spec, default
658    /// is 12).
659    pub fn mesh_n_high(&mut self, mesh_n_high: usize) -> &mut Self {
660        self.config
661            .topic_configuration
662            .default_mesh_params
663            .mesh_n_high = mesh_n_high;
664        self
665    }
666
667    /// Maximum number of peers in mesh network for a topic before removing some (D_high in the
668    /// spec, default is 12).
669    pub fn mesh_n_high_for_topic(
670        &mut self,
671        mesh_n_high: usize,
672        topic_hash: TopicHash,
673    ) -> &mut Self {
674        self.config
675            .topic_configuration
676            .topic_mesh_params
677            .entry(topic_hash)
678            .and_modify(|existing_config| {
679                existing_config.mesh_n_high = mesh_n_high;
680            })
681            .or_insert_with(|| TopicMeshConfig {
682                mesh_n_high,
683                ..TopicMeshConfig::default()
684            });
685        self
686    }
687
688    /// Minimum number of peers in mesh network before adding more (D_lo in the spec, default is 4).
689    pub fn mesh_n_low(&mut self, mesh_n_low: usize) -> &mut Self {
690        self.config
691            .topic_configuration
692            .default_mesh_params
693            .mesh_n_low = mesh_n_low;
694        self
695    }
696
697    /// Minimum number of peers in mesh network for a topic before adding more (D_lo in the spec,
698    /// default is 4).
699    pub fn mesh_n_low_for_topic(&mut self, mesh_n_low: usize, topic_hash: TopicHash) -> &mut Self {
700        self.config
701            .topic_configuration
702            .topic_mesh_params
703            .entry(topic_hash)
704            .and_modify(|existing_config| {
705                existing_config.mesh_n_low = mesh_n_low;
706            })
707            .or_insert_with(|| TopicMeshConfig {
708                mesh_n_low,
709                ..TopicMeshConfig::default()
710            });
711        self
712    }
713
714    /// Affects how peers are selected when pruning a mesh due to over subscription.
715    ///
716    /// At least [`Self::retain_scores`] of the retained peers will be high-scoring, while the
717    /// remainder are chosen randomly (D_score in the spec, default is 4).
718    pub fn retain_scores(&mut self, retain_scores: usize) -> &mut Self {
719        self.config.retain_scores = retain_scores;
720        self
721    }
722
723    /// Minimum number of peers to emit gossip to during a heartbeat (D_lazy in the spec,
724    /// default is 6).
725    pub fn gossip_lazy(&mut self, gossip_lazy: usize) -> &mut Self {
726        self.config.gossip_lazy = gossip_lazy;
727        self
728    }
729
730    /// Affects how many peers we will emit gossip to at each heartbeat.
731    ///
732    /// We will send gossip to `gossip_factor * (total number of non-mesh peers)`, or
733    /// `gossip_lazy`, whichever is greater. The default is 0.25.
734    pub fn gossip_factor(&mut self, gossip_factor: f64) -> &mut Self {
735        self.config.gossip_factor = gossip_factor;
736        self
737    }
738
739    /// Initial delay in each heartbeat (default is 5 seconds).
740    pub fn heartbeat_initial_delay(&mut self, heartbeat_initial_delay: Duration) -> &mut Self {
741        self.config.heartbeat_initial_delay = heartbeat_initial_delay;
742        self
743    }
744
745    /// Time between each heartbeat (default is 1 second).
746    pub fn heartbeat_interval(&mut self, heartbeat_interval: Duration) -> &mut Self {
747        self.config.heartbeat_interval = heartbeat_interval;
748        self
749    }
750
751    /// The number of heartbeat ticks until we recheck the connection to explicit peers and
752    /// reconnecting if necessary (default 300).
753    pub fn check_explicit_peers_ticks(&mut self, check_explicit_peers_ticks: u64) -> &mut Self {
754        self.config.check_explicit_peers_ticks = check_explicit_peers_ticks;
755        self
756    }
757
758    /// Time to live for fanout peers (default is 60 seconds).
759    pub fn fanout_ttl(&mut self, fanout_ttl: Duration) -> &mut Self {
760        self.config.fanout_ttl = fanout_ttl;
761        self
762    }
763
764    /// The maximum byte size for each gossip (default is 65536 bytes).
765    ///
766    /// ```rust
767    /// use libp2p_gossipsub::ConfigBuilder;
768    /// let mut config = ConfigBuilder::default();
769    /// assert_eq!(config.build().unwrap().max_transmit_size(), 65536);
770    /// config.max_transmit_size(1 << 20);
771    /// assert_eq!(config.build().unwrap().max_transmit_size(), 1 << 20);
772    /// ```
773    pub fn max_transmit_size(&mut self, max_transmit_size: usize) -> &mut Self {
774        self.config.protocol.default_max_transmit_size = max_transmit_size;
775        self
776    }
777
778    /// The maximum byte size for each gossip for a given topic. (default is
779    /// [`Self::max_transmit_size`]).
780    pub fn max_transmit_size_for_topic(
781        &mut self,
782        max_transmit_size: usize,
783        topic: TopicHash,
784    ) -> &mut Self {
785        self.config
786            .protocol
787            .max_transmit_sizes
788            .insert(topic, max_transmit_size);
789        self
790    }
791
792    /// Duplicates are prevented by storing message id's of known messages in an LRU time cache.
793    /// This settings sets the time period that messages are stored in the cache. Duplicates can be
794    /// received if duplicate messages are sent at a time greater than this setting apart. The
795    /// default is 1 minute.
796    pub fn duplicate_cache_time(&mut self, cache_size: Duration) -> &mut Self {
797        self.config.duplicate_cache_time = cache_size;
798        self
799    }
800
801    /// When set, prevents automatic forwarding of all received messages. This setting
802    /// allows a user to validate the messages before propagating them to their peers. If set,
803    /// the user must manually call [`crate::Behaviour::report_message_validation_result()`] on the
804    /// behaviour to forward a message once validated.
805    pub fn validate_messages(&mut self) -> &mut Self {
806        self.config.validate_messages = true;
807        self
808    }
809
810    /// Determines the level of validation used when receiving messages. See [`ValidationMode`]
811    /// for the available types. The default is ValidationMode::Strict.
812    pub fn validation_mode(&mut self, validation_mode: ValidationMode) -> &mut Self {
813        self.config.protocol.validation_mode = validation_mode;
814        self
815    }
816
817    /// A user-defined function allowing the user to specify the message id of a gossipsub message.
818    /// The default value is to concatenate the source peer id with a sequence number. Setting this
819    /// parameter allows the user to address packets arbitrarily. One example is content based
820    /// addressing, where this function may be set to `hash(message)`. This would prevent messages
821    /// of the same content from being duplicated.
822    ///
823    /// The function takes a [`Message`] as input and outputs a String to be
824    /// interpreted as the message id.
825    pub fn message_id_fn<F>(&mut self, id_fn: F) -> &mut Self
826    where
827        F: Fn(&Message) -> MessageId + Send + Sync + 'static,
828    {
829        self.config.message_id_fn = Arc::new(id_fn);
830        self
831    }
832
833    /// Enables Peer eXchange. This should be enabled in bootstrappers and other well
834    /// connected/trusted nodes. The default is false.
835    ///
836    /// Note: Peer exchange is not implemented today, see
837    /// <https://github.com/libp2p/rust-libp2p/issues/2398>.
838    pub fn do_px(&mut self) -> &mut Self {
839        self.config.do_px = true;
840        self
841    }
842
843    /// Controls the number of peers to include in prune Peer eXchange.
844    ///
845    /// When we prune a peer that's eligible for PX (has a good score, etc), we will try to
846    /// send them signed peer records for up to [`Self::prune_peers] other peers that we
847    /// know of. It is recommended that this value is larger than [`Self::mesh_n_high`] so that the
848    /// pruned peer can reliably form a full mesh. The default is 16.
849    pub fn prune_peers(&mut self, prune_peers: usize) -> &mut Self {
850        self.config.prune_peers = prune_peers;
851        self
852    }
853
854    /// Controls the backoff time for pruned peers. This is how long
855    /// a peer must wait before attempting to graft into our mesh again after being pruned.
856    /// When pruning a peer, we send them our value of [`Self::prune_backoff`] so they know
857    /// the minimum time to wait. Peers running older versions may not send a backoff time,
858    /// so if we receive a prune message without one, we will wait at least [`Self::prune_backoff`]
859    /// before attempting to re-graft. The default is one minute.
860    pub fn prune_backoff(&mut self, prune_backoff: Duration) -> &mut Self {
861        self.config.prune_backoff = prune_backoff;
862        self
863    }
864
865    /// Controls the backoff time when unsubscribing from a topic.
866    ///
867    /// This is how long to wait before resubscribing to the topic. A short backoff period in case
868    /// of an unsubscribe event allows reaching a healthy mesh in a more timely manner. The default
869    /// is 10 seconds.
870    pub fn unsubscribe_backoff(&mut self, unsubscribe_backoff: Duration) -> &mut Self {
871        self.config.unsubscribe_backoff = unsubscribe_backoff;
872        self
873    }
874
875    /// Number of heartbeat slots considered as slack for backoffs. This guarantees that we wait
876    /// at least backoff_slack heartbeats after a backoff is over before we try to graft. This
877    /// solves problems occurring through high latencies. In particular if
878    /// `backoff_slack * heartbeat_interval` is longer than any latencies between processing
879    /// prunes on our side and processing prunes on the receiving side this guarantees that we
880    /// get not punished for too early grafting. The default is 1.
881    pub fn backoff_slack(&mut self, backoff_slack: u32) -> &mut Self {
882        self.config.backoff_slack = backoff_slack;
883        self
884    }
885
886    /// Whether to do flood publishing or not. If enabled newly created messages will always be
887    /// sent to all peers that are subscribed to the topic and have a good enough score.
888    /// The default is true.
889    pub fn flood_publish(&mut self, flood_publish: bool) -> &mut Self {
890        self.config.flood_publish = flood_publish;
891        self
892    }
893
894    /// If a GRAFT comes before `graft_flood_threshold` has elapsed since the last PRUNE,
895    /// then there is an extra score penalty applied to the peer through P7.
896    pub fn graft_flood_threshold(&mut self, graft_flood_threshold: Duration) -> &mut Self {
897        self.config.graft_flood_threshold = graft_flood_threshold;
898        self
899    }
900
901    /// Minimum number of outbound peers in the mesh network before adding more (D_out in the spec).
902    /// This value must be smaller or equal than `mesh_n / 2` and smaller than `mesh_n_low`.
903    /// The default is 2.
904    pub fn mesh_outbound_min(&mut self, mesh_outbound_min: usize) -> &mut Self {
905        self.config
906            .topic_configuration
907            .default_mesh_params
908            .mesh_outbound_min = mesh_outbound_min;
909        self
910    }
911
912    /// Minimum number of outbound peers in the mesh network for a topic before adding more (D_out
913    /// in the spec). This value must be smaller or equal than `mesh_n / 2` and smaller than
914    /// `mesh_n_low`. The default is 2.
915    pub fn mesh_outbound_min_for_topic(
916        &mut self,
917        mesh_outbound_min: usize,
918        topic_hash: TopicHash,
919    ) -> &mut Self {
920        self.config
921            .topic_configuration
922            .topic_mesh_params
923            .entry(topic_hash)
924            .and_modify(|existing_config| {
925                existing_config.mesh_outbound_min = mesh_outbound_min;
926            })
927            .or_insert_with(|| TopicMeshConfig {
928                mesh_outbound_min,
929                ..TopicMeshConfig::default()
930            });
931        self
932    }
933
934    /// Number of heartbeat ticks that specify the interval in which opportunistic grafting is
935    /// applied. Every `opportunistic_graft_ticks` we will attempt to select some high-scoring mesh
936    /// peers to replace lower-scoring ones, if the median score of our mesh peers falls below a
937    /// threshold (see <https://godoc.org/github.com/libp2p/go-libp2p-pubsub#PeerScoreThresholds>).
938    /// The default is 60.
939    pub fn opportunistic_graft_ticks(&mut self, opportunistic_graft_ticks: u64) -> &mut Self {
940        self.config.opportunistic_graft_ticks = opportunistic_graft_ticks;
941        self
942    }
943
944    /// Controls how many times we will allow a peer to request the same message id through IWANT
945    /// gossip before we start ignoring them. This is designed to prevent peers from spamming us
946    /// with requests and wasting our resources.
947    pub fn gossip_retransimission(&mut self, gossip_retransimission: u32) -> &mut Self {
948        self.config.gossip_retransimission = gossip_retransimission;
949        self
950    }
951
952    /// The maximum number of new peers to graft to during opportunistic grafting. The default is 2.
953    pub fn opportunistic_graft_peers(&mut self, opportunistic_graft_peers: usize) -> &mut Self {
954        self.config.opportunistic_graft_peers = opportunistic_graft_peers;
955        self
956    }
957
958    /// The maximum number of messages we will process in a given RPC. If this is unset, there is
959    /// no limit. The default is None.
960    pub fn max_messages_per_rpc(&mut self, max: Option<usize>) -> &mut Self {
961        self.config.max_messages_per_rpc = max;
962        self
963    }
964
965    /// The maximum number of messages to include in an IHAVE message.
966    /// Also controls the maximum number of IHAVE ids we will accept and request with IWANT from a
967    /// peer within a heartbeat, to protect from IHAVE floods. You should adjust this value from the
968    /// default if your system is pushing more than 5000 messages in GossipSubHistoryGossip
969    /// heartbeats; with the defaults this is 1666 messages/s. The default is 5000.
970    pub fn max_ihave_length(&mut self, max_ihave_length: usize) -> &mut Self {
971        self.config.max_ihave_length = max_ihave_length;
972        self
973    }
974
975    /// GossipSubMaxIHaveMessages is the maximum number of IHAVE messages to accept from a peer
976    /// within a heartbeat.
977    pub fn max_ihave_messages(&mut self, max_ihave_messages: usize) -> &mut Self {
978        self.config.max_ihave_messages = max_ihave_messages;
979        self
980    }
981
982    /// By default, gossipsub will reject messages that are sent to us that has the same message
983    /// source as we have specified locally. Enabling this, allows these messages and prevents
984    /// penalizing the peer that sent us the message. Default is false.
985    pub fn allow_self_origin(&mut self, allow_self_origin: bool) -> &mut Self {
986        self.config.allow_self_origin = allow_self_origin;
987        self
988    }
989
990    /// Time to wait for a message requested through IWANT following an IHAVE advertisement.
991    /// If the message is not received within this window, a broken promise is declared and
992    /// the router may apply behavioural penalties. The default is 3 seconds.
993    pub fn iwant_followup_time(&mut self, iwant_followup_time: Duration) -> &mut Self {
994        self.config.iwant_followup_time = iwant_followup_time;
995        self
996    }
997
998    /// Enable support for flooodsub peers.
999    pub fn support_floodsub(&mut self) -> &mut Self {
1000        if self
1001            .config
1002            .protocol
1003            .protocol_ids
1004            .contains(&FLOODSUB_PROTOCOL)
1005        {
1006            return self;
1007        }
1008
1009        self.config.protocol.protocol_ids.push(FLOODSUB_PROTOCOL);
1010        self
1011    }
1012
1013    /// The max number of messages a `ConnectionHandler` can buffer. The default is 5000.
1014    pub fn connection_handler_queue_len(&mut self, len: usize) -> &mut Self {
1015        self.config.connection_handler_queue_len = len;
1016        self
1017    }
1018
1019    /// The duration a message to be published can wait to be sent before it is abandoned. The
1020    /// default is 5 seconds.
1021    pub fn publish_queue_duration(&mut self, duration: Duration) -> &mut Self {
1022        self.config.connection_handler_publish_duration = duration;
1023        self
1024    }
1025
1026    /// The duration a message to be forwarded can wait to be sent before it is abandoned. The
1027    /// default is 1s.
1028    pub fn forward_queue_duration(&mut self, duration: Duration) -> &mut Self {
1029        self.config.connection_handler_forward_duration = duration;
1030        self
1031    }
1032
1033    /// The message size threshold for which IDONTWANT messages are sent.
1034    /// Sending IDONTWANT messages for small messages can have a negative effect to the overall
1035    /// traffic and CPU load. This acts as a lower bound cutoff for the message size to which
1036    /// IDONTWANT won't be sent to peers. Only works if the peers support Gossipsub1.2
1037    /// (see <https://github.com/libp2p/specs/blob/master/pubsub/gossipsub/gossipsub-v1.2.md#idontwant-message>)
1038    /// default is 1kB
1039    pub fn idontwant_message_size_threshold(&mut self, size: usize) -> &mut Self {
1040        self.config.idontwant_message_size_threshold = size;
1041        self
1042    }
1043
1044    /// Send IDONTWANT messages after publishing message on gossip. This is an optimisation
1045    /// to avoid bandwidth consumption by downloading the published message over gossip.
1046    /// By default it is false.
1047    pub fn idontwant_on_publish(&mut self, idontwant_on_publish: bool) -> &mut Self {
1048        self.config.idontwant_on_publish = idontwant_on_publish;
1049        self
1050    }
1051
1052    /// The topic configuration sets mesh parameter sizes for a given topic. Notes on default
1053    /// below.
1054    ///
1055    /// mesh_outbound_min
1056    /// Minimum number of outbound peers in the mesh network before adding more (D_out in the spec).
1057    /// This value must be smaller or equal than `mesh_n / 2` and smaller than `mesh_n_low`.
1058    /// The default is 2.
1059    ///
1060    /// mesh_n
1061    /// Target number of peers for the mesh network for a given topic (D in the spec, default is 6).
1062    ///
1063    /// mesh_n_low
1064    /// Minimum number of peers in mesh network before adding more for a given topic (D_lo in the
1065    /// spec, default is 4).
1066    ///
1067    /// mesh_n_high
1068    /// Maximum number of peers in mesh network before removing some for a given topic (D_high in
1069    /// the spec, default is 12).
1070    pub fn set_topic_config(&mut self, topic: TopicHash, config: TopicMeshConfig) -> &mut Self {
1071        self.config
1072            .topic_configuration
1073            .topic_mesh_params
1074            .insert(topic, config);
1075        self
1076    }
1077
1078    /// The topic max size sets message sizes for a given topic.
1079    pub fn set_topic_max_transmit_size(&mut self, topic: TopicHash, max_size: usize) -> &mut Self {
1080        self.config
1081            .protocol
1082            .max_transmit_sizes
1083            .insert(topic, max_size);
1084        self
1085    }
1086
1087    /// Constructs a [`Config`] from the given configuration and validates the settings.
1088    pub fn build(&self) -> Result<Config, ConfigBuilderError> {
1089        // check all constraints on config
1090
1091        let pre_configured_topics = self.config.protocol.max_transmit_sizes.keys();
1092        for topic in pre_configured_topics {
1093            if self.config.protocol.max_transmit_size_for_topic(topic) < 100 {
1094                return Err(ConfigBuilderError::MaxTransmissionSizeTooSmall);
1095            }
1096
1097            let mesh_n = self.config.mesh_n_for_topic(topic);
1098            let mesh_n_low = self.config.mesh_n_low_for_topic(topic);
1099            let mesh_n_high = self.config.mesh_n_high_for_topic(topic);
1100            let mesh_outbound_min = self.config.mesh_outbound_min_for_topic(topic);
1101
1102            if !(mesh_outbound_min <= mesh_n_low && mesh_n_low <= mesh_n && mesh_n <= mesh_n_high) {
1103                return Err(ConfigBuilderError::MeshParametersInvalid);
1104            }
1105
1106            if mesh_outbound_min * 2 > mesh_n {
1107                return Err(ConfigBuilderError::MeshOutboundInvalid);
1108            }
1109        }
1110
1111        if self.config.history_length < self.config.history_gossip {
1112            return Err(ConfigBuilderError::HistoryLengthTooSmall);
1113        }
1114
1115        if self.config.unsubscribe_backoff.as_millis() == 0 {
1116            return Err(ConfigBuilderError::UnsubscribeBackoffIsZero);
1117        }
1118
1119        if self.invalid_protocol {
1120            return Err(ConfigBuilderError::InvalidProtocol);
1121        }
1122
1123        Ok(self.config.clone())
1124    }
1125}
1126
1127impl std::fmt::Debug for Config {
1128    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1129        let mut builder = f.debug_struct("GossipsubConfig");
1130        let _ = builder.field("protocol", &self.protocol);
1131        let _ = builder.field("history_length", &self.history_length);
1132        let _ = builder.field("history_gossip", &self.history_gossip);
1133        let _ = builder.field(
1134            "mesh_n",
1135            &self.topic_configuration.default_mesh_params.mesh_n,
1136        );
1137        let _ = builder.field(
1138            "mesh_n_low",
1139            &self.topic_configuration.default_mesh_params.mesh_n_low,
1140        );
1141        let _ = builder.field(
1142            "mesh_n_high",
1143            &self.topic_configuration.default_mesh_params.mesh_n_high,
1144        );
1145        let _ = builder.field("retain_scores", &self.retain_scores);
1146        let _ = builder.field("gossip_lazy", &self.gossip_lazy);
1147        let _ = builder.field("gossip_factor", &self.gossip_factor);
1148        let _ = builder.field("heartbeat_initial_delay", &self.heartbeat_initial_delay);
1149        let _ = builder.field("heartbeat_interval", &self.heartbeat_interval);
1150        let _ = builder.field("fanout_ttl", &self.fanout_ttl);
1151        let _ = builder.field("duplicate_cache_time", &self.duplicate_cache_time);
1152        let _ = builder.field("validate_messages", &self.validate_messages);
1153        let _ = builder.field("allow_self_origin", &self.allow_self_origin);
1154        let _ = builder.field("do_px", &self.do_px);
1155        let _ = builder.field("prune_peers", &self.prune_peers);
1156        let _ = builder.field("prune_backoff", &self.prune_backoff);
1157        let _ = builder.field("backoff_slack", &self.backoff_slack);
1158        let _ = builder.field("flood_publish", &self.flood_publish);
1159        let _ = builder.field("graft_flood_threshold", &self.graft_flood_threshold);
1160        let _ = builder.field(
1161            "mesh_outbound_min",
1162            &self
1163                .topic_configuration
1164                .default_mesh_params
1165                .mesh_outbound_min,
1166        );
1167        let _ = builder.field("opportunistic_graft_ticks", &self.opportunistic_graft_ticks);
1168        let _ = builder.field("opportunistic_graft_peers", &self.opportunistic_graft_peers);
1169        let _ = builder.field("max_messages_per_rpc", &self.max_messages_per_rpc);
1170        let _ = builder.field("max_ihave_length", &self.max_ihave_length);
1171        let _ = builder.field("max_ihave_messages", &self.max_ihave_messages);
1172        let _ = builder.field("iwant_followup_time", &self.iwant_followup_time);
1173        let _ = builder.field(
1174            "idontwant_message_size_threshold",
1175            &self.idontwant_message_size_threshold,
1176        );
1177        let _ = builder.field("idontwant_on_publish", &self.idontwant_on_publish);
1178        builder.finish()
1179    }
1180}
1181
1182#[cfg(test)]
1183mod test {
1184    use std::{
1185        collections::hash_map::DefaultHasher,
1186        hash::{Hash, Hasher},
1187    };
1188
1189    use libp2p_core::UpgradeInfo;
1190
1191    use super::*;
1192    use crate::{topic::IdentityHash, Topic};
1193
1194    #[test]
1195    fn create_config_with_message_id_as_plain_function() {
1196        let config = ConfigBuilder::default()
1197            .message_id_fn(message_id_plain_function)
1198            .build()
1199            .unwrap();
1200
1201        let result = config.message_id(&get_gossipsub_message());
1202
1203        assert_eq!(result, get_expected_message_id());
1204    }
1205
1206    #[test]
1207    fn create_config_with_message_id_as_closure() {
1208        let config = ConfigBuilder::default()
1209            .message_id_fn(|message: &Message| {
1210                let mut s = DefaultHasher::new();
1211                message.data.hash(&mut s);
1212                let mut v = s.finish().to_string();
1213                v.push('e');
1214                MessageId::from(v)
1215            })
1216            .build()
1217            .unwrap();
1218
1219        let result = config.message_id(&get_gossipsub_message());
1220
1221        assert_eq!(result, get_expected_message_id());
1222    }
1223
1224    #[test]
1225    fn create_config_with_message_id_as_closure_with_variable_capture() {
1226        let captured: char = 'e';
1227
1228        let config = ConfigBuilder::default()
1229            .message_id_fn(move |message: &Message| {
1230                let mut s = DefaultHasher::new();
1231                message.data.hash(&mut s);
1232                let mut v = s.finish().to_string();
1233                v.push(captured);
1234                MessageId::from(v)
1235            })
1236            .build()
1237            .unwrap();
1238
1239        let result = config.message_id(&get_gossipsub_message());
1240
1241        assert_eq!(result, get_expected_message_id());
1242    }
1243
1244    #[test]
1245    fn create_config_with_protocol_id_prefix() {
1246        let protocol_config = ConfigBuilder::default()
1247            .protocol_id_prefix("/purple")
1248            .build()
1249            .unwrap()
1250            .protocol_config();
1251
1252        let protocol_ids = protocol_config.protocol_info();
1253
1254        assert_eq!(protocol_ids.len(), 2);
1255
1256        assert_eq!(
1257            protocol_ids[0].protocol,
1258            StreamProtocol::new("/purple/1.1.0")
1259        );
1260        assert_eq!(protocol_ids[0].kind, PeerKind::Gossipsubv1_1);
1261
1262        assert_eq!(
1263            protocol_ids[1].protocol,
1264            StreamProtocol::new("/purple/1.0.0")
1265        );
1266        assert_eq!(protocol_ids[1].kind, PeerKind::Gossipsub);
1267    }
1268
1269    #[test]
1270    fn create_config_with_custom_protocol_id() {
1271        let protocol_config = ConfigBuilder::default()
1272            .protocol_id("/purple", Version::V1_0)
1273            .build()
1274            .unwrap()
1275            .protocol_config();
1276
1277        let protocol_ids = protocol_config.protocol_info();
1278
1279        assert_eq!(protocol_ids.len(), 1);
1280
1281        assert_eq!(protocol_ids[0].protocol, "/purple");
1282        assert_eq!(protocol_ids[0].kind, PeerKind::Gossipsub);
1283    }
1284
1285    fn get_gossipsub_message() -> Message {
1286        Message {
1287            source: None,
1288            data: vec![12, 34, 56],
1289            sequence_number: None,
1290            topic: Topic::<IdentityHash>::new("test").hash(),
1291        }
1292    }
1293
1294    fn get_expected_message_id() -> MessageId {
1295        MessageId::from([
1296            49, 55, 56, 51, 56, 52, 49, 51, 52, 51, 52, 55, 51, 51, 53, 52, 54, 54, 52, 49, 101,
1297        ])
1298    }
1299
1300    fn message_id_plain_function(message: &Message) -> MessageId {
1301        let mut s = DefaultHasher::new();
1302        message.data.hash(&mut s);
1303        let mut v = s.finish().to_string();
1304        v.push('e');
1305        MessageId::from(v)
1306    }
1307}