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