sui_config/
p2p.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{net::SocketAddr, num::NonZeroU32, time::Duration};
5
6use serde::{Deserialize, Serialize};
7use sui_types::{
8    messages_checkpoint::{CheckpointDigest, CheckpointSequenceNumber},
9    multiaddr::Multiaddr,
10};
11
12#[derive(Clone, Debug, Deserialize, Serialize)]
13#[serde(rename_all = "kebab-case")]
14pub struct P2pConfig {
15    /// The address that the p2p network will bind on.
16    #[serde(default = "default_listen_address")]
17    pub listen_address: SocketAddr,
18    /// The external address other nodes can use to reach this node.
19    /// This will be shared with other peers through the discovery service
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub external_address: Option<Multiaddr>,
22    /// SeedPeers are preferred and the node will always try to ensure a
23    /// connection is established with these nodes.
24    #[serde(skip_serializing_if = "Vec::is_empty", default)]
25    pub seed_peers: Vec<SeedPeer>,
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub anemo_config: Option<anemo::Config>,
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub state_sync: Option<StateSyncConfig>,
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub discovery: Option<DiscoveryConfig>,
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub randomness: Option<RandomnessConfig>,
34    /// Size in bytes above which network messages are considered excessively large. Excessively
35    /// large messages will still be handled, but logged and reported in metrics for debugging.
36    ///
37    /// If unspecified, this will default to 8 MiB.
38    #[serde(skip_serializing_if = "Option::is_none")]
39    pub excessive_message_size: Option<usize>,
40}
41
42fn default_listen_address() -> SocketAddr {
43    "0.0.0.0:8084".parse().unwrap()
44}
45
46impl Default for P2pConfig {
47    fn default() -> Self {
48        Self {
49            listen_address: default_listen_address(),
50            external_address: Default::default(),
51            seed_peers: Default::default(),
52            anemo_config: Default::default(),
53            state_sync: None,
54            discovery: None,
55            randomness: None,
56            excessive_message_size: None,
57        }
58    }
59}
60
61impl P2pConfig {
62    pub fn excessive_message_size(&self) -> usize {
63        const EXCESSIVE_MESSAGE_SIZE: usize = 32 << 20;
64
65        self.excessive_message_size
66            .unwrap_or(EXCESSIVE_MESSAGE_SIZE)
67    }
68
69    pub fn set_discovery_config(mut self, discovery_config: DiscoveryConfig) -> Self {
70        self.discovery = Some(discovery_config);
71        self
72    }
73}
74
75#[derive(Clone, Debug, Deserialize, Serialize)]
76#[serde(rename_all = "kebab-case")]
77pub struct SeedPeer {
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub peer_id: Option<anemo::PeerId>,
80    pub address: Multiaddr,
81}
82
83#[derive(Clone, Debug, Deserialize, Serialize)]
84#[serde(rename_all = "kebab-case")]
85pub struct AllowlistedPeer {
86    pub peer_id: anemo::PeerId,
87    #[serde(skip_serializing_if = "Option::is_none")]
88    pub address: Option<Multiaddr>,
89}
90
91#[derive(Clone, Debug, Default, Deserialize, Serialize)]
92#[serde(rename_all = "kebab-case")]
93pub struct StateSyncConfig {
94    /// List of "known-good" checkpoints that state sync will be forced to use. State sync will
95    /// skip verification of pinned checkpoints, and reject checkpoints with digests that don't
96    /// match pinned values for a given sequence number.
97    ///
98    /// This can be used:
99    /// - in case of a fork, to prevent the node from syncing to the wrong chain.
100    /// - in case of a network stall, to force the node to proceed with a manually-injected
101    ///   checkpoint.
102    #[serde(skip_serializing_if = "Vec::is_empty", default)]
103    pub pinned_checkpoints: Vec<(CheckpointSequenceNumber, CheckpointDigest)>,
104
105    /// Query peers for their latest checkpoint every interval period.
106    ///
107    /// If unspecified, this will default to `5,000` milliseconds.
108    #[serde(skip_serializing_if = "Option::is_none")]
109    pub interval_period_ms: Option<u64>,
110
111    /// Size of the StateSync actor's mailbox.
112    ///
113    /// If unspecified, this will default to `1,024`.
114    #[serde(skip_serializing_if = "Option::is_none")]
115    pub mailbox_capacity: Option<usize>,
116
117    /// Size of the broadcast channel use for notifying other systems of newly sync'ed checkpoints.
118    ///
119    /// If unspecified, this will default to `1,024`.
120    #[serde(skip_serializing_if = "Option::is_none")]
121    pub synced_checkpoint_broadcast_channel_capacity: Option<usize>,
122
123    /// Set the upper bound on the number of checkpoint headers to be downloaded concurrently.
124    ///
125    /// If unspecified, this will default to `400`.
126    #[serde(skip_serializing_if = "Option::is_none")]
127    pub checkpoint_header_download_concurrency: Option<usize>,
128
129    /// Set the upper bound on the number of checkpoint contents to be downloaded concurrently.
130    ///
131    /// If unspecified, this will default to `400`.
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub checkpoint_content_download_concurrency: Option<usize>,
134
135    /// Set the upper bound on the number of individual transactions contained in checkpoint
136    /// contents to be downloaded concurrently. If both this value and
137    /// `checkpoint_content_download_concurrency` are set, the lower of the two will apply.
138    ///
139    /// If unspecified, this will default to `50,000`.
140    #[serde(skip_serializing_if = "Option::is_none")]
141    pub checkpoint_content_download_tx_concurrency: Option<u64>,
142
143    /// Set the timeout that should be used when sending most state-sync RPC requests.
144    ///
145    /// If unspecified, this will default to `10,000` milliseconds.
146    #[serde(skip_serializing_if = "Option::is_none")]
147    pub timeout_ms: Option<u64>,
148
149    /// Set the timeout that should be used when sending RPC requests to sync checkpoint contents.
150    ///
151    /// If unspecified, this will default to `10,000` milliseconds.
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub checkpoint_content_timeout_ms: Option<u64>,
154
155    /// Per-peer rate-limit (in requests/sec) for the PushCheckpointSummary RPC.
156    ///
157    /// If unspecified, this will default to no limit.
158    #[serde(skip_serializing_if = "Option::is_none")]
159    pub push_checkpoint_summary_rate_limit: Option<NonZeroU32>,
160
161    /// Per-peer rate-limit (in requests/sec) for the GetCheckpointSummary RPC.
162    ///
163    /// If unspecified, this will default to no limit.
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub get_checkpoint_summary_rate_limit: Option<NonZeroU32>,
166
167    /// Per-peer rate-limit (in requests/sec) for the GetCheckpointContents RPC.
168    ///
169    /// If unspecified, this will default to no limit.
170    #[serde(skip_serializing_if = "Option::is_none")]
171    pub get_checkpoint_contents_rate_limit: Option<NonZeroU32>,
172
173    /// Per-peer inflight limit for the GetCheckpointContents RPC.
174    ///
175    /// If unspecified, this will default to no limit.
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub get_checkpoint_contents_inflight_limit: Option<usize>,
178
179    /// Per-checkpoint inflight limit for the GetCheckpointContents RPC. This is enforced globally
180    /// across all peers.
181    ///
182    /// If unspecified, this will default to no limit.
183    #[serde(skip_serializing_if = "Option::is_none")]
184    pub get_checkpoint_contents_per_checkpoint_limit: Option<usize>,
185
186    /// The amount of time to wait before retry if there are no peers to sync content from.
187    /// If unspecified, this will set to default value
188    #[serde(skip_serializing_if = "Option::is_none")]
189    pub wait_interval_when_no_peer_to_sync_content_ms: Option<u64>,
190
191    /// If true, the v2 version of get_checkpoint_contents will be used.
192    ///
193    /// If unspecified, this will default to false.
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub use_get_checkpoint_contents_v2: Option<bool>,
196}
197
198impl StateSyncConfig {
199    pub fn interval_period(&self) -> Duration {
200        const INTERVAL_PERIOD_MS: u64 = 5_000; // 5 seconds
201
202        Duration::from_millis(self.interval_period_ms.unwrap_or(INTERVAL_PERIOD_MS))
203    }
204
205    pub fn mailbox_capacity(&self) -> usize {
206        const MAILBOX_CAPACITY: usize = 1_024;
207
208        self.mailbox_capacity.unwrap_or(MAILBOX_CAPACITY)
209    }
210
211    pub fn synced_checkpoint_broadcast_channel_capacity(&self) -> usize {
212        const SYNCED_CHECKPOINT_BROADCAST_CHANNEL_CAPACITY: usize = 1_024;
213
214        self.synced_checkpoint_broadcast_channel_capacity
215            .unwrap_or(SYNCED_CHECKPOINT_BROADCAST_CHANNEL_CAPACITY)
216    }
217
218    pub fn checkpoint_header_download_concurrency(&self) -> usize {
219        const CHECKPOINT_HEADER_DOWNLOAD_CONCURRENCY: usize = 400;
220
221        self.checkpoint_header_download_concurrency
222            .unwrap_or(CHECKPOINT_HEADER_DOWNLOAD_CONCURRENCY)
223    }
224
225    pub fn checkpoint_content_download_concurrency(&self) -> usize {
226        const CHECKPOINT_CONTENT_DOWNLOAD_CONCURRENCY: usize = 400;
227
228        self.checkpoint_content_download_concurrency
229            .unwrap_or(CHECKPOINT_CONTENT_DOWNLOAD_CONCURRENCY)
230    }
231
232    pub fn checkpoint_content_download_tx_concurrency(&self) -> u64 {
233        const CHECKPOINT_CONTENT_DOWNLOAD_TX_CONCURRENCY: u64 = 50_000;
234
235        self.checkpoint_content_download_tx_concurrency
236            .unwrap_or(CHECKPOINT_CONTENT_DOWNLOAD_TX_CONCURRENCY)
237    }
238
239    pub fn timeout(&self) -> Duration {
240        const DEFAULT_TIMEOUT: Duration = Duration::from_secs(10);
241
242        self.timeout_ms
243            .map(Duration::from_millis)
244            .unwrap_or(DEFAULT_TIMEOUT)
245    }
246
247    pub fn checkpoint_content_timeout(&self) -> Duration {
248        const DEFAULT_TIMEOUT: Duration = Duration::from_secs(60);
249
250        self.checkpoint_content_timeout_ms
251            .map(Duration::from_millis)
252            .unwrap_or(DEFAULT_TIMEOUT)
253    }
254
255    pub fn wait_interval_when_no_peer_to_sync_content(&self) -> Duration {
256        self.wait_interval_when_no_peer_to_sync_content_ms
257            .map(Duration::from_millis)
258            .unwrap_or(self.default_wait_interval_when_no_peer_to_sync_content())
259    }
260
261    fn default_wait_interval_when_no_peer_to_sync_content(&self) -> Duration {
262        if cfg!(msim) {
263            Duration::from_secs(5)
264        } else {
265            Duration::from_secs(10)
266        }
267    }
268
269    pub fn use_get_checkpoint_contents_v2(&self) -> bool {
270        const DEFAULT_USE_GET_CHECKPOINT_CONTENTS_V2: bool = false;
271
272        self.use_get_checkpoint_contents_v2
273            .unwrap_or(DEFAULT_USE_GET_CHECKPOINT_CONTENTS_V2)
274    }
275}
276
277/// Access Type of a node.
278/// AccessType info is shared in the discovery process.
279/// * If the node marks itself as Public, other nodes may try to connect to it.
280/// * If the node marks itself as Private, only nodes that have it in
281///   their `allowlisted_peers` or `seed_peers` will try to connect to it.
282/// * If not set, defaults to Public.
283///
284/// AccessType is useful when a network of nodes want to stay private. To achieve this,
285/// mark every node in this network as `Private` and allowlist/seed them to each other.
286#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
287pub enum AccessType {
288    Public,
289    Private,
290}
291
292#[derive(Clone, Debug, Default, Deserialize, Serialize)]
293#[serde(rename_all = "kebab-case")]
294pub struct DiscoveryConfig {
295    /// Query peers for their latest checkpoint every interval period.
296    ///
297    /// If unspecified, this will default to `5,000` milliseconds.
298    #[serde(skip_serializing_if = "Option::is_none")]
299    pub interval_period_ms: Option<u64>,
300
301    /// Target number of concurrent connections to establish.
302    ///
303    /// If unspecified, this will default to `4`.
304    #[serde(skip_serializing_if = "Option::is_none")]
305    pub target_concurrent_connections: Option<usize>,
306
307    /// Number of peers to query each interval.
308    ///
309    /// Sets the number of peers, to be randomly selected, that are queried for their known peers
310    /// each interval.
311    ///
312    /// If unspecified, this will default to `1`.
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub peers_to_query: Option<usize>,
315
316    /// Per-peer rate-limit (in requests/sec) for the GetKnownPeers RPC.
317    ///
318    /// If unspecified, this will default to no limit.
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub get_known_peers_rate_limit: Option<NonZeroU32>,
321
322    /// See docstring for `AccessType`.
323    #[serde(skip_serializing_if = "Option::is_none")]
324    pub access_type: Option<AccessType>,
325
326    /// Like `seed_peers` in `P2pConfig`, allowlisted peers will awlays be allowed to establish
327    /// connection with this node regardless of the concurrency limit.
328    /// Unlike `seed_peers`, a node does not reach out to `allowlisted_peers` preferentially.
329    /// It is also used to determine if a peer is accessible when its AccessType is Private.
330    /// For example, a node will ignore a peer with Private AccessType if the peer is not in
331    /// its `allowlisted_peers`. Namely, the node will not try to establish connections
332    /// to this peer, nor advertise this peer's info to other peers in the network.
333    #[serde(skip_serializing_if = "Vec::is_empty", default)]
334    pub allowlisted_peers: Vec<AllowlistedPeer>,
335}
336
337impl DiscoveryConfig {
338    pub fn interval_period(&self) -> Duration {
339        const INTERVAL_PERIOD_MS: u64 = 5_000; // 5 seconds
340
341        Duration::from_millis(self.interval_period_ms.unwrap_or(INTERVAL_PERIOD_MS))
342    }
343
344    pub fn target_concurrent_connections(&self) -> usize {
345        const TARGET_CONCURRENT_CONNECTIONS: usize = 4;
346
347        self.target_concurrent_connections
348            .unwrap_or(TARGET_CONCURRENT_CONNECTIONS)
349    }
350
351    pub fn peers_to_query(&self) -> usize {
352        const PEERS_TO_QUERY: usize = 1;
353
354        self.peers_to_query.unwrap_or(PEERS_TO_QUERY)
355    }
356
357    pub fn access_type(&self) -> AccessType {
358        // defaults None to Public
359        self.access_type.unwrap_or(AccessType::Public)
360    }
361}
362
363#[derive(Clone, Debug, Default, Deserialize, Serialize)]
364#[serde(rename_all = "kebab-case")]
365pub struct RandomnessConfig {
366    /// Maximum number of rounds ahead of our most recent completed round for which we should
367    /// accept partial signatures from other validators.
368    ///
369    /// If unspecified, this will default to 50.
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub max_partial_sigs_rounds_ahead: Option<u64>,
372
373    /// Maximum number of rounds for which partial signatures should be concurrently sent.
374    ///
375    /// If unspecified, this will default to 20.
376    #[serde(skip_serializing_if = "Option::is_none")]
377    pub max_partial_sigs_concurrent_sends: Option<usize>,
378
379    /// Interval at which to retry sending partial signatures until the round is complete.
380    ///
381    /// If unspecified, this will default to `5,000` milliseconds.
382    #[serde(skip_serializing_if = "Option::is_none")]
383    pub partial_signature_retry_interval_ms: Option<u64>,
384
385    /// Size of the Randomness actor's mailbox. This should be set large enough to never
386    /// overflow unless a bug is encountered.
387    ///
388    /// If unspecified, this will default to `1,000,000`.
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub mailbox_capacity: Option<usize>,
391
392    /// Per-peer inflight limit for the SendPartialSignatures RPC.
393    ///
394    /// If unspecified, this will default to 20.
395    #[serde(skip_serializing_if = "Option::is_none")]
396    pub send_partial_signatures_inflight_limit: Option<usize>,
397
398    /// Maximum proportion of total peer weight to ignore in case of byzantine behavior.
399    ///
400    /// If unspecified, this will default to 0.2.
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub max_ignored_peer_weight_factor: Option<f64>,
403}
404
405impl RandomnessConfig {
406    pub fn max_partial_sigs_rounds_ahead(&self) -> u64 {
407        const MAX_PARTIAL_SIGS_ROUNDS_AHEAD: u64 = 50;
408
409        self.max_partial_sigs_rounds_ahead
410            .unwrap_or(MAX_PARTIAL_SIGS_ROUNDS_AHEAD)
411    }
412
413    pub fn max_partial_sigs_concurrent_sends(&self) -> usize {
414        const MAX_PARTIAL_SIGS_CONCURRENT_SENDS: usize = 20;
415
416        self.max_partial_sigs_concurrent_sends
417            .unwrap_or(MAX_PARTIAL_SIGS_CONCURRENT_SENDS)
418    }
419    pub fn partial_signature_retry_interval(&self) -> Duration {
420        const PARTIAL_SIGNATURE_RETRY_INTERVAL: u64 = 5_000; // 5 seconds
421
422        Duration::from_millis(
423            self.partial_signature_retry_interval_ms
424                .unwrap_or(PARTIAL_SIGNATURE_RETRY_INTERVAL),
425        )
426    }
427
428    pub fn mailbox_capacity(&self) -> usize {
429        const MAILBOX_CAPACITY: usize = 1_000_000;
430
431        self.mailbox_capacity.unwrap_or(MAILBOX_CAPACITY)
432    }
433
434    pub fn send_partial_signatures_inflight_limit(&self) -> usize {
435        const SEND_PARTIAL_SIGNATURES_INFLIGHT_LIMIT: usize = 20;
436
437        self.send_partial_signatures_inflight_limit
438            .unwrap_or(SEND_PARTIAL_SIGNATURES_INFLIGHT_LIMIT)
439    }
440
441    pub fn max_ignored_peer_weight_factor(&self) -> f64 {
442        const MAX_IGNORED_PEER_WEIGHT_FACTOR: f64 = 0.2;
443
444        self.max_ignored_peer_weight_factor
445            .unwrap_or(MAX_IGNORED_PEER_WEIGHT_FACTOR)
446    }
447}