1use std::{
5 net::SocketAddr,
6 num::{NonZeroU32, NonZeroU64},
7 path::PathBuf,
8 time::Duration,
9};
10
11use serde::{Deserialize, Serialize};
12use sui_types::{
13 messages_checkpoint::{CheckpointDigest, CheckpointSequenceNumber},
14 multiaddr::Multiaddr,
15};
16
17#[derive(Clone, Debug, Deserialize, Serialize)]
18#[serde(rename_all = "kebab-case")]
19pub struct P2pConfig {
20 #[serde(default = "default_listen_address")]
22 pub listen_address: SocketAddr,
23 #[serde(skip_serializing_if = "Option::is_none")]
26 pub external_address: Option<Multiaddr>,
27 #[serde(skip_serializing_if = "Vec::is_empty", default)]
30 pub seed_peers: Vec<SeedPeer>,
31 #[serde(skip_serializing_if = "Vec::is_empty", default)]
33 pub peer_address_overrides: Vec<PeerAddresses>,
34 #[serde(skip_serializing_if = "Option::is_none")]
35 pub anemo_config: Option<anemo::Config>,
36 #[serde(skip_serializing_if = "Option::is_none")]
37 pub state_sync: Option<StateSyncConfig>,
38 #[serde(skip_serializing_if = "Option::is_none")]
39 pub discovery: Option<DiscoveryConfig>,
40 #[serde(skip_serializing_if = "Option::is_none")]
41 pub randomness: Option<RandomnessConfig>,
42 #[serde(skip_serializing_if = "Option::is_none")]
47 pub excessive_message_size: Option<usize>,
48}
49
50fn default_listen_address() -> SocketAddr {
51 "0.0.0.0:8084".parse().unwrap()
52}
53
54impl Default for P2pConfig {
55 fn default() -> Self {
56 Self {
57 listen_address: default_listen_address(),
58 external_address: Default::default(),
59 seed_peers: Default::default(),
60 peer_address_overrides: Default::default(),
61 anemo_config: Default::default(),
62 state_sync: None,
63 discovery: None,
64 randomness: None,
65 excessive_message_size: None,
66 }
67 }
68}
69
70impl P2pConfig {
71 pub fn excessive_message_size(&self) -> usize {
72 const EXCESSIVE_MESSAGE_SIZE: usize = 32 << 20;
73
74 self.excessive_message_size
75 .unwrap_or(EXCESSIVE_MESSAGE_SIZE)
76 }
77
78 pub fn set_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
79 self.discovery = Some(discovery_config);
80 self
81 }
82}
83
84#[derive(Clone, Debug, Deserialize, Serialize)]
85#[serde(rename_all = "kebab-case")]
86pub struct SeedPeer {
87 #[serde(skip_serializing_if = "Option::is_none")]
88 pub peer_id: Option<anemo::PeerId>,
89 pub address: Multiaddr,
90}
91
92#[derive(Clone, Debug, Deserialize, Serialize)]
93#[serde(rename_all = "kebab-case")]
94pub struct AllowlistedPeer {
95 pub peer_id: anemo::PeerId,
96 #[serde(skip_serializing_if = "Option::is_none")]
97 pub address: Option<Multiaddr>,
98}
99
100#[derive(Clone, Debug, Deserialize, Serialize)]
101#[serde(rename_all = "kebab-case")]
102pub struct PeerAddresses {
103 pub peer_id: anemo::PeerId,
104 pub addresses: Vec<Multiaddr>,
105}
106
107#[derive(Clone, Debug, Default, Deserialize, Serialize)]
108#[serde(rename_all = "kebab-case")]
109pub struct StateSyncConfig {
110 #[serde(skip_serializing_if = "Vec::is_empty", default)]
119 pub pinned_checkpoints: Vec<(CheckpointSequenceNumber, CheckpointDigest)>,
120
121 #[serde(skip_serializing_if = "Option::is_none")]
125 pub interval_period_ms: Option<u64>,
126
127 #[serde(skip_serializing_if = "Option::is_none")]
131 pub mailbox_capacity: Option<usize>,
132
133 #[serde(skip_serializing_if = "Option::is_none")]
137 pub synced_checkpoint_broadcast_channel_capacity: Option<usize>,
138
139 #[serde(skip_serializing_if = "Option::is_none")]
143 pub checkpoint_header_download_concurrency: Option<usize>,
144
145 #[serde(skip_serializing_if = "Option::is_none")]
149 pub checkpoint_content_download_concurrency: Option<usize>,
150
151 #[serde(skip_serializing_if = "Option::is_none")]
157 pub checkpoint_content_download_tx_concurrency: Option<u64>,
158
159 #[serde(skip_serializing_if = "Option::is_none")]
163 pub timeout_ms: Option<u64>,
164
165 #[serde(skip_serializing_if = "Option::is_none")]
169 pub checkpoint_content_timeout_ms: Option<u64>,
170
171 #[serde(skip_serializing_if = "Option::is_none")]
175 pub push_checkpoint_summary_rate_limit: Option<NonZeroU32>,
176
177 #[serde(skip_serializing_if = "Option::is_none")]
181 pub get_checkpoint_summary_rate_limit: Option<NonZeroU32>,
182
183 #[serde(skip_serializing_if = "Option::is_none")]
187 pub get_checkpoint_contents_rate_limit: Option<NonZeroU32>,
188
189 #[serde(skip_serializing_if = "Option::is_none")]
193 pub get_checkpoint_contents_inflight_limit: Option<usize>,
194
195 #[serde(skip_serializing_if = "Option::is_none")]
200 pub get_checkpoint_contents_per_checkpoint_limit: Option<usize>,
201
202 #[serde(skip_serializing_if = "Option::is_none")]
205 pub wait_interval_when_no_peer_to_sync_content_ms: Option<u64>,
206
207 #[serde(skip_serializing_if = "Option::is_none")]
211 pub use_get_checkpoint_contents_v2: Option<bool>,
212
213 #[serde(skip_serializing_if = "Option::is_none")]
218 pub max_checkpoint_lookahead: Option<u64>,
219
220 #[serde(skip_serializing_if = "Option::is_none")]
224 pub max_checkpoint_sync_batch_size: Option<u64>,
225
226 #[serde(skip_serializing_if = "Option::is_none")]
231 pub max_checkpoint_summary_size: Option<usize>,
232
233 #[serde(skip_serializing_if = "Option::is_none")]
237 pub checkpoint_content_timeout_min_ms: Option<u64>,
238
239 #[serde(skip_serializing_if = "Option::is_none")]
243 pub checkpoint_content_timeout_max_ms: Option<u64>,
244
245 #[serde(skip_serializing_if = "Option::is_none")]
249 pub peer_scoring_window_ms: Option<u64>,
250
251 #[serde(skip_serializing_if = "Option::is_none")]
255 pub exploration_probability: Option<f64>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
262 pub peer_failure_rate: Option<f64>,
263
264 #[serde(skip_serializing_if = "Option::is_none")]
270 pub peer_disconnect_threshold_ms: Option<u64>,
271
272 #[serde(skip_serializing_if = "Option::is_none")]
290 pub checkpoint_content_hedge_delay_ms: Option<NonZeroU64>,
291}
292
293impl StateSyncConfig {
294 pub fn interval_period(&self) -> Duration {
295 const INTERVAL_PERIOD_MS: u64 = 5_000; Duration::from_millis(self.interval_period_ms.unwrap_or(INTERVAL_PERIOD_MS))
298 }
299
300 pub fn mailbox_capacity(&self) -> usize {
301 const MAILBOX_CAPACITY: usize = 1_024;
302
303 self.mailbox_capacity.unwrap_or(MAILBOX_CAPACITY)
304 }
305
306 pub fn synced_checkpoint_broadcast_channel_capacity(&self) -> usize {
307 const SYNCED_CHECKPOINT_BROADCAST_CHANNEL_CAPACITY: usize = 1_024;
308
309 self.synced_checkpoint_broadcast_channel_capacity
310 .unwrap_or(SYNCED_CHECKPOINT_BROADCAST_CHANNEL_CAPACITY)
311 }
312
313 pub fn checkpoint_header_download_concurrency(&self) -> usize {
314 const CHECKPOINT_HEADER_DOWNLOAD_CONCURRENCY: usize = 400;
315
316 self.checkpoint_header_download_concurrency
317 .unwrap_or(CHECKPOINT_HEADER_DOWNLOAD_CONCURRENCY)
318 }
319
320 pub fn checkpoint_content_download_concurrency(&self) -> usize {
321 const CHECKPOINT_CONTENT_DOWNLOAD_CONCURRENCY: usize = 400;
322
323 self.checkpoint_content_download_concurrency
324 .unwrap_or(CHECKPOINT_CONTENT_DOWNLOAD_CONCURRENCY)
325 }
326
327 pub fn checkpoint_content_download_tx_concurrency(&self) -> u64 {
328 const CHECKPOINT_CONTENT_DOWNLOAD_TX_CONCURRENCY: u64 = 50_000;
329
330 self.checkpoint_content_download_tx_concurrency
331 .unwrap_or(CHECKPOINT_CONTENT_DOWNLOAD_TX_CONCURRENCY)
332 }
333
334 pub fn timeout(&self) -> Duration {
335 const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
336
337 self.timeout_ms
338 .map(Duration::from_millis)
339 .unwrap_or(DEFAULT_TIMEOUT)
340 }
341
342 pub fn checkpoint_content_timeout(&self) -> Duration {
343 const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
344
345 self.checkpoint_content_timeout_ms
346 .map(Duration::from_millis)
347 .unwrap_or(DEFAULT_TIMEOUT)
348 }
349
350 pub fn wait_interval_when_no_peer_to_sync_content(&self) -> Duration {
351 self.wait_interval_when_no_peer_to_sync_content_ms
352 .map(Duration::from_millis)
353 .unwrap_or(self.default_wait_interval_when_no_peer_to_sync_content())
354 }
355
356 fn default_wait_interval_when_no_peer_to_sync_content(&self) -> Duration {
357 if cfg!(msim) {
358 Duration::from_secs(5)
359 } else {
360 Duration::from_secs(10)
361 }
362 }
363
364 pub fn use_get_checkpoint_contents_v2(&self) -> bool {
365 const DEFAULT_USE_GET_CHECKPOINT_CONTENTS_V2: bool = true;
366
367 self.use_get_checkpoint_contents_v2
368 .unwrap_or(DEFAULT_USE_GET_CHECKPOINT_CONTENTS_V2)
369 }
370
371 pub fn max_checkpoint_lookahead(&self) -> u64 {
372 const DEFAULT_MAX_CHECKPOINT_LOOKAHEAD: u64 = 1_000;
373
374 self.max_checkpoint_lookahead
375 .unwrap_or(DEFAULT_MAX_CHECKPOINT_LOOKAHEAD)
376 }
377
378 pub fn max_checkpoint_sync_batch_size(&self) -> u64 {
379 const DEFAULT_MAX_CHECKPOINT_SYNC_BATCH_SIZE: u64 = 400;
380
381 self.max_checkpoint_sync_batch_size
382 .unwrap_or(DEFAULT_MAX_CHECKPOINT_SYNC_BATCH_SIZE)
383 }
384
385 pub fn max_checkpoint_summary_size(&self) -> usize {
386 const DEFAULT_MAX_CHECKPOINT_SUMMARY_SIZE: usize = 256 * 1024; self.max_checkpoint_summary_size
389 .unwrap_or(DEFAULT_MAX_CHECKPOINT_SUMMARY_SIZE)
390 }
391
392 pub fn checkpoint_content_timeout_min(&self) -> Duration {
393 const DEFAULT: Duration = Duration::from_secs(5);
394 self.checkpoint_content_timeout_min_ms
395 .map(Duration::from_millis)
396 .unwrap_or(DEFAULT)
397 }
398
399 pub fn checkpoint_content_timeout_max(&self) -> Duration {
400 const DEFAULT: Duration = Duration::from_secs(30);
401 self.checkpoint_content_timeout_max_ms
402 .map(Duration::from_millis)
403 .unwrap_or(DEFAULT)
404 }
405
406 pub fn peer_scoring_window(&self) -> Duration {
407 const DEFAULT: Duration = Duration::from_secs(60);
408 self.peer_scoring_window_ms
409 .map(Duration::from_millis)
410 .unwrap_or(DEFAULT)
411 }
412
413 pub fn exploration_probability(&self) -> f64 {
414 const DEFAULT: f64 = 0.1;
415 let value = self.exploration_probability.unwrap_or(DEFAULT);
416 assert!(
417 (0.0..=1.0).contains(&value),
418 "exploration_probability must be in [0, 1], got {value}"
419 );
420 value
421 }
422
423 pub fn peer_failure_rate(&self) -> f64 {
424 const DEFAULT: f64 = 0.3;
425 let value = self.peer_failure_rate.unwrap_or(DEFAULT);
426 assert!(
427 (0.0..=1.0).contains(&value),
428 "peer_failure_rate must be in [0, 1], got {value}"
429 );
430 value
431 }
432
433 pub fn peer_disconnect_threshold(&self) -> Duration {
434 const DEFAULT_MS: u64 = 30_000; Duration::from_millis(self.peer_disconnect_threshold_ms.unwrap_or(DEFAULT_MS))
436 }
437
438 pub fn checkpoint_content_hedge_delay(&self) -> Option<Duration> {
439 self.checkpoint_content_hedge_delay_ms
440 .map(|delay_ms| Duration::from_millis(delay_ms.get()))
441 }
442
443 pub fn randomized_for_testing() -> Self {
444 use rand::Rng;
445 let mut rng = rand::thread_rng();
446 let config = Self {
447 mailbox_capacity: Some(rng.gen_range(16..=2048)),
448 synced_checkpoint_broadcast_channel_capacity: Some(rng.gen_range(16..=2048)),
449 checkpoint_header_download_concurrency: Some(rng.gen_range(10..=500)),
450 checkpoint_content_download_concurrency: Some(rng.gen_range(10..=500)),
451 max_checkpoint_lookahead: Some(rng.gen_range(100..=2000)),
452 max_checkpoint_sync_batch_size: Some(rng.gen_range(100..=500)),
453 max_checkpoint_summary_size: Some(rng.gen_range(64 * 1024..=512 * 1024)),
454 ..Default::default()
455 };
456 tracing::info!(
457 mailbox_capacity = config.mailbox_capacity.unwrap(),
458 broadcast_capacity = config.synced_checkpoint_broadcast_channel_capacity.unwrap(),
459 header_concurrency = config.checkpoint_header_download_concurrency.unwrap(),
460 content_concurrency = config.checkpoint_content_download_concurrency.unwrap(),
461 lookahead = config.max_checkpoint_lookahead.unwrap(),
462 batch_size = config.max_checkpoint_sync_batch_size.unwrap(),
463 summary_size = config.max_checkpoint_summary_size.unwrap(),
464 "StateSyncConfig::randomized_for_testing"
465 );
466 config
467 }
468}
469
470#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
485pub enum AccessType {
486 Public,
487 Private,
488 Trusted,
489}
490
491#[derive(Clone, Debug, Default, Deserialize, Serialize)]
492#[serde(rename_all = "kebab-case")]
493pub struct DiscoveryConfig {
494 #[serde(skip_serializing_if = "Option::is_none")]
498 pub interval_period_ms: Option<u64>,
499
500 #[serde(skip_serializing_if = "Option::is_none")]
504 pub target_concurrent_connections: Option<usize>,
505
506 #[serde(skip_serializing_if = "Option::is_none")]
513 pub peers_to_query: Option<usize>,
514
515 #[serde(skip_serializing_if = "Option::is_none")]
519 pub get_known_peers_rate_limit: Option<NonZeroU32>,
520
521 #[serde(skip_serializing_if = "Option::is_none")]
523 pub access_type: Option<AccessType>,
524
525 #[serde(skip_serializing_if = "Vec::is_empty", default)]
533 pub allowlisted_peers: Vec<AllowlistedPeer>,
534
535 #[serde(skip_serializing_if = "Option::is_none")]
539 pub mailbox_capacity: Option<usize>,
540
541 #[serde(skip_serializing_if = "Option::is_none")]
545 pub use_get_known_peers_v3: Option<bool>,
546
547 #[serde(skip_serializing_if = "Option::is_none")]
552 pub peer_addr_store_path: Option<PathBuf>,
553
554 #[serde(skip_serializing_if = "Option::is_none")]
559 pub peer_failure_cooldown_ms: Option<u64>,
560
561 #[serde(skip_serializing_if = "Option::is_none")]
565 pub min_peers_for_disconnect: Option<usize>,
566}
567
568impl DiscoveryConfig {
569 pub fn interval_period(&self) -> Duration {
570 const INTERVAL_PERIOD_MS: u64 = 5_000; Duration::from_millis(self.interval_period_ms.unwrap_or(INTERVAL_PERIOD_MS))
573 }
574
575 pub fn target_concurrent_connections(&self) -> usize {
576 const TARGET_CONCURRENT_CONNECTIONS: usize = 4;
577
578 self.target_concurrent_connections
579 .unwrap_or(TARGET_CONCURRENT_CONNECTIONS)
580 }
581
582 pub fn peers_to_query(&self) -> usize {
583 const PEERS_TO_QUERY: usize = 1;
584
585 self.peers_to_query.unwrap_or(PEERS_TO_QUERY)
586 }
587
588 pub fn access_type(&self) -> AccessType {
589 self.access_type.unwrap_or(AccessType::Public)
591 }
592
593 pub fn mailbox_capacity(&self) -> usize {
594 const MAILBOX_CAPACITY: usize = 2_048;
598
599 self.mailbox_capacity.unwrap_or(MAILBOX_CAPACITY)
600 }
601
602 pub fn use_get_known_peers_v3(&self) -> bool {
603 self.use_get_known_peers_v3.unwrap_or(false)
604 }
605
606 pub fn peer_failure_cooldown(&self) -> Duration {
607 const DEFAULT_MS: u64 = 3_600_000; Duration::from_millis(self.peer_failure_cooldown_ms.unwrap_or(DEFAULT_MS))
609 }
610
611 pub fn min_peers_for_disconnect(&self) -> usize {
612 const DEFAULT: usize = 2;
613 self.min_peers_for_disconnect.unwrap_or(DEFAULT)
614 }
615}
616
617#[derive(Clone, Debug, Default, Deserialize, Serialize)]
618#[serde(rename_all = "kebab-case")]
619pub struct RandomnessConfig {
620 #[serde(skip_serializing_if = "Option::is_none")]
625 pub max_partial_sigs_rounds_ahead: Option<u64>,
626
627 #[serde(skip_serializing_if = "Option::is_none")]
631 pub max_partial_sigs_concurrent_sends: Option<usize>,
632
633 #[serde(skip_serializing_if = "Option::is_none")]
637 pub partial_signature_retry_interval_ms: Option<u64>,
638
639 #[serde(skip_serializing_if = "Option::is_none")]
644 pub mailbox_capacity: Option<usize>,
645
646 #[serde(skip_serializing_if = "Option::is_none")]
650 pub send_partial_signatures_inflight_limit: Option<usize>,
651
652 #[serde(skip_serializing_if = "Option::is_none")]
656 pub max_ignored_peer_weight_factor: Option<f64>,
657}
658
659impl RandomnessConfig {
660 pub fn max_partial_sigs_rounds_ahead(&self) -> u64 {
661 const MAX_PARTIAL_SIGS_ROUNDS_AHEAD: u64 = 50;
662
663 self.max_partial_sigs_rounds_ahead
664 .unwrap_or(MAX_PARTIAL_SIGS_ROUNDS_AHEAD)
665 }
666
667 pub fn max_partial_sigs_concurrent_sends(&self) -> usize {
668 const MAX_PARTIAL_SIGS_CONCURRENT_SENDS: usize = 20;
669
670 self.max_partial_sigs_concurrent_sends
671 .unwrap_or(MAX_PARTIAL_SIGS_CONCURRENT_SENDS)
672 }
673 pub fn partial_signature_retry_interval(&self) -> Duration {
674 const PARTIAL_SIGNATURE_RETRY_INTERVAL: u64 = 5_000; Duration::from_millis(
677 self.partial_signature_retry_interval_ms
678 .unwrap_or(PARTIAL_SIGNATURE_RETRY_INTERVAL),
679 )
680 }
681
682 pub fn mailbox_capacity(&self) -> usize {
683 const MAILBOX_CAPACITY: usize = 1_000_000;
684
685 self.mailbox_capacity.unwrap_or(MAILBOX_CAPACITY)
686 }
687
688 pub fn send_partial_signatures_inflight_limit(&self) -> usize {
689 const SEND_PARTIAL_SIGNATURES_INFLIGHT_LIMIT: usize = 20;
690
691 self.send_partial_signatures_inflight_limit
692 .unwrap_or(SEND_PARTIAL_SIGNATURES_INFLIGHT_LIMIT)
693 }
694
695 pub fn max_ignored_peer_weight_factor(&self) -> f64 {
696 const MAX_IGNORED_PEER_WEIGHT_FACTOR: f64 = 0.2;
697
698 self.max_ignored_peer_weight_factor
699 .unwrap_or(MAX_IGNORED_PEER_WEIGHT_FACTOR)
700 }
701}