1use std::{
5 collections::{BTreeMap, HashSet},
6 fmt,
7};
8
9use linera_base::{
10 data_types::{ApplicationPermissions, BlanketMessagePolicy, MessagePolicy, TimeDelta},
11 identifiers::{AccountOwner, ApplicationId, ChainId, GenericApplicationId},
12 ownership::ChainOwnership,
13 time::Duration,
14};
15use linera_core::{
16 client::{
17 chain_client, DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
18 DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE, DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS,
19 DEFAULT_MAX_EVENT_STREAM_QUERIES, DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
20 },
21 node::CrossChainMessageDelivery,
22 DEFAULT_QUORUM_GRACE_PERIOD,
23};
24use linera_execution::ResourceControlPolicy;
25
26#[cfg(not(web))]
27use crate::client_metrics::TimingConfig;
28use crate::util;
29
30#[derive(Debug, thiserror::Error)]
31#[allow(missing_docs)]
32pub enum Error {
33 #[error("I/O error: {0}")]
34 IoError(#[from] std::io::Error),
35 #[error("there are {public_keys} public keys but {weights} weights")]
36 MisalignedWeights { public_keys: usize, weights: usize },
37 #[error("config error: {0}")]
38 Config(#[from] crate::config::GenesisConfigError),
39}
40
41util::impl_from_infallible!(Error);
42
43#[derive(Clone, clap::Parser, serde::Deserialize, tsify::Tsify)]
45#[tsify(from_wasm_abi)]
46#[group(skip)]
47#[serde(default, rename_all = "camelCase")]
48pub struct Options {
49 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
51 pub send_timeout: Duration,
52
53 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
55 pub recv_timeout: Duration,
56
57 #[arg(long, default_value = "300")]
59 pub max_pending_message_bundles: usize,
60
61 #[arg(long, default_value = "3")]
66 pub max_block_limit_errors: u32,
67
68 #[arg(long, default_value = "10")]
70 pub max_new_events_per_block: usize,
71
72 #[arg(long = "staging-bundles-time-budget-ms", value_parser = util::parse_millis)]
76 pub staging_bundles_time_budget: Option<Duration>,
77
78 #[arg(long, value_parser = util::parse_chain_set)]
80 pub prioritize_bundles_from: Option<HashSet<ChainId>>,
81
82 #[arg(long, value_parser = util::parse_chain_set)]
84 pub ignore_bundles_from: Option<HashSet<ChainId>>,
85
86 #[arg(
88 long = "chain-worker-ttl-ms",
89 default_value = "30000",
90 env = "LINERA_CHAIN_WORKER_TTL_MS",
91 value_parser = util::parse_millis,
92 )]
93 pub chain_worker_ttl: Duration,
94
95 #[arg(
98 long = "sender-chain-worker-ttl-ms",
99 default_value = "1000",
100 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
101 value_parser = util::parse_millis
102 )]
103 pub sender_chain_worker_ttl: Duration,
104
105 #[arg(long, default_value_t = 1000)]
108 pub cross_chain_batch_size_limit: usize,
109
110 #[arg(
112 long = "retry-delay-ms",
113 default_value = "1000",
114 value_parser = util::parse_millis
115 )]
116 pub retry_delay: Duration,
117
118 #[arg(long, default_value = "10")]
120 pub max_retries: u32,
121
122 #[arg(
124 long = "max-backoff-ms",
125 default_value = "30000",
126 value_parser = util::parse_millis
127 )]
128 pub max_backoff: Duration,
129
130 #[arg(
134 long = "notification-circuit-breaker-initial-probe-interval-ms",
135 default_value = "300000",
136 value_parser = util::parse_millis
137 )]
138 pub notification_circuit_breaker_initial_probe_interval: Duration,
139
140 #[arg(
143 long = "notification-circuit-breaker-max-probe-interval-ms",
144 default_value = "3600000",
145 value_parser = util::parse_millis
146 )]
147 pub notification_circuit_breaker_max_probe_interval: Duration,
148
149 #[arg(long)]
152 pub wait_for_outgoing_messages: bool,
153
154 #[arg(long)]
157 pub allow_fast_blocks: bool,
158
159 #[arg(long)]
161 pub long_lived_services: bool,
162
163 #[arg(long, default_value_t, value_enum)]
165 pub blanket_message_policy: BlanketMessagePolicy,
166
167 #[arg(long, value_parser = util::parse_chain_set)]
171 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
172
173 #[arg(long, value_parser = util::parse_app_set)]
176 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
177
178 #[arg(long, value_parser = util::parse_app_set)]
181 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
182
183 #[arg(long, value_parser = util::parse_app_set)]
186 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
187
188 #[arg(long, value_parser = util::parse_app_set)]
195 pub never_reject_application_ids: Option<HashSet<GenericApplicationId>>,
196
197 #[cfg(not(web))]
199 #[arg(long)]
200 pub timings: bool,
201
202 #[cfg(not(web))]
204 #[arg(long, default_value = "5")]
205 pub timing_interval: u64,
206
207 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
210 pub quorum_grace_period: f64,
211
212 #[arg(
214 long = "blob-download-hedge-delay-ms",
215 default_value = "1000",
216 value_parser = util::parse_millis,
217 )]
218 pub blob_download_hedge_delay: Duration,
219
220 #[arg(
223 long = "cert-batch-download-hedge-delay-ms",
224 default_value = "1000",
225 value_parser = util::parse_millis
226 )]
227 pub certificate_batch_download_hedge_delay: Duration,
228
229 #[arg(
232 long,
233 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
234 )]
235 pub certificate_download_batch_size: u64,
236
237 #[arg(
240 long,
241 default_value_t = DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE,
242 )]
243 pub certificate_upload_batch_size: u64,
244
245 #[arg(
248 long,
249 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
250 )]
251 pub sender_certificate_download_batch_size: usize,
252
253 #[arg(long, default_value_t = DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS)]
255 pub max_concurrent_batch_downloads: usize,
256
257 #[arg(long, default_value = "100")]
259 pub max_joined_tasks: usize,
260
261 #[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
264 pub max_event_stream_queries: usize,
265
266 #[arg(
268 long,
269 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
270 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
271 )]
272 pub max_accepted_latency_ms: f64,
273
274 #[arg(
276 long,
277 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
278 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
279 )]
280 pub cache_ttl_ms: u64,
281
282 #[arg(
284 long,
285 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
286 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
287 )]
288 pub cache_max_size: usize,
289
290 #[arg(
292 long,
293 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
294 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
295 )]
296 pub max_request_ttl_ms: u64,
297
298 #[arg(
304 long,
305 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
306 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
307 )]
308 pub alpha: f64,
309
310 #[arg(
313 long,
314 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
315 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
316 )]
317 pub alternative_peers_retry_delay_ms: u64,
318
319 #[serde(flatten)]
321 #[clap(flatten)]
322 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
323}
324
325impl Default for Options {
326 fn default() -> Self {
327 use clap::Parser;
328
329 #[derive(Parser)]
330 struct OptionsParser {
331 #[clap(flatten)]
332 options: Options,
333 }
334
335 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
336 .expect("Options has no required arguments")
337 .options
338 }
339}
340
341impl Options {
342 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
344 let message_policy = MessagePolicy {
345 blanket: self.blanket_message_policy,
346 restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
347 ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
348 reject_message_bundles_without_application_ids: self
349 .reject_message_bundles_without_application_ids
350 .clone(),
351 reject_message_bundles_with_other_application_ids: self
352 .reject_message_bundles_with_other_application_ids
353 .clone(),
354 process_events_from_application_ids: self.process_events_from_application_ids.clone(),
355 never_reject_application_ids: self
356 .never_reject_application_ids
357 .clone()
358 .unwrap_or_default(),
359 };
360 let cross_chain_message_delivery =
361 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
362 chain_client::Options {
363 max_pending_message_bundles: self.max_pending_message_bundles,
364 max_block_limit_errors: self.max_block_limit_errors,
365 max_new_events_per_block: self.max_new_events_per_block,
366 staging_bundles_time_budget: self.staging_bundles_time_budget,
367 priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
368 message_policy,
369 cross_chain_message_delivery,
370 quorum_grace_period: self.quorum_grace_period,
371 blob_download_hedge_delay: self.blob_download_hedge_delay,
372 certificate_batch_download_hedge_delay: self.certificate_batch_download_hedge_delay,
373 certificate_download_batch_size: self.certificate_download_batch_size,
374 certificate_upload_batch_size: self.certificate_upload_batch_size,
375 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
376 max_concurrent_batch_downloads: self.max_concurrent_batch_downloads,
377 max_joined_tasks: self.max_joined_tasks,
378 allow_fast_blocks: self.allow_fast_blocks,
379 notification_circuit_breaker_initial_probe_interval: self
380 .notification_circuit_breaker_initial_probe_interval,
381 notification_circuit_breaker_max_probe_interval: self
382 .notification_circuit_breaker_max_probe_interval,
383 max_event_stream_queries: self.max_event_stream_queries,
384 }
385 }
386
387 #[cfg(not(web))]
389 pub(crate) fn to_timing_config(&self) -> TimingConfig {
390 TimingConfig {
391 enabled: self.timings,
392 report_interval_secs: self.timing_interval,
393 }
394 }
395
396 pub(crate) fn to_requests_scheduler_config(
398 &self,
399 ) -> linera_core::client::RequestsSchedulerConfig {
400 linera_core::client::RequestsSchedulerConfig {
401 max_accepted_latency_ms: self.max_accepted_latency_ms,
402 cache_ttl_ms: self.cache_ttl_ms,
403 cache_max_size: self.cache_max_size,
404 max_request_ttl_ms: self.max_request_ttl_ms,
405 alpha: self.alpha,
406 retry_delay_ms: self.alternative_peers_retry_delay_ms,
407 }
408 }
409}
410
411#[derive(Debug, Clone, clap::Args)]
413pub struct ChainOwnershipConfig {
414 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
422 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
423
424 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
427 pub owners: Option<BTreeMap<AccountOwner, u64>>,
428
429 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
433 pub multi_leader_rounds: Option<std::option::Option<u32>>,
434
435 #[arg(long)]
439 pub open_multi_leader_rounds: bool,
440
441 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
444 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
445
446 #[arg(
449 long = "base-timeout-ms",
450 value_parser = util::parse_millis_delta
451 )]
452 pub base_timeout: Option<TimeDelta>,
453
454 #[arg(
457 long = "timeout-increment-ms",
458 value_parser = util::parse_millis_delta
459 )]
460 pub timeout_increment: Option<TimeDelta>,
461
462 #[arg(
466 long = "fallback-duration-ms",
467 value_parser = util::parse_millis_delta
468 )]
469 pub fallback_duration: Option<TimeDelta>,
470}
471
472impl ChainOwnershipConfig {
473 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
475 let ChainOwnershipConfig {
476 super_owners,
477 owners,
478 multi_leader_rounds,
479 fast_round_duration,
480 open_multi_leader_rounds,
481 base_timeout,
482 timeout_increment,
483 fallback_duration,
484 } = self;
485
486 if let Some(owners) = owners {
487 chain_ownership.owners = owners;
488 }
489
490 if let Some(super_owners) = super_owners {
491 chain_ownership.super_owners = super_owners.into_iter().collect();
492 }
493
494 if let Some(multi_leader_rounds) = multi_leader_rounds {
495 chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
496 }
497
498 chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
499
500 if let Some(fast_round_duration) = fast_round_duration {
501 chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
502 }
503 if let Some(base_timeout) = base_timeout {
504 chain_ownership.timeout_config.base_timeout = base_timeout;
505 }
506 if let Some(timeout_increment) = timeout_increment {
507 chain_ownership.timeout_config.timeout_increment = timeout_increment;
508 }
509 if let Some(fallback_duration) = fallback_duration {
510 chain_ownership.timeout_config.fallback_duration = fallback_duration;
511 }
512
513 Ok(())
514 }
515}
516
517impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
518 type Error = Error;
519
520 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
521 let mut chain_ownership = ChainOwnership::default();
522 config.update(&mut chain_ownership)?;
523 Ok(chain_ownership)
524 }
525}
526
527#[derive(Debug, Clone, clap::Args)]
529pub struct ApplicationPermissionsConfig {
530 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
540 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
541 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
545 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
546 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
549 pub close_chain: Option<std::vec::Vec<ApplicationId>>,
550 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
553 pub change_application_permissions: Option<std::vec::Vec<ApplicationId>>,
554 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
558 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
559 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
563 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
564}
565
566impl ApplicationPermissionsConfig {
567 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
569 if let Some(execute_operations) = self.execute_operations {
570 application_permissions.execute_operations = execute_operations;
571 }
572 if let Some(mandatory_applications) = self.mandatory_applications {
573 application_permissions.mandatory_applications = mandatory_applications;
574 }
575 if let Some(close_chain) = self.close_chain {
576 application_permissions.close_chain = close_chain;
577 }
578 if let Some(change_application_permissions) = self.change_application_permissions {
579 application_permissions.change_application_permissions = change_application_permissions;
580 }
581 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
582 application_permissions.call_service_as_oracle = call_service_as_oracle;
583 }
584 if let Some(make_http_requests) = self.make_http_requests {
585 application_permissions.make_http_requests = make_http_requests;
586 }
587 }
588}
589
590#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
592pub enum ResourceControlPolicyConfig {
593 NoFees,
595 Testnet,
597 #[cfg(with_testing)]
599 OnlyFuel,
600 #[cfg(with_testing)]
602 AllCategories,
603}
604
605impl ResourceControlPolicyConfig {
606 pub fn into_policy(self) -> ResourceControlPolicy {
608 match self {
609 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
610 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
611 #[cfg(with_testing)]
612 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
613 #[cfg(with_testing)]
614 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
615 }
616 }
617}
618
619impl std::str::FromStr for ResourceControlPolicyConfig {
620 type Err = String;
621
622 fn from_str(s: &str) -> Result<Self, Self::Err> {
623 clap::ValueEnum::from_str(s, true)
624 }
625}
626
627impl fmt::Display for ResourceControlPolicyConfig {
628 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629 write!(f, "{self:?}")
630 }
631}