Skip to main content

sui_config/
p2p.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    /// The address that the p2p network will bind on.
21    #[serde(default = "default_listen_address")]
22    pub listen_address: SocketAddr,
23    /// The external address other nodes can use to reach this node.
24    /// This will be shared with other peers through the discovery service
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub external_address: Option<Multiaddr>,
27    /// SeedPeers are preferred and the node will always try to ensure a
28    /// connection is established with these nodes.
29    #[serde(skip_serializing_if = "Vec::is_empty", default)]
30    pub seed_peers: Vec<SeedPeer>,
31    /// Manually configured peer addresses. These override addresses loaded from on-chain configs.
32    #[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    /// Size in bytes above which network messages are considered excessively large. Excessively
43    /// large messages will still be handled, but logged and reported in metrics for debugging.
44    ///
45    /// If unspecified, this will default to 8 MiB.
46    #[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    /// List of "known-good" checkpoints that state sync will be forced to use. State sync will
111    /// skip verification of pinned checkpoints, and reject checkpoints with digests that don't
112    /// match pinned values for a given sequence number.
113    ///
114    /// This can be used:
115    /// - in case of a fork, to prevent the node from syncing to the wrong chain.
116    /// - in case of a network stall, to force the node to proceed with a manually-injected
117    ///   checkpoint.
118    #[serde(skip_serializing_if = "Vec::is_empty", default)]
119    pub pinned_checkpoints: Vec<(CheckpointSequenceNumber, CheckpointDigest)>,
120
121    /// Query peers for their latest checkpoint every interval period.
122    ///
123    /// If unspecified, this will default to `5,000` milliseconds.
124    #[serde(skip_serializing_if = "Option::is_none")]
125    pub interval_period_ms: Option<u64>,
126
127    /// Size of the StateSync actor's mailbox.
128    ///
129    /// If unspecified, this will default to `1,024`.
130    #[serde(skip_serializing_if = "Option::is_none")]
131    pub mailbox_capacity: Option<usize>,
132
133    /// Size of the broadcast channel use for notifying other systems of newly sync'ed checkpoints.
134    ///
135    /// If unspecified, this will default to `1,024`.
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub synced_checkpoint_broadcast_channel_capacity: Option<usize>,
138
139    /// Set the upper bound on the number of checkpoint headers to be downloaded concurrently.
140    ///
141    /// If unspecified, this will default to `400`.
142    #[serde(skip_serializing_if = "Option::is_none")]
143    pub checkpoint_header_download_concurrency: Option<usize>,
144
145    /// Set the upper bound on the number of checkpoint contents to be downloaded concurrently.
146    ///
147    /// If unspecified, this will default to `400`.
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub checkpoint_content_download_concurrency: Option<usize>,
150
151    /// Set the upper bound on the number of individual transactions contained in checkpoint
152    /// contents to be downloaded concurrently. If both this value and
153    /// `checkpoint_content_download_concurrency` are set, the lower of the two will apply.
154    ///
155    /// If unspecified, this will default to `50,000`.
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub checkpoint_content_download_tx_concurrency: Option<u64>,
158
159    /// Set the timeout that should be used when sending most state-sync RPC requests.
160    ///
161    /// If unspecified, this will default to `10,000` milliseconds.
162    #[serde(skip_serializing_if = "Option::is_none")]
163    pub timeout_ms: Option<u64>,
164
165    /// Set the timeout that should be used when sending RPC requests to sync checkpoint contents.
166    ///
167    /// If unspecified, this will default to `10,000` milliseconds.
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub checkpoint_content_timeout_ms: Option<u64>,
170
171    /// Per-peer rate-limit (in requests/sec) for the PushCheckpointSummary RPC.
172    ///
173    /// If unspecified, this will default to no limit.
174    #[serde(skip_serializing_if = "Option::is_none")]
175    pub push_checkpoint_summary_rate_limit: Option<NonZeroU32>,
176
177    /// Per-peer rate-limit (in requests/sec) for the GetCheckpointSummary RPC.
178    ///
179    /// If unspecified, this will default to no limit.
180    #[serde(skip_serializing_if = "Option::is_none")]
181    pub get_checkpoint_summary_rate_limit: Option<NonZeroU32>,
182
183    /// Per-peer rate-limit (in requests/sec) for the GetCheckpointContents RPC.
184    ///
185    /// If unspecified, this will default to no limit.
186    #[serde(skip_serializing_if = "Option::is_none")]
187    pub get_checkpoint_contents_rate_limit: Option<NonZeroU32>,
188
189    /// Per-peer inflight limit for the GetCheckpointContents RPC.
190    ///
191    /// If unspecified, this will default to no limit.
192    #[serde(skip_serializing_if = "Option::is_none")]
193    pub get_checkpoint_contents_inflight_limit: Option<usize>,
194
195    /// Per-checkpoint inflight limit for the GetCheckpointContents RPC. This is enforced globally
196    /// across all peers.
197    ///
198    /// If unspecified, this will default to no limit.
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub get_checkpoint_contents_per_checkpoint_limit: Option<usize>,
201
202    /// The amount of time to wait before retry if there are no peers to sync content from.
203    /// If unspecified, this will set to default value
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub wait_interval_when_no_peer_to_sync_content_ms: Option<u64>,
206
207    /// If true, the v2 version of get_checkpoint_contents will be used.
208    ///
209    /// If unspecified, this will default to true.
210    #[serde(skip_serializing_if = "Option::is_none")]
211    pub use_get_checkpoint_contents_v2: Option<bool>,
212
213    /// Maximum lookahead (in checkpoint sequence numbers) for storing unverified checkpoint
214    /// summaries received via PushCheckpointSummary.
215    ///
216    /// If unspecified, this will default to `1,000`.
217    #[serde(skip_serializing_if = "Option::is_none")]
218    pub max_checkpoint_lookahead: Option<u64>,
219
220    /// Maximum number of checkpoint headers to attempt to sync in a single sync task.
221    ///
222    /// If unspecified, this will default to `400`.
223    #[serde(skip_serializing_if = "Option::is_none")]
224    pub max_checkpoint_sync_batch_size: Option<u64>,
225
226    /// Maximum serialized size in bytes for a CertifiedCheckpointSummary received via
227    /// PushCheckpointSummary.
228    ///
229    /// If unspecified, this will default to `262,144` (256 KiB).
230    #[serde(skip_serializing_if = "Option::is_none")]
231    pub max_checkpoint_summary_size: Option<usize>,
232
233    /// Minimum timeout for checkpoint content downloads (adaptive timeout lower bound).
234    ///
235    /// If unspecified, this will default to `10,000` milliseconds.
236    #[serde(skip_serializing_if = "Option::is_none")]
237    pub checkpoint_content_timeout_min_ms: Option<u64>,
238
239    /// Maximum timeout for checkpoint content downloads (adaptive timeout upper bound).
240    ///
241    /// If unspecified, this will default to `30,000` milliseconds.
242    #[serde(skip_serializing_if = "Option::is_none")]
243    pub checkpoint_content_timeout_max_ms: Option<u64>,
244
245    /// Time window for peer scoring samples.
246    ///
247    /// If unspecified, this will default to `60,000` milliseconds.
248    #[serde(skip_serializing_if = "Option::is_none")]
249    pub peer_scoring_window_ms: Option<u64>,
250
251    /// Probability of selecting an unknown peer to explore its throughput.
252    ///
253    /// If unspecified, this will default to `0.1`.
254    #[serde(skip_serializing_if = "Option::is_none")]
255    pub exploration_probability: Option<f64>,
256
257    /// Failure rate threshold for marking a peer as failing.
258    /// A peer is marked as failing if its failure rate exceeds this threshold.
259    ///
260    /// If unspecified, this will default to `0.3` (30%).
261    #[serde(skip_serializing_if = "Option::is_none")]
262    pub peer_failure_rate: Option<f64>,
263
264    /// Duration in milliseconds that a peer must be continuously failing before it is
265    /// reported to discovery for disconnection. Set to 0 to disable automatic reporting
266    /// of failing peers.
267    ///
268    /// If unspecified, this will default to `30,000` (30 seconds).
269    #[serde(skip_serializing_if = "Option::is_none")]
270    pub peer_disconnect_threshold_ms: Option<u64>,
271
272    /// Turns on hedged checkpoint contents downloads, with this delay as the hedge delay. A request
273    /// with no valid response after the hedge delay is also sent to another peer that has the
274    /// checkpoint; the first valid response wins, and the other request is cancelled. At most two
275    /// requests per checkpoint are in flight, and only checkpoints close to the next one to be
276    /// released in order are hedged. Near that checkpoint, a download whose requests have all
277    /// failed keeps looking for a peer that has the checkpoint, for up to
278    /// `wait_interval_when_no_peer_to_sync_content_ms`, instead of sleeping that long.
279    ///
280    /// Near that checkpoint, requests also go to the peers expected to answer soonest, based on
281    /// each peer's recent response times, and a request is hedged right away when another peer is
282    /// expected to answer much sooner. A request to a peer that has often been slow from this node
283    /// is hedged sooner than the hedge delay, or right away once a peer that isn't slow has the
284    /// checkpoint. Hedges add download traffic, mostly on nodes with a slow network path to some
285    /// of their peers. The exact rules are documented in `sui_network::state_sync`.
286    ///
287    /// If unspecified, hedging is disabled: the peers that had announced the checkpoint when the
288    /// download started are tried one at a time.
289    #[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; // 5 seconds
296
297        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; // 256 KiB
387
388        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; // 30 seconds
435        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/// Access Type of a node.
471/// AccessType info is shared in the discovery process.
472/// * If the node marks itself as Public, other nodes may try to connect to it.
473/// * If the node marks itself as Private, only nodes that have it in
474///   their `allowlisted_peers` or `seed_peers` will try to connect to it. The
475///   node's info will not be shared through discovery.
476/// * Trusted is the same as Private, except it allows sharing the node's info
477///   only to other preconfigured peers (i.e. those in `allowlisted_peers` and
478///   `seed_peers`, or active Sui validators).
479/// * If not set, defaults to Public.
480///
481/// AccessType is useful when a network of nodes want to stay private. To achieve this,
482/// mark every node in this network as `Private` or `Trusted`, and allowlist/seed them
483/// to each other.
484#[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    /// Query peers for their latest checkpoint every interval period.
495    ///
496    /// If unspecified, this will default to `5,000` milliseconds.
497    #[serde(skip_serializing_if = "Option::is_none")]
498    pub interval_period_ms: Option<u64>,
499
500    /// Target number of concurrent connections to establish.
501    ///
502    /// If unspecified, this will default to `4`.
503    #[serde(skip_serializing_if = "Option::is_none")]
504    pub target_concurrent_connections: Option<usize>,
505
506    /// Number of peers to query each interval.
507    ///
508    /// Sets the number of peers, to be randomly selected, that are queried for their known peers
509    /// each interval.
510    ///
511    /// If unspecified, this will default to `1`.
512    #[serde(skip_serializing_if = "Option::is_none")]
513    pub peers_to_query: Option<usize>,
514
515    /// Per-peer rate-limit (in requests/sec) for the GetKnownPeers RPC.
516    ///
517    /// If unspecified, this will default to no limit.
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub get_known_peers_rate_limit: Option<NonZeroU32>,
520
521    /// See docstring for `AccessType`.
522    #[serde(skip_serializing_if = "Option::is_none")]
523    pub access_type: Option<AccessType>,
524
525    /// Like `seed_peers` in `P2pConfig`, allowlisted peers will always be allowed to establish
526    /// connection with this node regardless of the concurrency limit.
527    /// Unlike `seed_peers`, a node does not reach out to `allowlisted_peers` preferentially.
528    /// It is also used to determine if a peer is accessible when its AccessType is Private.
529    /// For example, a node will ignore a peer with Private AccessType if the peer is not in
530    /// its `allowlisted_peers`. Namely, the node will not try to establish connections
531    /// to this peer, nor advertise this peer's info to other peers in the network.
532    #[serde(skip_serializing_if = "Vec::is_empty", default)]
533    pub allowlisted_peers: Vec<AllowlistedPeer>,
534
535    /// Size of the Discovery loop's mailbox.
536    ///
537    /// If unspecified, this will default to `1,024`.
538    #[serde(skip_serializing_if = "Option::is_none")]
539    pub mailbox_capacity: Option<usize>,
540
541    /// Use get_known_peers_v3 RPC to fetch peer info.
542    ///
543    /// If unspecified, this will default to `false`.
544    #[serde(skip_serializing_if = "Option::is_none")]
545    pub use_get_known_peers_v3: Option<bool>,
546
547    /// Path to store discovered peer addresses across restarts.
548    ///
549    /// If unspecified, defaults to `<db_path>/discovery_peer_cache.yaml` when
550    /// started via `SuiNode`.
551    #[serde(skip_serializing_if = "Option::is_none")]
552    pub peer_addr_store_path: Option<PathBuf>,
553
554    /// Duration in milliseconds to cooldown a peer after a failure report from state-sync.
555    /// During cooldown, the peer is deprioritized for new connections.
556    ///
557    /// If unspecified, this will default to `3,600,000` (1 hour).
558    #[serde(skip_serializing_if = "Option::is_none")]
559    pub peer_failure_cooldown_ms: Option<u64>,
560
561    /// Minimum number of connected peers below which no failing-peer disconnect will occur.
562    ///
563    /// If unspecified, this will default to `2`.
564    #[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; // 5 seconds
571
572        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        // defaults None to Public
590        self.access_type.unwrap_or(AccessType::Public)
591    }
592
593    pub fn mailbox_capacity(&self) -> usize {
594        // Sized with headroom for bursts of PeerAddressChange messages: a
595        // single GetKnownPeers response can enqueue one message per trusted
596        // peer, and multiple concurrent query tasks can stack their batches.
597        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; // 1 hour
608        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    /// Maximum number of rounds ahead of our most recent completed round for which we should
621    /// accept partial signatures from other validators.
622    ///
623    /// If unspecified, this will default to 50.
624    #[serde(skip_serializing_if = "Option::is_none")]
625    pub max_partial_sigs_rounds_ahead: Option<u64>,
626
627    /// Maximum number of rounds for which partial signatures should be concurrently sent.
628    ///
629    /// If unspecified, this will default to 20.
630    #[serde(skip_serializing_if = "Option::is_none")]
631    pub max_partial_sigs_concurrent_sends: Option<usize>,
632
633    /// Interval at which to retry sending partial signatures until the round is complete.
634    ///
635    /// If unspecified, this will default to `5,000` milliseconds.
636    #[serde(skip_serializing_if = "Option::is_none")]
637    pub partial_signature_retry_interval_ms: Option<u64>,
638
639    /// Size of the Randomness actor's mailbox. This should be set large enough to never
640    /// overflow unless a bug is encountered.
641    ///
642    /// If unspecified, this will default to `1,000,000`.
643    #[serde(skip_serializing_if = "Option::is_none")]
644    pub mailbox_capacity: Option<usize>,
645
646    /// Per-peer inflight limit for the SendPartialSignatures RPC.
647    ///
648    /// If unspecified, this will default to 20.
649    #[serde(skip_serializing_if = "Option::is_none")]
650    pub send_partial_signatures_inflight_limit: Option<usize>,
651
652    /// Maximum proportion of total peer weight to ignore in case of byzantine behavior.
653    ///
654    /// If unspecified, this will default to 0.2.
655    #[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; // 5 seconds
675
676        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}