Skip to main content

starfish_config/
parameters.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use std::{path::PathBuf, time::Duration};
6
7use serde::{Deserialize, Serialize};
8
9/// Ceiling on the number of headers, and on the number of shards, a bundle may
10/// declare. Enforced while decoding, before the entries are materialised, so a
11/// peer cannot make the decoder build a vector far larger than the configured
12/// caps allow it to keep. Both caps are validated against it at startup.
13pub const MAX_HEADERS_OR_SHARDS_PER_BUNDLE: usize = 1024;
14
15/// Ceiling on the headers a fetch response may carry, for header sync and
16/// commit sync alike. The server truncates its response to its own
17/// `max_headers_per_header_sync_fetch` or `max_headers_per_commit_sync_fetch`,
18/// both validated against this at startup, so a client can reject a response
19/// past it as misbehaviour while still accepting a peer configured above its
20/// own cap. Enforced while decoding each response message, before its entries
21/// are materialised.
22pub const HEADERS_PER_FETCH_CEILING: usize = 1024;
23
24/// Ceiling on the transaction entries a fetch request or response may carry,
25/// for transaction sync and regular commit sync alike. A client requests at
26/// most its own `max_transactions_per_transaction_sync_fetch` or
27/// `max_transactions_per_commit_sync_fetch`, both validated against this at
28/// startup, and the server returns at most one entry per requested reference.
29/// Enforced while decoding each message, before its entries are materialised.
30pub const TRANSACTIONS_PER_FETCH_CEILING: usize = 1024;
31
32/// Ceiling on the commits a commit-sync fetch may request, for regular and
33/// fast commit sync alike. A client requests at most its own
34/// `commit_sync_batch_size` or `fast_commit_sync_batch_size` commits, both
35/// validated against this at startup. Enforced while decoding each response
36/// message, before its entries are materialised.
37pub const COMMITS_PER_FETCH_CEILING: usize = 1024;
38
39/// Operational configurations of a consensus authority.
40///
41/// All fields should tolerate inconsistencies among authorities, without
42/// affecting safety of the protocol. Otherwise, they need to be part of IOTA
43/// protocol config or epoch state on-chain.
44///
45/// NOTE: fields with default values are specified in the serde default
46/// functions. Most operators should not need to specify any field, except
47/// db_path.
48#[derive(Clone, Debug, Deserialize, Serialize)]
49pub struct Parameters {
50    /// Path to consensus DB for this epoch. Required when initializing
51    /// consensus. This is calculated based on user configuration for base
52    /// directory.
53    #[serde(skip)]
54    pub db_path: PathBuf,
55
56    /// Time to wait for parent round leader before sealing a block, from when
57    /// parent round has a quorum.
58    #[serde(default = "Parameters::default_leader_timeout")]
59    pub leader_timeout: Duration,
60
61    /// Sustained spacing between own blocks: long-run production never exceeds
62    /// one block per `min_block_delay`. This avoids generating too many rounds
63    /// when latency is low. This is especially necessary for tests running
64    /// locally. If setting a non-default value, it should be set low enough
65    /// to avoid reducing round rate and increasing latency in realistic and
66    /// distributed configurations.
67    #[serde(default = "Parameters::default_min_block_delay")]
68    pub min_block_delay: Duration,
69
70    /// Soft counterpart of `leader_timeout`: after this duration we are
71    /// willing to propose a block even without a strong-vote quorum, to avoid
72    /// liveness stalls when leader data is slow to propagate. Fires earlier
73    /// than `leader_timeout` and does not force block creation on its own.
74    #[serde(default = "Parameters::default_soft_leader_timeout")]
75    pub soft_leader_timeout: Duration,
76
77    /// Window bounding own block production together with `min_block_delay`:
78    /// idle time accrues budget for bursts of up to `block_rate_window /
79    /// min_block_delay` back-to-back blocks, letting a validator that fell
80    /// behind catch up on rounds instead of skipping them. Set at or below
81    /// `min_block_delay` to disable bursting (fixed spacing between blocks).
82    #[serde(default = "Parameters::default_block_rate_window")]
83    pub block_rate_window: Duration,
84
85    /// Number of block headers to fetch per commit sync request.
86    #[serde(default = "Parameters::default_max_headers_per_commit_sync_fetch")]
87    pub max_headers_per_commit_sync_fetch: usize,
88
89    /// Number of transactions to fetch per commit sync request.
90    #[serde(default = "Parameters::default_max_transactions_per_commit_sync_fetch")]
91    pub max_transactions_per_commit_sync_fetch: usize,
92
93    /// Number of block headers to fetch per header sync (periodic or live)
94    /// request.
95    #[serde(default = "Parameters::default_max_headers_per_header_sync_fetch")]
96    pub max_headers_per_header_sync_fetch: usize,
97
98    /// Number of transactions to fetch per transaction sync request.
99    #[serde(default = "Parameters::default_max_transactions_per_transaction_sync_fetch")]
100    pub max_transactions_per_transaction_sync_fetch: usize,
101
102    /// Time to wait during node start up until the node has synced the last
103    /// proposed block via the network peers. When set to `0` the sync
104    /// mechanism is disabled. This property is meant to be used for amnesia
105    /// recovery.
106    #[serde(default = "Parameters::default_sync_last_known_own_block_timeout")]
107    pub sync_last_known_own_block_timeout: Duration,
108
109    /// The number of rounds of blocks to be kept in the Dag state cache per
110    /// authority. The larger the number the more the blocks that will be
111    /// kept in memory allowing minimising any potential disk access.
112    /// Value should be at minimum 50 rounds to ensure node performance, but
113    /// being too large can be expensive in memory usage.
114    #[serde(default = "Parameters::default_dag_state_cached_rounds")]
115    pub dag_state_cached_rounds: u32,
116
117    /// Rounds a header from a far-future-bounded source may lead the locally
118    /// accepted frontier, in addition to `dag_state_cached_rounds`, before it
119    /// is dropped as too far ahead to connect.
120    #[serde(default = "Parameters::default_peer_round_ahead_margin")]
121    pub peer_round_ahead_margin: u32,
122
123    // Number of authorities commit syncer fetches in parallel.
124    // Both commits in a range and blocks referenced by the commits are fetched per authority.
125    #[serde(default = "Parameters::default_commit_sync_parallel_fetches")]
126    pub commit_sync_parallel_fetches: usize,
127
128    // Number of commits to fetch in a batch, also the maximum number of commits returned per
129    // fetch. If this value is set too small, fetching becomes inefficient.
130    // If this value is set too large, it can result in load imbalance and stragglers.
131    #[serde(default = "Parameters::default_commit_sync_batch_size")]
132    pub commit_sync_batch_size: u32,
133
134    // This affects the maximum number of commit batches being fetched, and those fetched but not
135    // processed as consensus output, before throttling of outgoing commit fetches starts.
136    #[serde(default = "Parameters::default_commit_sync_batches_ahead")]
137    pub commit_sync_batches_ahead: usize,
138
139    /// Maximum number of commits scanned and replayed per batch during
140    /// recovery, bounding peak memory when a large unprocessed range is
141    /// replayed at startup.
142    #[serde(default = "Parameters::default_commit_recovery_batch_size")]
143    pub commit_recovery_batch_size: u32,
144
145    /// Maximum number of headers to be included in a bundle. Headers exceeding
146    /// the max allowed limit will be truncated.
147    #[serde(default = "Parameters::default_max_headers_per_bundle")]
148    pub max_headers_per_bundle: usize,
149
150    /// Maximum number of transaction shards to be included in a bundle. Shards
151    /// exceeding the max allowed limit will be truncated.
152    #[serde(default = "Parameters::default_max_shards_per_bundle")]
153    pub max_shards_per_bundle: usize,
154
155    /// Tonic network settings.
156    #[serde(default = "TonicParameters::default")]
157    pub tonic: TonicParameters,
158
159    /// Number of commits requested per fast commit sync fetch. A response may
160    /// carry up to twice this many, since it extends past the requested end to
161    /// reach a certifiable commit. Set too small, fetching is inefficient and
162    /// the server has little room to reach one; set too large, it causes load
163    /// imbalance and stragglers.
164    #[serde(default = "Parameters::default_fast_commit_sync_batch_size")]
165    pub fast_commit_sync_batch_size: u32,
166
167    // Gap threshold for switching between commit syncers. When the gap between quorum and local
168    // commit index is larger than this threshold, FastCommitSyncer fetches. Otherwise,
169    // CommitSyncer fetches.
170    #[serde(default = "Parameters::default_commit_sync_gap_threshold")]
171    pub commit_sync_gap_threshold: u32,
172
173    /// Enable FastCommitSyncer for faster recovery from large commit gaps.
174    /// Enabled by default; operators can disable it locally if bugs are
175    /// discovered.
176    #[serde(default = "Parameters::default_enable_fast_commit_syncer")]
177    pub enable_fast_commit_syncer: bool,
178
179    /// Reconnect block streams after fast commit sync reinitializes consensus,
180    /// discarding bundles buffered against the previous state. Enabled by
181    /// default; operators can disable it locally. Has no effect unless
182    /// `enable_fast_commit_syncer` is also enabled, since only fast sync
183    /// signals the reset.
184    #[serde(default = "Parameters::default_enable_block_stream_reset_on_fast_sync_exit")]
185    pub enable_block_stream_reset_on_fast_sync_exit: bool,
186
187    /// Ask commit-sync peers that have voted for the end of the requested
188    /// range before those that have not, since a vote means the peer has
189    /// solidified every commit in the range. Peers without an observed vote
190    /// are ordered behind; each fetch round tries a bounded number of peers,
191    /// so on a committee larger than that bound they can stay outside the
192    /// round until their votes are observed. The header fetch that
193    /// reinitializes the node at the end of fast sync likewise asks peers that
194    /// have voted for the last commit first. Enabled by default; disabling it
195    /// leaves peers in the order `enable_peer_responsiveness_ranking` gives
196    /// them.
197    #[serde(default = "Parameters::default_enable_commit_sync_peer_selection_by_commit_votes")]
198    pub enable_commit_sync_peer_selection_by_commit_votes: bool,
199
200    /// Enable adaptive acknowledgment filtering for StarfishSpeed.
201    /// Local heuristic that leaves out of leader blocks the acknowledgments
202    /// that the voters are not expected to hold at the next round, judged
203    /// from how soon they acknowledged recent blocks. Voters count by how
204    /// often this node's blocks reference them. Deferred acknowledgments go
205    /// into later blocks. Effective only when the
206    /// protocol-level `consensus_starfish_speed` flag is also on.
207    #[serde(default = "Parameters::default_enable_starfish_speed_adaptive_acknowledgments")]
208    pub enable_starfish_speed_adaptive_acknowledgments: bool,
209
210    /// Prefer more responsive peers when the transactions synchronizer, the
211    /// commit syncer and the header synchronizer select peers to fetch from.
212    /// Responses are verified the same way regardless, so it cannot affect
213    /// safety. Enabled by default; disabling it restores the previous
214    /// selection: for the transactions synchronizer a uniform random order
215    /// that excludes the most recently failed peers (up to less than f+1 by
216    /// stake), and for the commit syncer and the header synchronizer a uniform
217    /// random order.
218    #[serde(default = "Parameters::default_enable_peer_responsiveness_ranking")]
219    pub enable_peer_responsiveness_ranking: bool,
220
221    /// Ask only a bounded number of peers that recently supplied a new header
222    /// or referenced a missing one to push more headers from that author.
223    #[serde(default = "Parameters::default_enable_bounded_header_advertisement")]
224    pub enable_bounded_header_advertisement: bool,
225
226    /// Ask only the fastest peers that together provide enough shards and
227    /// stake for reconstruction. Enabled by default; disabling it asks every
228    /// peer.
229    #[serde(default = "Parameters::default_enable_shard_peer_selection")]
230    pub enable_shard_peer_selection: bool,
231
232    /// Port for the DAG visualizer gRPC server (localhost only).
233    /// When set, starts a debugging server for real-time DAG visualization.
234    /// Only has an effect when the `dag-visualizer` feature is compiled in.
235    /// Disabled by default (None).
236    #[serde(default)]
237    pub dag_visualizer_port: Option<u16>,
238
239    /// Maximum number of rounds the last commit's leader may run ahead of the
240    /// last solid commit's leader before the node stops accepting new block
241    /// bundles and restricts header fetching, until solidification catches up.
242    #[serde(default = "Parameters::default_solid_commit_lag_threshold")]
243    pub solid_commit_lag_threshold: u32,
244
245    /// Maximum number of shards from one relaying authority retained across
246    /// all pending shard accumulators. At the budget, admitting a new shard
247    /// from the authority evicts its oldest retained one, so reconstructor
248    /// memory stays bounded by `committee size × budget × shard size`
249    /// regardless of how far commits run ahead of solidification.
250    #[serde(default = "Parameters::default_shard_budget_per_authority")]
251    pub shard_budget_per_authority: u32,
252
253    /// Maximum transaction payload bytes one fast commit-sync response carries.
254    /// A fetch covering more commits than this is answered with the commits
255    /// whose payloads fit, and the requester asks for the rest in its next
256    /// fetch. When the range's first commit exceeds this on its own it is
257    /// still served whole. A requester stops reading a response once it passes
258    /// this and its first commit is complete.
259    #[serde(default = "Parameters::default_max_fast_commit_sync_transaction_bytes")]
260    pub max_fast_commit_sync_transaction_bytes: usize,
261}
262
263impl Parameters {
264    /// Threshold for the number of commits sent to the consumer but not yet
265    /// handled, above which commit producers (commit syncers, commit observer
266    /// recovery) pause to let the consumer catch up.
267    pub fn unhandled_commits_threshold(&self) -> u32 {
268        self.commit_sync_batch_size * (self.commit_sync_batches_ahead as u32)
269    }
270
271    pub(crate) fn default_leader_timeout() -> Duration {
272        Duration::from_millis(200)
273    }
274
275    pub(crate) fn default_min_block_delay() -> Duration {
276        if cfg!(msim) || std::env::var("__TEST_ONLY_CONSENSUS_USE_LONG_MIN_BLOCK_DELAY").is_ok() {
277            // Checkpoint building and execution cannot keep up with high commit rate in
278            // simtests, leading to long reconfiguration delays. This is because
279            // simtest is single threaded, and spending too much time in
280            // consensus can lead to starvation elsewhere.
281            Duration::from_millis(400)
282        } else if cfg!(test) {
283            // Avoid excessive CPU, data and logs in tests.
284            Duration::from_millis(250)
285        } else {
286            // For production, use min delay between block being set to 50ms, reducing the
287            // block rate to 20 blocks/sec
288            Duration::from_millis(50)
289        }
290    }
291
292    pub(crate) fn default_soft_leader_timeout() -> Duration {
293        Duration::from_millis(5)
294    }
295
296    pub(crate) fn default_block_rate_window() -> Duration {
297        Duration::from_secs(2)
298    }
299
300    /// Burst capacity: maximum number of own blocks within `block_rate_window`
301    /// (40 in production, 5 in msim, 8 in tests with the default window).
302    pub fn block_rate_burst(&self) -> u64 {
303        let interval_ms = self.min_block_delay.as_millis().max(1) as u64;
304        (self.block_rate_window.as_millis() as u64 / interval_ms).max(1)
305    }
306
307    /// Highest round a header from a far-future-bounded source may have,
308    /// relative to the accepted `frontier`, to still be close enough to
309    /// connect; headers above this are too far ahead and dropped.
310    pub fn far_future_round_ceiling(&self, frontier: u32) -> u32 {
311        frontier
312            .saturating_add(self.dag_state_cached_rounds)
313            .saturating_add(self.peer_round_ahead_margin)
314    }
315
316    /// Maximum number of block headers served per fetch request, depending on
317    /// whether the request comes from commit sync or the header synchronizer.
318    pub fn max_headers_per_fetch(&self, commit_sync: bool) -> usize {
319        if commit_sync {
320            self.max_headers_per_commit_sync_fetch
321        } else {
322            self.max_headers_per_header_sync_fetch
323        }
324    }
325
326    /// Validates local consensus parameters, rejecting zero values that can
327    /// lead to synchronization problems. Returns a description of the first
328    /// offending field.
329    pub fn validate(&self) -> Result<(), String> {
330        let positive_fields = [
331            (
332                "max_headers_per_commit_sync_fetch",
333                self.max_headers_per_commit_sync_fetch as u128,
334            ),
335            (
336                "max_transactions_per_commit_sync_fetch",
337                self.max_transactions_per_commit_sync_fetch as u128,
338            ),
339            (
340                "max_headers_per_header_sync_fetch",
341                self.max_headers_per_header_sync_fetch as u128,
342            ),
343            (
344                "max_transactions_per_transaction_sync_fetch",
345                self.max_transactions_per_transaction_sync_fetch as u128,
346            ),
347            (
348                "dag_state_cached_rounds",
349                self.dag_state_cached_rounds as u128,
350            ),
351            (
352                "commit_sync_parallel_fetches",
353                self.commit_sync_parallel_fetches as u128,
354            ),
355            (
356                "commit_sync_batch_size",
357                self.commit_sync_batch_size as u128,
358            ),
359            (
360                "commit_recovery_batch_size",
361                self.commit_recovery_batch_size as u128,
362            ),
363            (
364                "commit_sync_batches_ahead",
365                self.commit_sync_batches_ahead as u128,
366            ),
367            (
368                "max_headers_per_bundle",
369                self.max_headers_per_bundle as u128,
370            ),
371            ("max_shards_per_bundle", self.max_shards_per_bundle as u128),
372            (
373                "fast_commit_sync_batch_size",
374                self.fast_commit_sync_batch_size as u128,
375            ),
376            (
377                "max_fast_commit_sync_transaction_bytes",
378                self.max_fast_commit_sync_transaction_bytes as u128,
379            ),
380            (
381                "tonic.connection_buffer_size",
382                self.tonic.connection_buffer_size as u128,
383            ),
384            (
385                "tonic.excessive_message_size",
386                self.tonic.excessive_message_size as u128,
387            ),
388            (
389                "tonic.message_size_limit",
390                self.tonic.message_size_limit as u128,
391            ),
392            (
393                "tonic.keepalive_interval",
394                self.tonic.keepalive_interval.as_nanos(),
395            ),
396            (
397                "shard_budget_per_authority",
398                self.shard_budget_per_authority as u128,
399            ),
400        ];
401        for (name, value) in positive_fields {
402            if value == 0 {
403                return Err(format!("{name} must be positive"));
404            }
405        }
406        let bundle_fields = [
407            ("max_headers_per_bundle", self.max_headers_per_bundle),
408            ("max_shards_per_bundle", self.max_shards_per_bundle),
409        ];
410        for (name, value) in bundle_fields {
411            if value > MAX_HEADERS_OR_SHARDS_PER_BUNDLE {
412                return Err(format!(
413                    "{name} must not exceed {MAX_HEADERS_OR_SHARDS_PER_BUNDLE}"
414                ));
415            }
416        }
417        let header_fetch_fields = [
418            (
419                "max_headers_per_header_sync_fetch",
420                self.max_headers_per_header_sync_fetch,
421            ),
422            (
423                "max_headers_per_commit_sync_fetch",
424                self.max_headers_per_commit_sync_fetch,
425            ),
426        ];
427        for (name, value) in header_fetch_fields {
428            if value > HEADERS_PER_FETCH_CEILING {
429                return Err(format!(
430                    "{name} must not exceed {HEADERS_PER_FETCH_CEILING}"
431                ));
432            }
433        }
434        let transaction_fetch_fields = [
435            (
436                "max_transactions_per_transaction_sync_fetch",
437                self.max_transactions_per_transaction_sync_fetch,
438            ),
439            (
440                "max_transactions_per_commit_sync_fetch",
441                self.max_transactions_per_commit_sync_fetch,
442            ),
443        ];
444        for (name, value) in transaction_fetch_fields {
445            if value > TRANSACTIONS_PER_FETCH_CEILING {
446                return Err(format!(
447                    "{name} must not exceed {TRANSACTIONS_PER_FETCH_CEILING}"
448                ));
449            }
450        }
451        let commit_fetch_fields = [
452            ("commit_sync_batch_size", self.commit_sync_batch_size),
453            (
454                "fast_commit_sync_batch_size",
455                self.fast_commit_sync_batch_size,
456            ),
457        ];
458        for (name, value) in commit_fetch_fields {
459            if value as usize > COMMITS_PER_FETCH_CEILING {
460                return Err(format!(
461                    "{name} must not exceed {COMMITS_PER_FETCH_CEILING}"
462                ));
463            }
464        }
465        Ok(())
466    }
467
468    // Maximum number of block headers to fetch per commit sync request.
469    pub(crate) fn default_max_headers_per_commit_sync_fetch() -> usize {
470        if cfg!(msim) {
471            // Exercise hitting blocks per fetch limit.
472            10
473        } else {
474            1000
475        }
476    }
477
478    // Maximum number of transactions to fetch per commit sync request.
479    pub(crate) fn default_max_transactions_per_commit_sync_fetch() -> usize {
480        if cfg!(msim) {
481            // Exercise hitting transactions per fetch limit.
482            10
483        } else {
484            1000
485        }
486    }
487
488    // Maximum number of block headers to fetch per header sync (periodic or
489    // live) request.
490    pub(crate) fn default_max_headers_per_header_sync_fetch() -> usize {
491        if cfg!(msim) {
492            // Exercise hitting blocks per fetch limit.
493            10
494        } else {
495            // TODO: This might should match the value of block headers in the bundle.
496            100
497        }
498    }
499
500    // Maximum number of transactions to fetch per transaction sync request.
501    pub(crate) fn default_max_transactions_per_transaction_sync_fetch() -> usize {
502        if cfg!(msim) { 10 } else { 1000 }
503    }
504
505    pub(crate) fn default_sync_last_known_own_block_timeout() -> Duration {
506        if cfg!(msim) {
507            Duration::from_millis(500)
508        } else {
509            // Here we prioritise liveness over the complete de-risking of block
510            // equivocation. 5 seconds in the majority of cases should be good
511            // enough for this given a healthy network.
512            Duration::from_secs(5)
513        }
514    }
515
516    pub(crate) fn default_dag_state_cached_rounds() -> u32 {
517        if cfg!(msim) {
518            // Exercise reading blocks from store.
519            5
520        } else {
521            500
522        }
523    }
524
525    pub(crate) fn default_peer_round_ahead_margin() -> u32 {
526        1000
527    }
528
529    pub(crate) fn default_commit_sync_parallel_fetches() -> usize {
530        8
531    }
532
533    pub(crate) fn default_commit_sync_batch_size() -> u32 {
534        if cfg!(msim) {
535            // Exercise commit sync.
536            5
537        } else {
538            100
539        }
540    }
541
542    pub(crate) fn default_commit_recovery_batch_size() -> u32 {
543        if cfg!(msim) { 3 } else { 250 }
544    }
545
546    pub(crate) fn default_commit_sync_batches_ahead() -> usize {
547        // This is set to be a multiple of default commit_sync_parallel_fetches to allow
548        // fetching ahead, while keeping the total number of inflight fetches
549        // and unprocessed fetched commits limited.
550        32
551    }
552
553    pub(crate) fn default_max_headers_per_bundle() -> usize {
554        150
555    }
556
557    pub(crate) fn default_max_shards_per_bundle() -> usize {
558        150
559    }
560
561    pub(crate) fn default_fast_commit_sync_batch_size() -> u32 {
562        if cfg!(msim) {
563            // Exercise fast commit sync.
564            5
565        } else {
566            // Sized so that commit_sync_parallel_fetches ranges fit under the
567            // unhandled-commits threshold (8 x 400 <= 3200), letting fast sync
568            // actually run its fetches in parallel. The server chunks larger
569            // responses across multiple messages either way.
570            400
571        }
572    }
573
574    pub(crate) fn default_commit_sync_gap_threshold() -> u32 {
575        if cfg!(msim) {
576            // Use smaller threshold for testing.
577            10
578        } else {
579            // When gap > 1000, FastCommitSyncer is more efficient.
580            // When gap <= 1000, CommitSyncer handles incremental sync.
581            1000
582        }
583    }
584
585    pub(crate) fn default_enable_fast_commit_syncer() -> bool {
586        // Enabled by default. Operators can disable it locally if bugs are discovered,
587        // without waiting for a protocol upgrade.
588        true
589    }
590
591    pub(crate) fn default_enable_block_stream_reset_on_fast_sync_exit() -> bool {
592        true
593    }
594
595    pub(crate) fn default_enable_commit_sync_peer_selection_by_commit_votes() -> bool {
596        // Enabled by default. Ordering only, so it cannot diverge consensus.
597        true
598    }
599
600    pub(crate) fn default_enable_starfish_speed_adaptive_acknowledgments() -> bool {
601        true
602    }
603
604    pub(crate) fn default_enable_peer_responsiveness_ranking() -> bool {
605        true
606    }
607
608    pub(crate) fn default_enable_bounded_header_advertisement() -> bool {
609        true
610    }
611
612    pub(crate) fn default_enable_shard_peer_selection() -> bool {
613        true
614    }
615
616    pub(crate) fn default_solid_commit_lag_threshold() -> u32 {
617        // The healthy gap is a few rounds at most; 500 rounds (a few minutes of
618        // commits) is far above live jitter yet caps how long a solidification
619        // stall can keep widening the shard/payload retention window.
620        500
621    }
622
623    pub(crate) fn default_max_fast_commit_sync_transaction_bytes() -> usize {
624        64 * 1024 * 1024
625    }
626
627    pub(crate) fn default_shard_budget_per_authority() -> u32 {
628        // Honest need per authority is one shard per slot times a few rounds
629        // until decode, well under the budget at any realistic committee size.
630        // Worst-case total pool ≈ 3 × budget × the maximum payload size.
631        1000
632    }
633}
634
635impl Default for Parameters {
636    fn default() -> Self {
637        Self {
638            db_path: PathBuf::default(),
639            leader_timeout: Parameters::default_leader_timeout(),
640            min_block_delay: Parameters::default_min_block_delay(),
641            soft_leader_timeout: Parameters::default_soft_leader_timeout(),
642            block_rate_window: Parameters::default_block_rate_window(),
643            max_headers_per_commit_sync_fetch:
644                Parameters::default_max_headers_per_commit_sync_fetch(),
645            max_transactions_per_commit_sync_fetch:
646                Parameters::default_max_transactions_per_commit_sync_fetch(),
647            max_headers_per_header_sync_fetch:
648                Parameters::default_max_headers_per_header_sync_fetch(),
649            max_transactions_per_transaction_sync_fetch:
650                Parameters::default_max_transactions_per_transaction_sync_fetch(),
651            sync_last_known_own_block_timeout:
652                Parameters::default_sync_last_known_own_block_timeout(),
653            dag_state_cached_rounds: Parameters::default_dag_state_cached_rounds(),
654            peer_round_ahead_margin: Parameters::default_peer_round_ahead_margin(),
655            commit_sync_parallel_fetches: Parameters::default_commit_sync_parallel_fetches(),
656            commit_sync_batch_size: Parameters::default_commit_sync_batch_size(),
657            commit_sync_batches_ahead: Parameters::default_commit_sync_batches_ahead(),
658            commit_recovery_batch_size: Parameters::default_commit_recovery_batch_size(),
659            max_headers_per_bundle: Parameters::default_max_headers_per_bundle(),
660            max_shards_per_bundle: Parameters::default_max_shards_per_bundle(),
661            tonic: TonicParameters::default(),
662            fast_commit_sync_batch_size: Parameters::default_fast_commit_sync_batch_size(),
663            commit_sync_gap_threshold: Parameters::default_commit_sync_gap_threshold(),
664            enable_fast_commit_syncer: Parameters::default_enable_fast_commit_syncer(),
665            enable_block_stream_reset_on_fast_sync_exit:
666                Parameters::default_enable_block_stream_reset_on_fast_sync_exit(),
667            enable_commit_sync_peer_selection_by_commit_votes:
668                Parameters::default_enable_commit_sync_peer_selection_by_commit_votes(),
669            enable_starfish_speed_adaptive_acknowledgments:
670                Parameters::default_enable_starfish_speed_adaptive_acknowledgments(),
671            enable_peer_responsiveness_ranking:
672                Parameters::default_enable_peer_responsiveness_ranking(),
673            enable_bounded_header_advertisement:
674                Parameters::default_enable_bounded_header_advertisement(),
675            enable_shard_peer_selection: Parameters::default_enable_shard_peer_selection(),
676            dag_visualizer_port: None,
677            solid_commit_lag_threshold: Parameters::default_solid_commit_lag_threshold(),
678            shard_budget_per_authority: Parameters::default_shard_budget_per_authority(),
679            max_fast_commit_sync_transaction_bytes:
680                Parameters::default_max_fast_commit_sync_transaction_bytes(),
681        }
682    }
683}
684
685#[derive(Clone, Debug, Deserialize, Serialize)]
686pub struct TonicParameters {
687    /// Keepalive interval and timeouts for both client and server.
688    ///
689    /// If unspecified, this will default to 5s.
690    #[serde(default = "TonicParameters::default_keepalive_interval")]
691    pub keepalive_interval: Duration,
692
693    /// Size of various per-connection buffers.
694    ///
695    /// If unspecified, this will default to 32MiB.
696    #[serde(default = "TonicParameters::default_connection_buffer_size")]
697    pub connection_buffer_size: usize,
698
699    /// Response messages over this wire size, prefix plus compressed payload,
700    /// increment a counter.
701    ///
702    /// If unspecified, this will default to 16MiB.
703    #[serde(default = "TonicParameters::default_excessive_message_size")]
704    pub excessive_message_size: usize,
705
706    /// Hard message size limit for both requests and responses.
707    /// This value is higher than strictly necessary, to allow overheads.
708    /// Message size targets and soft limits are computed based on this value.
709    ///
710    /// If unspecified, this will default to 64MiB.
711    #[serde(default = "TonicParameters::default_message_size_limit")]
712    pub message_size_limit: usize,
713
714    /// Maximum number of concurrent HTTP/2 streams a peer may open on a single
715    /// connection. Bounds per-connection request fan-out.
716    ///
717    /// If unspecified, this will default to 64. `0` disables the limit, leaving
718    /// the transport default.
719    #[serde(default = "TonicParameters::default_max_concurrent_streams")]
720    pub max_concurrent_streams: u32,
721
722    /// Server-side fallback deadline for requests that omit a `grpc-timeout`
723    /// header. The long-lived block-subscription stream is always exempt.
724    ///
725    /// If unspecified, this will default to 120s. A zero duration disables the
726    /// fallback deadline.
727    #[serde(default = "TonicParameters::default_request_timeout")]
728    pub request_timeout: Duration,
729
730    /// Hard size limit for request messages: inbound requests when decoding
731    /// and outbound requests when encoding. Consensus requests are small (ref
732    /// lists); large payloads belong to responses, bounded by
733    /// `message_size_limit`. A smaller request bound shrinks the memory a
734    /// single in-flight request can pin before its handler runs.
735    ///
736    /// If unspecified, this will default to 1MiB. `0` falls back to
737    /// `message_size_limit`.
738    #[serde(
739        default = "TonicParameters::default_max_request_message_size",
740        alias = "max_inbound_message_size"
741    )]
742    pub max_request_message_size: usize,
743
744    /// Per-peer, per-RPC admission caps for the inbound consensus server.
745    #[serde(default)]
746    pub admission: AdmissionParameters,
747
748    /// Deadline for receiving the request message that opens a block
749    /// subscription stream. A peer that opens the stream and withholds the
750    /// request is disconnected once it expires; the stream itself stays exempt
751    /// from `request_timeout`.
752    ///
753    /// If unspecified, this will default to 30s. A zero duration disables the
754    /// deadline.
755    #[serde(default = "TonicParameters::default_subscribe_request_timeout")]
756    pub subscribe_request_timeout: Duration,
757}
758
759impl TonicParameters {
760    /// Hard size limit for request messages, `message_size_limit` when
761    /// `max_request_message_size` is `0`.
762    pub fn request_message_size_limit(&self) -> usize {
763        if self.max_request_message_size == 0 {
764            self.message_size_limit
765        } else {
766            self.max_request_message_size
767        }
768    }
769
770    fn default_keepalive_interval() -> Duration {
771        Duration::from_secs(5)
772    }
773
774    fn default_connection_buffer_size() -> usize {
775        32 << 20
776    }
777
778    fn default_excessive_message_size() -> usize {
779        16 << 20
780    }
781
782    fn default_message_size_limit() -> usize {
783        64 << 20
784    }
785
786    fn default_max_concurrent_streams() -> u32 {
787        64
788    }
789
790    fn default_request_timeout() -> Duration {
791        Duration::from_secs(120)
792    }
793
794    fn default_max_request_message_size() -> usize {
795        1 << 20
796    }
797
798    fn default_subscribe_request_timeout() -> Duration {
799        Duration::from_secs(30)
800    }
801}
802
803impl Default for TonicParameters {
804    fn default() -> Self {
805        Self {
806            keepalive_interval: TonicParameters::default_keepalive_interval(),
807            connection_buffer_size: TonicParameters::default_connection_buffer_size(),
808            excessive_message_size: TonicParameters::default_excessive_message_size(),
809            message_size_limit: TonicParameters::default_message_size_limit(),
810            max_concurrent_streams: TonicParameters::default_max_concurrent_streams(),
811            request_timeout: TonicParameters::default_request_timeout(),
812            max_request_message_size: TonicParameters::default_max_request_message_size(),
813            admission: AdmissionParameters::default(),
814            subscribe_request_timeout: TonicParameters::default_subscribe_request_timeout(),
815        }
816    }
817}
818
819/// Per-peer, per-RPC concurrency caps for the inbound consensus gRPC server.
820///
821/// Each cap bounds how many concurrent requests of one RPC group a single
822/// committee peer (keyed on its authenticated authority index) may have in
823/// flight; a peer cannot consume another peer's budget. These are local,
824/// non-protocol parameters — heterogeneous values across authorities are safe,
825/// so they can be rolled out and tuned per node.
826///
827/// The defaults are sized for ~100-validator committees and the local
828/// synchronizer fan-out toward one server. `0` turns a cap off; each cap is
829/// checked on its own.
830#[derive(Clone, Debug, Deserialize, Serialize)]
831pub struct AdmissionParameters {
832    /// Max concurrent block-subscription streams per peer.
833    ///
834    /// If unspecified, this will default to 2.
835    #[serde(default = "AdmissionParameters::default_max_subscriptions_per_peer")]
836    pub max_subscriptions_per_peer: u32,
837
838    /// Max concurrent header fetches per peer
839    /// (`fetch_block_headers` + `fetch_latest_block_headers`).
840    ///
841    /// If unspecified, this will default to 32.
842    #[serde(default = "AdmissionParameters::default_max_header_fetches_per_peer")]
843    pub max_header_fetches_per_peer: u32,
844
845    /// Max concurrent transaction fetches per peer (`fetch_transactions`).
846    ///
847    /// If unspecified, this will default to 16.
848    #[serde(default = "AdmissionParameters::default_max_transaction_fetches_per_peer")]
849    pub max_transaction_fetches_per_peer: u32,
850
851    /// Max concurrent commit fetches per peer
852    /// (`fetch_commits` + `fetch_commits_and_transactions`).
853    ///
854    /// If unspecified, this will default to 8.
855    #[serde(default = "AdmissionParameters::default_max_commit_fetches_per_peer")]
856    pub max_commit_fetches_per_peer: u32,
857
858    /// Max concurrent commit fetches across all peers, beyond which a peer is
859    /// still granted its first. A fast commit-sync response holds its commits
860    /// and certifier headers until it has been sent, and reads its transactions
861    /// from the store while it is being sent, so this caps what serving them
862    /// can cost the node at once, short of one response per peer.
863    ///
864    /// If unspecified, this will default to 16.
865    #[serde(default = "AdmissionParameters::default_max_commit_fetches_total")]
866    pub max_commit_fetches_total: u32,
867}
868
869impl AdmissionParameters {
870    fn default_max_subscriptions_per_peer() -> u32 {
871        2
872    }
873
874    fn default_max_header_fetches_per_peer() -> u32 {
875        32
876    }
877
878    fn default_max_transaction_fetches_per_peer() -> u32 {
879        16
880    }
881
882    fn default_max_commit_fetches_per_peer() -> u32 {
883        Parameters::default_commit_sync_parallel_fetches() as u32
884    }
885
886    fn default_max_commit_fetches_total() -> u32 {
887        2 * AdmissionParameters::default_max_commit_fetches_per_peer()
888    }
889}
890
891impl Default for AdmissionParameters {
892    fn default() -> Self {
893        Self {
894            max_subscriptions_per_peer: AdmissionParameters::default_max_subscriptions_per_peer(),
895            max_header_fetches_per_peer: AdmissionParameters::default_max_header_fetches_per_peer(),
896            max_transaction_fetches_per_peer:
897                AdmissionParameters::default_max_transaction_fetches_per_peer(),
898            max_commit_fetches_per_peer: AdmissionParameters::default_max_commit_fetches_per_peer(),
899            max_commit_fetches_total: AdmissionParameters::default_max_commit_fetches_total(),
900        }
901    }
902}