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 = "staging-bundles-time-budget-ms", value_parser = util::parse_millis)]
72 pub staging_bundles_time_budget: Option<Duration>,
73
74 #[arg(long, value_parser = util::parse_chain_set)]
76 pub prioritize_bundles_from: Option<HashSet<ChainId>>,
77
78 #[arg(long, value_parser = util::parse_chain_set)]
80 pub ignore_bundles_from: Option<HashSet<ChainId>>,
81
82 #[arg(
85 long = "chain-worker-ttl-ms",
86 default_value = "30000",
87 env = "LINERA_CHAIN_WORKER_TTL_MS",
88 value_parser = util::parse_millis,
89 )]
90 pub chain_worker_ttl: Duration,
91
92 #[arg(
95 long = "sender-chain-worker-ttl-ms",
96 default_value = "1000",
97 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
98 value_parser = util::parse_millis
99 )]
100 pub sender_chain_worker_ttl: Duration,
101
102 #[arg(long, default_value_t = 1000)]
105 pub cross_chain_batch_size_limit: usize,
106
107 #[arg(
109 long = "retry-delay-ms",
110 default_value = "1000",
111 value_parser = util::parse_millis
112 )]
113 pub retry_delay: Duration,
114
115 #[arg(long, default_value = "10")]
117 pub max_retries: u32,
118
119 #[arg(
121 long = "max-backoff-ms",
122 default_value = "30000",
123 value_parser = util::parse_millis
124 )]
125 pub max_backoff: Duration,
126
127 #[arg(
131 long = "notification-circuit-breaker-initial-probe-interval-ms",
132 default_value = "300000",
133 value_parser = util::parse_millis
134 )]
135 pub notification_circuit_breaker_initial_probe_interval: Duration,
136
137 #[arg(
140 long = "notification-circuit-breaker-max-probe-interval-ms",
141 default_value = "3600000",
142 value_parser = util::parse_millis
143 )]
144 pub notification_circuit_breaker_max_probe_interval: Duration,
145
146 #[arg(long)]
149 pub wait_for_outgoing_messages: bool,
150
151 #[arg(long)]
154 pub allow_fast_blocks: bool,
155
156 #[arg(long)]
158 pub long_lived_services: bool,
159
160 #[arg(long, default_value_t, value_enum)]
162 pub blanket_message_policy: BlanketMessagePolicy,
163
164 #[arg(long, value_parser = util::parse_chain_set)]
168 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
169
170 #[arg(long, value_parser = util::parse_app_set)]
173 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
174
175 #[arg(long, value_parser = util::parse_app_set)]
178 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
179
180 #[arg(long, value_parser = util::parse_app_set)]
183 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
184
185 #[arg(long, value_parser = util::parse_app_set)]
192 pub never_reject_application_ids: Option<HashSet<GenericApplicationId>>,
193
194 #[cfg(not(web))]
196 #[arg(long)]
197 pub timings: bool,
198
199 #[cfg(not(web))]
201 #[arg(long, default_value = "5")]
202 pub timing_interval: u64,
203
204 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
207 pub quorum_grace_period: f64,
208
209 #[arg(
211 long = "blob-download-hedge-delay-ms",
212 default_value = "1000",
213 value_parser = util::parse_millis,
214 )]
215 pub blob_download_hedge_delay: Duration,
216
217 #[arg(
220 long = "cert-batch-download-hedge-delay-ms",
221 default_value = "1000",
222 value_parser = util::parse_millis
223 )]
224 pub certificate_batch_download_hedge_delay: Duration,
225
226 #[arg(
229 long,
230 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
231 )]
232 pub certificate_download_batch_size: u64,
233
234 #[arg(
237 long,
238 default_value_t = DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE,
239 )]
240 pub certificate_upload_batch_size: usize,
241
242 #[arg(
245 long,
246 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
247 )]
248 pub sender_certificate_download_batch_size: usize,
249
250 #[arg(long, default_value_t = DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS)]
252 pub max_concurrent_batch_downloads: usize,
253
254 #[arg(long, default_value = "100")]
256 pub max_joined_tasks: usize,
257
258 #[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
261 pub max_event_stream_queries: usize,
262
263 #[arg(
265 long,
266 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
267 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
268 )]
269 pub max_accepted_latency_ms: f64,
270
271 #[arg(
273 long,
274 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
275 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
276 )]
277 pub cache_ttl_ms: u64,
278
279 #[arg(
281 long,
282 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
283 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
284 )]
285 pub cache_max_size: usize,
286
287 #[arg(
289 long,
290 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
291 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
292 )]
293 pub max_request_ttl_ms: u64,
294
295 #[arg(
301 long,
302 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
303 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
304 )]
305 pub alpha: f64,
306
307 #[arg(
310 long,
311 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
312 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
313 )]
314 pub alternative_peers_retry_delay_ms: u64,
315
316 #[serde(flatten)]
318 #[clap(flatten)]
319 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
320}
321
322impl Default for Options {
323 fn default() -> Self {
324 use clap::Parser;
325
326 #[derive(Parser)]
327 struct OptionsParser {
328 #[clap(flatten)]
329 options: Options,
330 }
331
332 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
333 .expect("Options has no required arguments")
334 .options
335 }
336}
337
338impl Options {
339 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
341 let message_policy = MessagePolicy {
342 blanket: self.blanket_message_policy,
343 restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
344 ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
345 reject_message_bundles_without_application_ids: self
346 .reject_message_bundles_without_application_ids
347 .clone(),
348 reject_message_bundles_with_other_application_ids: self
349 .reject_message_bundles_with_other_application_ids
350 .clone(),
351 process_events_from_application_ids: self.process_events_from_application_ids.clone(),
352 never_reject_application_ids: self
353 .never_reject_application_ids
354 .clone()
355 .unwrap_or_default(),
356 };
357 let cross_chain_message_delivery =
358 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
359 chain_client::Options {
360 max_pending_message_bundles: self.max_pending_message_bundles,
361 max_block_limit_errors: self.max_block_limit_errors,
362 staging_bundles_time_budget: self.staging_bundles_time_budget,
363 priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
364 message_policy,
365 cross_chain_message_delivery,
366 quorum_grace_period: self.quorum_grace_period,
367 blob_download_hedge_delay: self.blob_download_hedge_delay,
368 certificate_batch_download_hedge_delay: self.certificate_batch_download_hedge_delay,
369 certificate_download_batch_size: self.certificate_download_batch_size,
370 certificate_upload_batch_size: self.certificate_upload_batch_size,
371 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
372 max_concurrent_batch_downloads: self.max_concurrent_batch_downloads,
373 max_joined_tasks: self.max_joined_tasks,
374 allow_fast_blocks: self.allow_fast_blocks,
375 notification_circuit_breaker_initial_probe_interval: self
376 .notification_circuit_breaker_initial_probe_interval,
377 notification_circuit_breaker_max_probe_interval: self
378 .notification_circuit_breaker_max_probe_interval,
379 max_event_stream_queries: self.max_event_stream_queries,
380 }
381 }
382
383 #[cfg(not(web))]
385 pub(crate) fn to_timing_config(&self) -> TimingConfig {
386 TimingConfig {
387 enabled: self.timings,
388 report_interval_secs: self.timing_interval,
389 }
390 }
391
392 pub(crate) fn to_requests_scheduler_config(
394 &self,
395 ) -> linera_core::client::RequestsSchedulerConfig {
396 linera_core::client::RequestsSchedulerConfig {
397 max_accepted_latency_ms: self.max_accepted_latency_ms,
398 cache_ttl_ms: self.cache_ttl_ms,
399 cache_max_size: self.cache_max_size,
400 max_request_ttl_ms: self.max_request_ttl_ms,
401 alpha: self.alpha,
402 retry_delay_ms: self.alternative_peers_retry_delay_ms,
403 }
404 }
405}
406
407#[derive(Debug, Clone, clap::Args)]
409pub struct ChainOwnershipConfig {
410 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
418 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
419
420 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
423 pub owners: Option<BTreeMap<AccountOwner, u64>>,
424
425 #[arg(long, value_parser = util::parse_json::<Option<AccountOwner>>)]
428 pub first_leader: Option<std::option::Option<AccountOwner>>,
429
430 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
434 pub multi_leader_rounds: Option<std::option::Option<u32>>,
435
436 #[arg(long)]
440 pub open_multi_leader_rounds: bool,
441
442 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
445 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
446
447 #[arg(
450 long = "base-timeout-ms",
451 value_parser = util::parse_millis_delta
452 )]
453 pub base_timeout: Option<TimeDelta>,
454
455 #[arg(
458 long = "timeout-increment-ms",
459 value_parser = util::parse_millis_delta
460 )]
461 pub timeout_increment: Option<TimeDelta>,
462
463 #[arg(
467 long = "fallback-duration-ms",
468 value_parser = util::parse_millis_delta
469 )]
470 pub fallback_duration: Option<TimeDelta>,
471}
472
473impl ChainOwnershipConfig {
474 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
476 let ChainOwnershipConfig {
477 super_owners,
478 owners,
479 first_leader,
480 multi_leader_rounds,
481 fast_round_duration,
482 open_multi_leader_rounds,
483 base_timeout,
484 timeout_increment,
485 fallback_duration,
486 } = self;
487
488 if let Some(owners) = owners {
489 chain_ownership.owners = owners;
490 }
491
492 if let Some(super_owners) = super_owners {
493 chain_ownership.super_owners = super_owners.into_iter().collect();
494 }
495
496 if let Some(first_leader) = first_leader {
497 chain_ownership.first_leader = first_leader;
498 }
499 if let Some(multi_leader_rounds) = multi_leader_rounds {
500 chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
501 }
502
503 chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
504
505 if let Some(fast_round_duration) = fast_round_duration {
506 chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
507 }
508 if let Some(base_timeout) = base_timeout {
509 chain_ownership.timeout_config.base_timeout = base_timeout;
510 }
511 if let Some(timeout_increment) = timeout_increment {
512 chain_ownership.timeout_config.timeout_increment = timeout_increment;
513 }
514 if let Some(fallback_duration) = fallback_duration {
515 chain_ownership.timeout_config.fallback_duration = fallback_duration;
516 }
517
518 Ok(())
519 }
520}
521
522impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
523 type Error = Error;
524
525 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
526 let mut chain_ownership = ChainOwnership::default();
527 config.update(&mut chain_ownership)?;
528 Ok(chain_ownership)
529 }
530}
531
532#[derive(Debug, Clone, clap::Args)]
534pub struct ApplicationPermissionsConfig {
535 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
545 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
546 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
550 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
551 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
555 pub manage_chain: Option<std::vec::Vec<ApplicationId>>,
556 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
560 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
561 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
565 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
566}
567
568impl ApplicationPermissionsConfig {
569 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
571 if let Some(execute_operations) = self.execute_operations {
572 application_permissions.execute_operations = execute_operations;
573 }
574 if let Some(mandatory_applications) = self.mandatory_applications {
575 application_permissions.mandatory_applications = mandatory_applications;
576 }
577 if let Some(manage_chain) = self.manage_chain {
578 application_permissions.manage_chain = manage_chain;
579 }
580 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
581 application_permissions.call_service_as_oracle = call_service_as_oracle;
582 }
583 if let Some(make_http_requests) = self.make_http_requests {
584 application_permissions.make_http_requests = make_http_requests;
585 }
586 }
587}
588
589#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
591pub enum ResourceControlPolicyConfig {
592 NoFees,
594 Testnet,
596 #[cfg(with_testing)]
598 OnlyFuel,
599 #[cfg(with_testing)]
601 AllCategories,
602}
603
604impl ResourceControlPolicyConfig {
605 pub fn into_policy(self) -> ResourceControlPolicy {
607 match self {
608 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
609 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
610 #[cfg(with_testing)]
611 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
612 #[cfg(with_testing)]
613 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
614 }
615 }
616}
617
618impl std::str::FromStr for ResourceControlPolicyConfig {
619 type Err = String;
620
621 fn from_str(s: &str) -> Result<Self, Self::Err> {
622 clap::ValueEnum::from_str(s, true)
623 }
624}
625
626impl fmt::Display for ResourceControlPolicyConfig {
627 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
628 write!(f, "{self:?}")
629 }
630}