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    /// Enable adaptive acknowledgment filtering for StarfishSpeed.
149    /// Local heuristic that drops acks for authorities persistently blamed
150    /// by recent strong-vote masks. Effective only when the protocol-level
151    /// `consensus_starfish_speed` flag is also on. Enabled by default;
152    /// operators can disable it locally without a protocol change.
153    #[serde(default = "Parameters::default_enable_starfish_speed_adaptive_acknowledgments")]
154    pub enable_starfish_speed_adaptive_acknowledgments: bool,
155
156    /// Prefer more responsive peers when the transactions synchronizer selects
157    /// peers to fetch from. Ranking is a preference within the already-eligible
158    /// candidate set, not a change of eligibility, so it cannot affect safety.
159    /// Enabled by default; disabling it restores the previous selection: a
160    /// uniform random order that excludes the most recently failed peers (up
161    /// to less than f+1 by stake).
162    #[serde(default = "Parameters::default_enable_peer_responsiveness_ranking")]
163    pub enable_peer_responsiveness_ranking: bool,
164
165    /// Port for the DAG visualizer gRPC server (localhost only).
166    /// When set, starts a debugging server for real-time DAG visualization.
167    /// Only has an effect when the `dag-visualizer` feature is compiled in.
168    /// Disabled by default (None).
169    #[serde(default)]
170    pub dag_visualizer_port: Option<u16>,
171}
172
173impl Parameters {
174    /// Threshold for the number of commits sent to the consumer but not yet
175    /// handled, above which commit producers (commit syncers, commit observer
176    /// recovery) pause to let the consumer catch up.
177    pub fn unhandled_commits_threshold(&self) -> u32 {
178        self.commit_sync_batch_size * (self.commit_sync_batches_ahead as u32)
179    }
180
181    pub(crate) fn default_leader_timeout() -> Duration {
182        Duration::from_millis(200)
183    }
184
185    pub(crate) fn default_min_block_delay() -> Duration {
186        if cfg!(msim) || std::env::var("__TEST_ONLY_CONSENSUS_USE_LONG_MIN_BLOCK_DELAY").is_ok() {
187            // Checkpoint building and execution cannot keep up with high commit rate in
188            // simtests, leading to long reconfiguration delays. This is because
189            // simtest is single threaded, and spending too much time in
190            // consensus can lead to starvation elsewhere.
191            Duration::from_millis(400)
192        } else if cfg!(test) {
193            // Avoid excessive CPU, data and logs in tests.
194            Duration::from_millis(250)
195        } else {
196            // For production, use min delay between block being set to 50ms, reducing the
197            // block rate to 20 blocks/sec
198            Duration::from_millis(50)
199        }
200    }
201
202    pub(crate) fn default_soft_leader_timeout() -> Duration {
203        Duration::from_millis(5)
204    }
205
206    pub(crate) fn default_block_rate_window() -> Duration {
207        Duration::from_secs(2)
208    }
209
210    /// Burst capacity: maximum number of own blocks within `block_rate_window`
211    /// (40 in production, 5 in msim, 8 in tests with the default window).
212    pub fn block_rate_burst(&self) -> u64 {
213        let interval_ms = self.min_block_delay.as_millis().max(1) as u64;
214        (self.block_rate_window.as_millis() as u64 / interval_ms).max(1)
215    }
216
217    /// Highest round a header from a far-future-bounded source may have,
218    /// relative to the accepted `frontier`, to still be close enough to
219    /// connect; headers above this are too far ahead and dropped.
220    pub fn far_future_round_ceiling(&self, frontier: u32) -> u32 {
221        frontier
222            .saturating_add(self.dag_state_cached_rounds)
223            .saturating_add(self.peer_round_ahead_margin)
224    }
225
226    /// Maximum number of block headers served per fetch request, depending on
227    /// whether the request comes from commit sync or the header synchronizer.
228    pub fn max_headers_per_fetch(&self, commit_sync: bool) -> usize {
229        if commit_sync {
230            self.max_headers_per_commit_sync_fetch
231        } else {
232            self.max_headers_per_header_sync_fetch
233        }
234    }
235
236    /// Validates local consensus parameters, rejecting zero values that can
237    /// lead to synchronization problems. Returns a description of the first
238    /// offending field.
239    pub fn validate(&self) -> Result<(), String> {
240        let positive_fields = [
241            (
242                "max_headers_per_commit_sync_fetch",
243                self.max_headers_per_commit_sync_fetch as u128,
244            ),
245            (
246                "max_transactions_per_commit_sync_fetch",
247                self.max_transactions_per_commit_sync_fetch as u128,
248            ),
249            (
250                "max_headers_per_header_sync_fetch",
251                self.max_headers_per_header_sync_fetch as u128,
252            ),
253            (
254                "max_transactions_per_transaction_sync_fetch",
255                self.max_transactions_per_transaction_sync_fetch as u128,
256            ),
257            (
258                "dag_state_cached_rounds",
259                self.dag_state_cached_rounds as u128,
260            ),
261            (
262                "commit_sync_parallel_fetches",
263                self.commit_sync_parallel_fetches as u128,
264            ),
265            (
266                "commit_sync_batch_size",
267                self.commit_sync_batch_size as u128,
268            ),
269            (
270                "commit_recovery_batch_size",
271                self.commit_recovery_batch_size as u128,
272            ),
273            (
274                "commit_sync_batches_ahead",
275                self.commit_sync_batches_ahead as u128,
276            ),
277            (
278                "max_headers_per_bundle",
279                self.max_headers_per_bundle as u128,
280            ),
281            ("max_shards_per_bundle", self.max_shards_per_bundle as u128),
282            (
283                "fast_commit_sync_batch_size",
284                self.fast_commit_sync_batch_size as u128,
285            ),
286            (
287                "tonic.connection_buffer_size",
288                self.tonic.connection_buffer_size as u128,
289            ),
290            (
291                "tonic.excessive_message_size",
292                self.tonic.excessive_message_size as u128,
293            ),
294            (
295                "tonic.message_size_limit",
296                self.tonic.message_size_limit as u128,
297            ),
298            (
299                "tonic.keepalive_interval",
300                self.tonic.keepalive_interval.as_nanos(),
301            ),
302        ];
303        for (name, value) in positive_fields {
304            if value == 0 {
305                return Err(format!("{name} must be positive"));
306            }
307        }
308        Ok(())
309    }
310
311    // Maximum number of block headers to fetch per commit sync request.
312    pub(crate) fn default_max_headers_per_commit_sync_fetch() -> usize {
313        if cfg!(msim) {
314            // Exercise hitting blocks per fetch limit.
315            10
316        } else {
317            1000
318        }
319    }
320
321    // Maximum number of transactions to fetch per commit sync request.
322    pub(crate) fn default_max_transactions_per_commit_sync_fetch() -> usize {
323        if cfg!(msim) {
324            // Exercise hitting transactions per fetch limit.
325            10
326        } else {
327            1000
328        }
329    }
330
331    // Maximum number of block headers to fetch per header sync (periodic or
332    // live) request.
333    pub(crate) fn default_max_headers_per_header_sync_fetch() -> usize {
334        if cfg!(msim) {
335            // Exercise hitting blocks per fetch limit.
336            10
337        } else {
338            // TODO: This might should match the value of block headers in the bundle.
339            100
340        }
341    }
342
343    // Maximum number of transactions to fetch per transaction sync request.
344    pub(crate) fn default_max_transactions_per_transaction_sync_fetch() -> usize {
345        if cfg!(msim) { 10 } else { 1000 }
346    }
347
348    pub(crate) fn default_sync_last_known_own_block_timeout() -> Duration {
349        if cfg!(msim) {
350            Duration::from_millis(500)
351        } else {
352            // Here we prioritise liveness over the complete de-risking of block
353            // equivocation. 5 seconds in the majority of cases should be good
354            // enough for this given a healthy network.
355            Duration::from_secs(5)
356        }
357    }
358
359    pub(crate) fn default_dag_state_cached_rounds() -> u32 {
360        if cfg!(msim) {
361            // Exercise reading blocks from store.
362            5
363        } else {
364            500
365        }
366    }
367
368    pub(crate) fn default_peer_round_ahead_margin() -> u32 {
369        1000
370    }
371
372    pub(crate) fn default_commit_sync_parallel_fetches() -> usize {
373        8
374    }
375
376    pub(crate) fn default_commit_sync_batch_size() -> u32 {
377        if cfg!(msim) {
378            // Exercise commit sync.
379            5
380        } else {
381            100
382        }
383    }
384
385    pub(crate) fn default_commit_recovery_batch_size() -> u32 {
386        if cfg!(msim) { 3 } else { 250 }
387    }
388
389    pub(crate) fn default_commit_sync_batches_ahead() -> usize {
390        // This is set to be a multiple of default commit_sync_parallel_fetches to allow
391        // fetching ahead, while keeping the total number of inflight fetches
392        // and unprocessed fetched commits limited.
393        32
394    }
395
396    pub(crate) fn default_max_headers_per_bundle() -> usize {
397        150
398    }
399
400    pub(crate) fn default_max_shards_per_bundle() -> usize {
401        150
402    }
403
404    pub(crate) fn default_fast_commit_sync_batch_size() -> u32 {
405        if cfg!(msim) {
406            // Exercise fast commit sync.
407            5
408        } else {
409            // With ~10KB per commit and 4MB max message size, 1000 commits (~10MB) requires
410            // chunking. The server will chunk commits across multiple response messages.
411            1000
412        }
413    }
414
415    pub(crate) fn default_commit_sync_gap_threshold() -> u32 {
416        if cfg!(msim) {
417            // Use smaller threshold for testing.
418            10
419        } else {
420            // When gap > 1000, FastCommitSyncer is more efficient.
421            // When gap <= 1000, CommitSyncer handles incremental sync.
422            1000
423        }
424    }
425
426    pub(crate) fn default_enable_fast_commit_syncer() -> bool {
427        // Enabled by default. Operators can disable it locally if bugs are discovered,
428        // without waiting for a protocol upgrade.
429        true
430    }
431
432    pub(crate) fn default_enable_starfish_speed_adaptive_acknowledgments() -> bool {
433        true
434    }
435
436    pub(crate) fn default_enable_peer_responsiveness_ranking() -> bool {
437        true
438    }
439}
440
441impl Default for Parameters {
442    fn default() -> Self {
443        Self {
444            db_path: PathBuf::default(),
445            leader_timeout: Parameters::default_leader_timeout(),
446            min_block_delay: Parameters::default_min_block_delay(),
447            soft_leader_timeout: Parameters::default_soft_leader_timeout(),
448            block_rate_window: Parameters::default_block_rate_window(),
449            max_headers_per_commit_sync_fetch:
450                Parameters::default_max_headers_per_commit_sync_fetch(),
451            max_transactions_per_commit_sync_fetch:
452                Parameters::default_max_transactions_per_commit_sync_fetch(),
453            max_headers_per_header_sync_fetch:
454                Parameters::default_max_headers_per_header_sync_fetch(),
455            max_transactions_per_transaction_sync_fetch:
456                Parameters::default_max_transactions_per_transaction_sync_fetch(),
457            sync_last_known_own_block_timeout:
458                Parameters::default_sync_last_known_own_block_timeout(),
459            dag_state_cached_rounds: Parameters::default_dag_state_cached_rounds(),
460            peer_round_ahead_margin: Parameters::default_peer_round_ahead_margin(),
461            commit_sync_parallel_fetches: Parameters::default_commit_sync_parallel_fetches(),
462            commit_sync_batch_size: Parameters::default_commit_sync_batch_size(),
463            commit_sync_batches_ahead: Parameters::default_commit_sync_batches_ahead(),
464            commit_recovery_batch_size: Parameters::default_commit_recovery_batch_size(),
465            max_headers_per_bundle: Parameters::default_max_headers_per_bundle(),
466            max_shards_per_bundle: Parameters::default_max_shards_per_bundle(),
467            tonic: TonicParameters::default(),
468            fast_commit_sync_batch_size: Parameters::default_fast_commit_sync_batch_size(),
469            commit_sync_gap_threshold: Parameters::default_commit_sync_gap_threshold(),
470            enable_fast_commit_syncer: Parameters::default_enable_fast_commit_syncer(),
471            enable_starfish_speed_adaptive_acknowledgments:
472                Parameters::default_enable_starfish_speed_adaptive_acknowledgments(),
473            enable_peer_responsiveness_ranking:
474                Parameters::default_enable_peer_responsiveness_ranking(),
475            dag_visualizer_port: None,
476        }
477    }
478}
479
480#[derive(Clone, Debug, Deserialize, Serialize)]
481pub struct TonicParameters {
482    /// Keepalive interval and timeouts for both client and server.
483    ///
484    /// If unspecified, this will default to 5s.
485    #[serde(default = "TonicParameters::default_keepalive_interval")]
486    pub keepalive_interval: Duration,
487
488    /// Size of various per-connection buffers.
489    ///
490    /// If unspecified, this will default to 32MiB.
491    #[serde(default = "TonicParameters::default_connection_buffer_size")]
492    pub connection_buffer_size: usize,
493
494    /// Messages over this size threshold will increment a counter.
495    ///
496    /// If unspecified, this will default to 16MiB.
497    #[serde(default = "TonicParameters::default_excessive_message_size")]
498    pub excessive_message_size: usize,
499
500    /// Hard message size limit for both requests and responses.
501    /// This value is higher than strictly necessary, to allow overheads.
502    /// Message size targets and soft limits are computed based on this value.
503    ///
504    /// If unspecified, this will default to 1GiB.
505    #[serde(default = "TonicParameters::default_message_size_limit")]
506    pub message_size_limit: usize,
507
508    /// Maximum number of concurrent HTTP/2 streams a peer may open on a single
509    /// connection. Bounds per-connection request fan-out.
510    ///
511    /// `0` (the default) disables the limit, leaving the transport default.
512    #[serde(default)]
513    pub max_concurrent_streams: u32,
514
515    /// Server-side fallback deadline for requests that omit a `grpc-timeout`
516    /// header. The long-lived block-subscription stream is always exempt.
517    ///
518    /// A zero duration (the default) disables the fallback deadline.
519    #[serde(default)]
520    pub request_timeout: Duration,
521
522    /// Hard size limit for inbound (decoded) requests. Consensus requests are
523    /// small (ref lists); large payloads belong to responses, bounded by
524    /// `message_size_limit`. A smaller inbound bound shrinks the memory a
525    /// single in-flight request can pin before its handler runs.
526    ///
527    /// `0` (the default) falls back to `message_size_limit`.
528    #[serde(default)]
529    pub max_inbound_message_size: usize,
530
531    /// Per-peer, per-RPC admission caps for the inbound consensus server.
532    #[serde(default)]
533    pub admission: AdmissionParameters,
534}
535
536impl TonicParameters {
537    fn default_keepalive_interval() -> Duration {
538        Duration::from_secs(5)
539    }
540
541    fn default_connection_buffer_size() -> usize {
542        32 << 20
543    }
544
545    fn default_excessive_message_size() -> usize {
546        16 << 20
547    }
548
549    fn default_message_size_limit() -> usize {
550        64 << 20
551    }
552
553    /// Fills the inbound resource bounds that are still at their inert
554    /// defaults with the protective preset (sized for ~100-validator
555    /// committees). Bounds an operator configured explicitly are kept, as are
556    /// the transport settings the preset does not cover (keepalive, buffers,
557    /// `message_size_limit`). A bound explicitly configured to its inert value
558    /// still receives the preset; running without a bound requires disabling
559    /// the preset itself (`CONSENSUS_GRPC_PROTECTIVE_LIMITS=0`).
560    pub fn apply_protective(&mut self) {
561        if self.max_concurrent_streams == 0 {
562            self.max_concurrent_streams = 64;
563        }
564        if self.request_timeout.is_zero() {
565            self.request_timeout = Duration::from_secs(120);
566        }
567        if self.max_inbound_message_size == 0 {
568            self.max_inbound_message_size = 1 << 20;
569        }
570        if self.admission.is_inert() {
571            self.admission = AdmissionParameters::protective();
572        }
573    }
574
575    /// The inert defaults with the protective bounds applied.
576    pub fn protective() -> Self {
577        let mut params = Self::default();
578        params.apply_protective();
579        params
580    }
581}
582
583impl Default for TonicParameters {
584    fn default() -> Self {
585        Self {
586            keepalive_interval: TonicParameters::default_keepalive_interval(),
587            connection_buffer_size: TonicParameters::default_connection_buffer_size(),
588            excessive_message_size: TonicParameters::default_excessive_message_size(),
589            message_size_limit: TonicParameters::default_message_size_limit(),
590            max_concurrent_streams: 0,
591            request_timeout: Duration::ZERO,
592            max_inbound_message_size: 0,
593            admission: AdmissionParameters::default(),
594        }
595    }
596}
597
598/// Per-peer, per-RPC concurrency caps for the inbound consensus gRPC server.
599///
600/// Each cap bounds how many concurrent requests of one RPC group a single
601/// committee peer (keyed on its authenticated authority index) may have in
602/// flight; a peer cannot consume another peer's budget. These are local,
603/// non-protocol parameters — heterogeneous values across authorities are safe,
604/// so they can be rolled out and tuned per node.
605///
606/// `0` (the default for every cap) disables admission for that group. At node
607/// start the protective preset fills the caps when all of them are left at
608/// `0`; a caps block with any explicitly configured value is kept as-is, and
609/// `CONSENSUS_GRPC_PROTECTIVE_LIMITS=0` disables the preset entirely. Preset
610/// values, sized for ~100-validator committees and the local synchronizer
611/// fan-out toward one server: subscriptions 2, header fetches 32, transaction
612/// fetches 16, commit fetches `commit_sync_parallel_fetches` (8).
613#[derive(Clone, Debug, Default, Deserialize, Serialize)]
614pub struct AdmissionParameters {
615    /// Max concurrent block-subscription streams per peer.
616    #[serde(default)]
617    pub max_subscriptions_per_peer: u32,
618
619    /// Max concurrent header fetches per peer
620    /// (`fetch_block_headers` + `fetch_latest_block_headers`).
621    #[serde(default)]
622    pub max_header_fetches_per_peer: u32,
623
624    /// Max concurrent transaction fetches per peer (`fetch_transactions`).
625    #[serde(default)]
626    pub max_transaction_fetches_per_peer: u32,
627
628    /// Max concurrent commit fetches per peer
629    /// (`fetch_commits` + `fetch_commits_and_transactions`).
630    #[serde(default)]
631    pub max_commit_fetches_per_peer: u32,
632}
633
634impl AdmissionParameters {
635    /// Preset sized for ~100-validator committees and the local synchronizer
636    /// fan-out toward one server.
637    pub fn protective() -> Self {
638        Self {
639            max_subscriptions_per_peer: 2,
640            max_header_fetches_per_peer: 32,
641            max_transaction_fetches_per_peer: 16,
642            max_commit_fetches_per_peer: Parameters::default_commit_sync_parallel_fetches() as u32,
643        }
644    }
645
646    /// True when every cap is `0`, i.e. admission control is disabled.
647    pub fn is_inert(&self) -> bool {
648        self.max_subscriptions_per_peer == 0
649            && self.max_header_fetches_per_peer == 0
650            && self.max_transaction_fetches_per_peer == 0
651            && self.max_commit_fetches_per_peer == 0
652    }
653}