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}