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