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}