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