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_EVENT_STREAM_QUERIES,
19 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: u64,
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 = "100")]
250 pub max_joined_tasks: usize,
251
252 #[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
255 pub max_event_stream_queries: usize,
256
257 #[arg(
259 long,
260 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
261 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
262 )]
263 pub max_accepted_latency_ms: f64,
264
265 #[arg(
267 long,
268 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
269 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
270 )]
271 pub cache_ttl_ms: u64,
272
273 #[arg(
275 long,
276 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
277 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
278 )]
279 pub cache_max_size: usize,
280
281 #[arg(
283 long,
284 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
285 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
286 )]
287 pub max_request_ttl_ms: u64,
288
289 #[arg(
295 long,
296 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
297 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
298 )]
299 pub alpha: f64,
300
301 #[arg(
304 long,
305 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
306 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
307 )]
308 pub alternative_peers_retry_delay_ms: u64,
309
310 #[serde(flatten)]
311 #[clap(flatten)]
312 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
313}
314
315impl Default for Options {
316 fn default() -> Self {
317 use clap::Parser;
318
319 #[derive(Parser)]
320 struct OptionsParser {
321 #[clap(flatten)]
322 options: Options,
323 }
324
325 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
326 .expect("Options has no required arguments")
327 .options
328 }
329}
330
331impl Options {
332 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
334 let message_policy = MessagePolicy {
335 blanket: self.blanket_message_policy,
336 restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
337 ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
338 reject_message_bundles_without_application_ids: self
339 .reject_message_bundles_without_application_ids
340 .clone(),
341 reject_message_bundles_with_other_application_ids: self
342 .reject_message_bundles_with_other_application_ids
343 .clone(),
344 process_events_from_application_ids: self.process_events_from_application_ids.clone(),
345 never_reject_application_ids: self
346 .never_reject_application_ids
347 .clone()
348 .unwrap_or_default(),
349 };
350 let cross_chain_message_delivery =
351 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
352 chain_client::Options {
353 max_pending_message_bundles: self.max_pending_message_bundles,
354 max_block_limit_errors: self.max_block_limit_errors,
355 staging_bundles_time_budget: self.staging_bundles_time_budget,
356 priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
357 message_policy,
358 cross_chain_message_delivery,
359 quorum_grace_period: self.quorum_grace_period,
360 blob_download_timeout: self.blob_download_timeout,
361 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
362 certificate_download_batch_size: self.certificate_download_batch_size,
363 certificate_upload_batch_size: self.certificate_upload_batch_size,
364 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
365 max_joined_tasks: self.max_joined_tasks,
366 allow_fast_blocks: self.allow_fast_blocks,
367 notification_circuit_breaker_initial_probe_interval: self
368 .notification_circuit_breaker_initial_probe_interval,
369 notification_circuit_breaker_max_probe_interval: self
370 .notification_circuit_breaker_max_probe_interval,
371 max_event_stream_queries: self.max_event_stream_queries,
372 }
373 }
374
375 #[cfg(not(web))]
377 pub(crate) fn to_timing_config(&self) -> TimingConfig {
378 TimingConfig {
379 enabled: self.timings,
380 report_interval_secs: self.timing_interval,
381 }
382 }
383
384 pub(crate) fn to_requests_scheduler_config(
386 &self,
387 ) -> linera_core::client::RequestsSchedulerConfig {
388 linera_core::client::RequestsSchedulerConfig {
389 max_accepted_latency_ms: self.max_accepted_latency_ms,
390 cache_ttl_ms: self.cache_ttl_ms,
391 cache_max_size: self.cache_max_size,
392 max_request_ttl_ms: self.max_request_ttl_ms,
393 alpha: self.alpha,
394 retry_delay_ms: self.alternative_peers_retry_delay_ms,
395 }
396 }
397}
398
399#[derive(Debug, Clone, clap::Args)]
400pub struct ChainOwnershipConfig {
401 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
409 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
410
411 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
414 pub owners: Option<BTreeMap<AccountOwner, u64>>,
415
416 #[arg(long, value_parser = util::parse_json::<Option<AccountOwner>>)]
419 pub first_leader: Option<std::option::Option<AccountOwner>>,
420
421 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
425 pub multi_leader_rounds: Option<std::option::Option<u32>>,
426
427 #[arg(long)]
431 pub open_multi_leader_rounds: bool,
432
433 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
436 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
437
438 #[arg(
441 long = "base-timeout-ms",
442 value_parser = util::parse_millis_delta
443 )]
444 pub base_timeout: Option<TimeDelta>,
445
446 #[arg(
449 long = "timeout-increment-ms",
450 value_parser = util::parse_millis_delta
451 )]
452 pub timeout_increment: Option<TimeDelta>,
453
454 #[arg(
458 long = "fallback-duration-ms",
459 value_parser = util::parse_millis_delta
460 )]
461 pub fallback_duration: Option<TimeDelta>,
462}
463
464impl ChainOwnershipConfig {
465 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
466 let ChainOwnershipConfig {
467 super_owners,
468 owners,
469 first_leader,
470 multi_leader_rounds,
471 fast_round_duration,
472 open_multi_leader_rounds,
473 base_timeout,
474 timeout_increment,
475 fallback_duration,
476 } = self;
477
478 if let Some(owners) = owners {
479 chain_ownership.owners = owners;
480 }
481
482 if let Some(super_owners) = super_owners {
483 chain_ownership.super_owners = super_owners.into_iter().collect();
484 }
485
486 if let Some(first_leader) = first_leader {
487 chain_ownership.first_leader = first_leader;
488 }
489 if let Some(multi_leader_rounds) = multi_leader_rounds {
490 chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
491 }
492
493 chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
494
495 if let Some(fast_round_duration) = fast_round_duration {
496 chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
497 }
498 if let Some(base_timeout) = base_timeout {
499 chain_ownership.timeout_config.base_timeout = base_timeout;
500 }
501 if let Some(timeout_increment) = timeout_increment {
502 chain_ownership.timeout_config.timeout_increment = timeout_increment;
503 }
504 if let Some(fallback_duration) = fallback_duration {
505 chain_ownership.timeout_config.fallback_duration = fallback_duration;
506 }
507
508 Ok(())
509 }
510}
511
512impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
513 type Error = Error;
514
515 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
516 let mut chain_ownership = ChainOwnership::default();
517 config.update(&mut chain_ownership)?;
518 Ok(chain_ownership)
519 }
520}
521
522#[derive(Debug, Clone, clap::Args)]
523pub struct ApplicationPermissionsConfig {
524 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
534 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
535 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
539 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
540 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
544 pub manage_chain: Option<std::vec::Vec<ApplicationId>>,
545 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
549 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
550 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
554 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
555}
556
557impl ApplicationPermissionsConfig {
558 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
559 if let Some(execute_operations) = self.execute_operations {
560 application_permissions.execute_operations = execute_operations;
561 }
562 if let Some(mandatory_applications) = self.mandatory_applications {
563 application_permissions.mandatory_applications = mandatory_applications;
564 }
565 if let Some(manage_chain) = self.manage_chain {
566 application_permissions.manage_chain = manage_chain;
567 }
568 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
569 application_permissions.call_service_as_oracle = call_service_as_oracle;
570 }
571 if let Some(make_http_requests) = self.make_http_requests {
572 application_permissions.make_http_requests = make_http_requests;
573 }
574 }
575}
576
577#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
578pub enum ResourceControlPolicyConfig {
579 NoFees,
580 Testnet,
581 #[cfg(with_testing)]
582 OnlyFuel,
583 #[cfg(with_testing)]
584 AllCategories,
585}
586
587impl ResourceControlPolicyConfig {
588 pub fn into_policy(self) -> ResourceControlPolicy {
589 match self {
590 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
591 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
592 #[cfg(with_testing)]
593 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
594 #[cfg(with_testing)]
595 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
596 }
597 }
598}
599
600impl std::str::FromStr for ResourceControlPolicyConfig {
601 type Err = String;
602
603 fn from_str(s: &str) -> Result<Self, Self::Err> {
604 clap::ValueEnum::from_str(s, true)
605 }
606}
607
608impl fmt::Display for ResourceControlPolicyConfig {
609 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
610 write!(f, "{self:?}")
611 }
612}