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