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)]
31pub enum Error {
32 #[error("I/O error: {0}")]
33 IoError(#[from] std::io::Error),
34 #[error("there are {public_keys} public keys but {weights} weights")]
35 MisalignedWeights { public_keys: usize, weights: usize },
36 #[error("config error: {0}")]
37 Config(#[from] crate::config::GenesisConfigError),
38}
39
40util::impl_from_infallible!(Error);
41
42#[derive(Clone, clap::Parser, serde::Deserialize, tsify::Tsify)]
43#[tsify(from_wasm_abi)]
44#[group(skip)]
45#[serde(default, rename_all = "camelCase")]
46pub struct Options {
47 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
49 pub send_timeout: Duration,
50
51 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
53 pub recv_timeout: Duration,
54
55 #[arg(long, default_value = "300")]
57 pub max_pending_message_bundles: usize,
58
59 #[arg(long, default_value = "3")]
64 pub max_block_limit_errors: u32,
65
66 #[arg(long = "staging-bundles-time-budget-ms", value_parser = util::parse_millis)]
70 pub staging_bundles_time_budget: Option<Duration>,
71
72 #[arg(long, value_parser = util::parse_chain_set)]
74 pub prioritize_bundles_from: Option<HashSet<ChainId>>,
75
76 #[arg(long, value_parser = util::parse_chain_set)]
78 pub ignore_bundles_from: Option<HashSet<ChainId>>,
79
80 #[arg(
83 long = "chain-worker-ttl-ms",
84 default_value = "30000",
85 env = "LINERA_CHAIN_WORKER_TTL_MS",
86 value_parser = util::parse_millis,
87 )]
88 pub chain_worker_ttl: Duration,
89
90 #[arg(
93 long = "sender-chain-worker-ttl-ms",
94 default_value = "1000",
95 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
96 value_parser = util::parse_millis
97 )]
98 pub sender_chain_worker_ttl: Duration,
99
100 #[arg(long, default_value_t = 1000)]
103 pub cross_chain_batch_size_limit: usize,
104
105 #[arg(
107 long = "retry-delay-ms",
108 default_value = "1000",
109 value_parser = util::parse_millis
110 )]
111 pub retry_delay: Duration,
112
113 #[arg(long, default_value = "10")]
115 pub max_retries: u32,
116
117 #[arg(
119 long = "max-backoff-ms",
120 default_value = "30000",
121 value_parser = util::parse_millis
122 )]
123 pub max_backoff: Duration,
124
125 #[arg(
129 long = "notification-circuit-breaker-initial-probe-interval-ms",
130 default_value = "300000",
131 value_parser = util::parse_millis
132 )]
133 pub notification_circuit_breaker_initial_probe_interval: Duration,
134
135 #[arg(
138 long = "notification-circuit-breaker-max-probe-interval-ms",
139 default_value = "3600000",
140 value_parser = util::parse_millis
141 )]
142 pub notification_circuit_breaker_max_probe_interval: Duration,
143
144 #[arg(long)]
147 pub wait_for_outgoing_messages: bool,
148
149 #[arg(long)]
152 pub allow_fast_blocks: bool,
153
154 #[arg(long)]
156 pub long_lived_services: bool,
157
158 #[arg(long, default_value_t, value_enum)]
160 pub blanket_message_policy: BlanketMessagePolicy,
161
162 #[arg(long, value_parser = util::parse_chain_set)]
166 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
167
168 #[arg(long, value_parser = util::parse_app_set)]
171 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
172
173 #[arg(long, value_parser = util::parse_app_set)]
176 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
177
178 #[arg(long, value_parser = util::parse_app_set)]
181 pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
182
183 #[arg(long, value_parser = util::parse_app_set)]
190 pub never_reject_application_ids: Option<HashSet<GenericApplicationId>>,
191
192 #[cfg(not(web))]
194 #[arg(long)]
195 pub timings: bool,
196
197 #[cfg(not(web))]
199 #[arg(long, default_value = "5")]
200 pub timing_interval: u64,
201
202 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
205 pub quorum_grace_period: f64,
206
207 #[arg(
209 long = "blob-download-timeout-ms",
210 default_value = "1000",
211 value_parser = util::parse_millis,
212 )]
213 pub blob_download_timeout: Duration,
214
215 #[arg(
218 long = "cert-batch-download-timeout-ms",
219 default_value = "1000",
220 value_parser = util::parse_millis
221 )]
222 pub certificate_batch_download_timeout: Duration,
223
224 #[arg(
227 long,
228 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
229 )]
230 pub certificate_download_batch_size: u64,
231
232 #[arg(
235 long,
236 default_value_t = DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE,
237 )]
238 pub certificate_upload_batch_size: usize,
239
240 #[arg(
243 long,
244 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
245 )]
246 pub sender_certificate_download_batch_size: usize,
247
248 #[arg(long, default_value_t = DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS)]
250 pub max_concurrent_batch_downloads: usize,
251
252 #[arg(long, default_value = "100")]
254 pub max_joined_tasks: usize,
255
256 #[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
259 pub max_event_stream_queries: usize,
260
261 #[arg(
263 long,
264 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
265 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
266 )]
267 pub max_accepted_latency_ms: f64,
268
269 #[arg(
271 long,
272 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
273 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
274 )]
275 pub cache_ttl_ms: u64,
276
277 #[arg(
279 long,
280 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
281 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
282 )]
283 pub cache_max_size: usize,
284
285 #[arg(
287 long,
288 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
289 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
290 )]
291 pub max_request_ttl_ms: u64,
292
293 #[arg(
299 long,
300 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
301 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
302 )]
303 pub alpha: f64,
304
305 #[arg(
308 long,
309 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
310 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
311 )]
312 pub alternative_peers_retry_delay_ms: u64,
313
314 #[serde(flatten)]
315 #[clap(flatten)]
316 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
317}
318
319impl Default for Options {
320 fn default() -> Self {
321 use clap::Parser;
322
323 #[derive(Parser)]
324 struct OptionsParser {
325 #[clap(flatten)]
326 options: Options,
327 }
328
329 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
330 .expect("Options has no required arguments")
331 .options
332 }
333}
334
335impl Options {
336 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
338 let message_policy = MessagePolicy {
339 blanket: self.blanket_message_policy,
340 restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
341 ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
342 reject_message_bundles_without_application_ids: self
343 .reject_message_bundles_without_application_ids
344 .clone(),
345 reject_message_bundles_with_other_application_ids: self
346 .reject_message_bundles_with_other_application_ids
347 .clone(),
348 process_events_from_application_ids: self.process_events_from_application_ids.clone(),
349 never_reject_application_ids: self
350 .never_reject_application_ids
351 .clone()
352 .unwrap_or_default(),
353 };
354 let cross_chain_message_delivery =
355 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
356 chain_client::Options {
357 max_pending_message_bundles: self.max_pending_message_bundles,
358 max_block_limit_errors: self.max_block_limit_errors,
359 staging_bundles_time_budget: self.staging_bundles_time_budget,
360 priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
361 message_policy,
362 cross_chain_message_delivery,
363 quorum_grace_period: self.quorum_grace_period,
364 blob_download_timeout: self.blob_download_timeout,
365 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
366 certificate_download_batch_size: self.certificate_download_batch_size,
367 certificate_upload_batch_size: self.certificate_upload_batch_size,
368 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
369 max_concurrent_batch_downloads: self.max_concurrent_batch_downloads,
370 max_joined_tasks: self.max_joined_tasks,
371 allow_fast_blocks: self.allow_fast_blocks,
372 notification_circuit_breaker_initial_probe_interval: self
373 .notification_circuit_breaker_initial_probe_interval,
374 notification_circuit_breaker_max_probe_interval: self
375 .notification_circuit_breaker_max_probe_interval,
376 max_event_stream_queries: self.max_event_stream_queries,
377 }
378 }
379
380 #[cfg(not(web))]
382 pub(crate) fn to_timing_config(&self) -> TimingConfig {
383 TimingConfig {
384 enabled: self.timings,
385 report_interval_secs: self.timing_interval,
386 }
387 }
388
389 pub(crate) fn to_requests_scheduler_config(
391 &self,
392 ) -> linera_core::client::RequestsSchedulerConfig {
393 linera_core::client::RequestsSchedulerConfig {
394 max_accepted_latency_ms: self.max_accepted_latency_ms,
395 cache_ttl_ms: self.cache_ttl_ms,
396 cache_max_size: self.cache_max_size,
397 max_request_ttl_ms: self.max_request_ttl_ms,
398 alpha: self.alpha,
399 retry_delay_ms: self.alternative_peers_retry_delay_ms,
400 }
401 }
402}
403
404#[derive(Debug, Clone, clap::Args)]
405pub struct ChainOwnershipConfig {
406 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
414 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
415
416 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
419 pub owners: Option<BTreeMap<AccountOwner, u64>>,
420
421 #[arg(long, value_parser = util::parse_json::<Option<AccountOwner>>)]
424 pub first_leader: Option<std::option::Option<AccountOwner>>,
425
426 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
430 pub multi_leader_rounds: Option<std::option::Option<u32>>,
431
432 #[arg(long)]
436 pub open_multi_leader_rounds: bool,
437
438 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
441 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
442
443 #[arg(
446 long = "base-timeout-ms",
447 value_parser = util::parse_millis_delta
448 )]
449 pub base_timeout: Option<TimeDelta>,
450
451 #[arg(
454 long = "timeout-increment-ms",
455 value_parser = util::parse_millis_delta
456 )]
457 pub timeout_increment: Option<TimeDelta>,
458
459 #[arg(
463 long = "fallback-duration-ms",
464 value_parser = util::parse_millis_delta
465 )]
466 pub fallback_duration: Option<TimeDelta>,
467}
468
469impl ChainOwnershipConfig {
470 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
471 let ChainOwnershipConfig {
472 super_owners,
473 owners,
474 first_leader,
475 multi_leader_rounds,
476 fast_round_duration,
477 open_multi_leader_rounds,
478 base_timeout,
479 timeout_increment,
480 fallback_duration,
481 } = self;
482
483 if let Some(owners) = owners {
484 chain_ownership.owners = owners;
485 }
486
487 if let Some(super_owners) = super_owners {
488 chain_ownership.super_owners = super_owners.into_iter().collect();
489 }
490
491 if let Some(first_leader) = first_leader {
492 chain_ownership.first_leader = first_leader;
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)]
528pub struct ApplicationPermissionsConfig {
529 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
539 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
540 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
544 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
545 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
549 pub manage_chain: Option<std::vec::Vec<ApplicationId>>,
550 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
554 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
555 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
559 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
560}
561
562impl ApplicationPermissionsConfig {
563 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
564 if let Some(execute_operations) = self.execute_operations {
565 application_permissions.execute_operations = execute_operations;
566 }
567 if let Some(mandatory_applications) = self.mandatory_applications {
568 application_permissions.mandatory_applications = mandatory_applications;
569 }
570 if let Some(manage_chain) = self.manage_chain {
571 application_permissions.manage_chain = manage_chain;
572 }
573 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
574 application_permissions.call_service_as_oracle = call_service_as_oracle;
575 }
576 if let Some(make_http_requests) = self.make_http_requests {
577 application_permissions.make_http_requests = make_http_requests;
578 }
579 }
580}
581
582#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
583pub enum ResourceControlPolicyConfig {
584 NoFees,
585 Testnet,
586 #[cfg(with_testing)]
587 OnlyFuel,
588 #[cfg(with_testing)]
589 AllCategories,
590}
591
592impl ResourceControlPolicyConfig {
593 pub fn into_policy(self) -> ResourceControlPolicy {
594 match self {
595 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
596 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
597 #[cfg(with_testing)]
598 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
599 #[cfg(with_testing)]
600 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
601 }
602 }
603}
604
605impl std::str::FromStr for ResourceControlPolicyConfig {
606 type Err = String;
607
608 fn from_str(s: &str) -> Result<Self, Self::Err> {
609 clap::ValueEnum::from_str(s, true)
610 }
611}
612
613impl fmt::Display for ResourceControlPolicyConfig {
614 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
615 write!(f, "{self:?}")
616 }
617}