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_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
19 },
20 node::CrossChainMessageDelivery,
21 DEFAULT_QUORUM_GRACE_PERIOD,
22};
23use linera_execution::ResourceControlPolicy;
24
25#[cfg(not(web))]
26use crate::client_metrics::TimingConfig;
27use crate::util;
28
29#[derive(Debug, thiserror::Error)]
30pub enum Error {
31 #[error("I/O error: {0}")]
32 IoError(#[from] std::io::Error),
33 #[error("there are {public_keys} public keys but {weights} weights")]
34 MisalignedWeights { public_keys: usize, weights: usize },
35 #[error("config error: {0}")]
36 Config(#[from] crate::config::Error),
37}
38
39util::impl_from_infallible!(Error);
40
41#[derive(Clone, clap::Parser, serde::Deserialize, tsify::Tsify)]
42#[tsify(from_wasm_abi)]
43#[group(skip)]
44#[serde(default, rename_all = "camelCase")]
45pub struct Options {
46 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
48 pub send_timeout: Duration,
49
50 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
52 pub recv_timeout: Duration,
53
54 #[arg(long, default_value = "10")]
56 pub max_pending_message_bundles: usize,
57
58 #[arg(
60 long = "chain-worker-ttl-ms",
61 default_value = "30000",
62 env = "LINERA_CHAIN_WORKER_TTL_MS",
63 value_parser = util::parse_millis,
64 )]
65 pub chain_worker_ttl: Duration,
66
67 #[arg(
70 long = "sender-chain-worker-ttl-ms",
71 default_value = "1000",
72 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
73 value_parser = util::parse_millis
74 )]
75 pub sender_chain_worker_ttl: Duration,
76
77 #[arg(
79 long = "retry-delay-ms",
80 default_value = "1000",
81 value_parser = util::parse_millis
82 )]
83 pub retry_delay: Duration,
84
85 #[arg(long, default_value = "10")]
87 pub max_retries: u32,
88
89 #[arg(long)]
92 pub wait_for_outgoing_messages: bool,
93
94 #[arg(long)]
97 pub allow_fast_blocks: bool,
98
99 #[arg(long)]
101 pub long_lived_services: bool,
102
103 #[arg(long, default_value_t, value_enum)]
105 pub blanket_message_policy: BlanketMessagePolicy,
106
107 #[arg(long, value_parser = util::parse_chain_set)]
111 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
112
113 #[arg(long, value_parser = util::parse_app_set)]
116 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
117
118 #[arg(long, value_parser = util::parse_app_set)]
121 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
122
123 #[cfg(not(web))]
125 #[arg(long)]
126 pub timings: bool,
127
128 #[cfg(not(web))]
130 #[arg(long, default_value = "5")]
131 pub timing_interval: u64,
132
133 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
136 pub quorum_grace_period: f64,
137
138 #[arg(
140 long = "blob-download-timeout-ms",
141 default_value = "1000",
142 value_parser = util::parse_millis,
143 )]
144 pub blob_download_timeout: Duration,
145
146 #[arg(
149 long = "cert-batch-download-timeout-ms",
150 default_value = "1000",
151 value_parser = util::parse_millis
152 )]
153 pub certificate_batch_download_timeout: Duration,
154
155 #[arg(
158 long,
159 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
160 )]
161 pub certificate_download_batch_size: u64,
162
163 #[arg(
166 long,
167 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
168 )]
169 pub sender_certificate_download_batch_size: usize,
170
171 #[arg(long, default_value = "100")]
173 pub max_joined_tasks: usize,
174
175 #[arg(
177 long,
178 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
179 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
180 )]
181 pub max_accepted_latency_ms: f64,
182
183 #[arg(
185 long,
186 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
187 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
188 )]
189 pub cache_ttl_ms: u64,
190
191 #[arg(
193 long,
194 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
195 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
196 )]
197 pub cache_max_size: usize,
198
199 #[arg(
201 long,
202 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
203 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
204 )]
205 pub max_request_ttl_ms: u64,
206
207 #[arg(
213 long,
214 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
215 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
216 )]
217 pub alpha: f64,
218
219 #[arg(
222 long,
223 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
224 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
225 )]
226 pub alternative_peers_retry_delay_ms: u64,
227
228 #[serde(flatten)]
229 #[clap(flatten)]
230 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
231}
232
233impl Default for Options {
234 fn default() -> Self {
235 use clap::Parser;
236
237 #[derive(Parser)]
238 struct OptionsParser {
239 #[clap(flatten)]
240 options: Options,
241 }
242
243 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
244 .expect("Options has no required arguments")
245 .options
246 }
247}
248
249impl Options {
250 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
252 let message_policy = MessagePolicy::new(
253 self.blanket_message_policy,
254 self.restrict_chain_ids_to.clone(),
255 self.reject_message_bundles_without_application_ids.clone(),
256 self.reject_message_bundles_with_other_application_ids
257 .clone(),
258 );
259 let cross_chain_message_delivery =
260 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
261 chain_client::Options {
262 max_pending_message_bundles: self.max_pending_message_bundles,
263 message_policy,
264 cross_chain_message_delivery,
265 quorum_grace_period: self.quorum_grace_period,
266 blob_download_timeout: self.blob_download_timeout,
267 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
268 certificate_download_batch_size: self.certificate_download_batch_size,
269 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
270 max_joined_tasks: self.max_joined_tasks,
271 allow_fast_blocks: self.allow_fast_blocks,
272 }
273 }
274
275 #[cfg(not(web))]
277 pub(crate) fn to_timing_config(&self) -> TimingConfig {
278 TimingConfig {
279 enabled: self.timings,
280 report_interval_secs: self.timing_interval,
281 }
282 }
283
284 pub(crate) fn to_requests_scheduler_config(
286 &self,
287 ) -> linera_core::client::RequestsSchedulerConfig {
288 linera_core::client::RequestsSchedulerConfig {
289 max_accepted_latency_ms: self.max_accepted_latency_ms,
290 cache_ttl_ms: self.cache_ttl_ms,
291 cache_max_size: self.cache_max_size,
292 max_request_ttl_ms: self.max_request_ttl_ms,
293 alpha: self.alpha,
294 retry_delay_ms: self.alternative_peers_retry_delay_ms,
295 }
296 }
297}
298
299#[derive(Debug, Clone, clap::Args)]
300pub struct ChainOwnershipConfig {
301 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
309 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
310
311 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
314 pub owners: Option<BTreeMap<AccountOwner, u64>>,
315
316 #[arg(long, value_parser = util::parse_json::<Option<AccountOwner>>)]
319 pub first_leader: Option<std::option::Option<AccountOwner>>,
320
321 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
325 pub multi_leader_rounds: Option<std::option::Option<u32>>,
326
327 #[arg(long)]
331 pub open_multi_leader_rounds: bool,
332
333 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
336 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
337
338 #[arg(
341 long = "base-timeout-ms",
342 value_parser = util::parse_millis_delta
343 )]
344 pub base_timeout: Option<TimeDelta>,
345
346 #[arg(
349 long = "timeout-increment-ms",
350 value_parser = util::parse_millis_delta
351 )]
352 pub timeout_increment: Option<TimeDelta>,
353
354 #[arg(
358 long = "fallback-duration-ms",
359 value_parser = util::parse_millis_delta
360 )]
361 pub fallback_duration: Option<TimeDelta>,
362}
363
364impl ChainOwnershipConfig {
365 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
366 let ChainOwnershipConfig {
367 super_owners,
368 owners,
369 first_leader,
370 multi_leader_rounds,
371 fast_round_duration,
372 open_multi_leader_rounds,
373 base_timeout,
374 timeout_increment,
375 fallback_duration,
376 } = self;
377
378 if let Some(owners) = owners {
379 chain_ownership.owners = owners;
380 }
381
382 if let Some(super_owners) = super_owners {
383 chain_ownership.super_owners = super_owners.into_iter().collect();
384 }
385
386 if let Some(first_leader) = first_leader {
387 chain_ownership.first_leader = first_leader;
388 }
389 if let Some(multi_leader_rounds) = multi_leader_rounds {
390 chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
391 }
392
393 chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
394
395 if let Some(fast_round_duration) = fast_round_duration {
396 chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
397 }
398 if let Some(base_timeout) = base_timeout {
399 chain_ownership.timeout_config.base_timeout = base_timeout;
400 }
401 if let Some(timeout_increment) = timeout_increment {
402 chain_ownership.timeout_config.timeout_increment = timeout_increment;
403 }
404 if let Some(fallback_duration) = fallback_duration {
405 chain_ownership.timeout_config.fallback_duration = fallback_duration;
406 }
407
408 Ok(())
409 }
410}
411
412impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
413 type Error = Error;
414
415 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
416 let mut chain_ownership = ChainOwnership::default();
417 config.update(&mut chain_ownership)?;
418 Ok(chain_ownership)
419 }
420}
421
422#[derive(Debug, Clone, clap::Args)]
423pub struct ApplicationPermissionsConfig {
424 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
434 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
435 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
439 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
440 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
444 pub manage_chain: Option<std::vec::Vec<ApplicationId>>,
445 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
449 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
450 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
454 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
455}
456
457impl ApplicationPermissionsConfig {
458 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
459 if let Some(execute_operations) = self.execute_operations {
460 application_permissions.execute_operations = execute_operations;
461 }
462 if let Some(mandatory_applications) = self.mandatory_applications {
463 application_permissions.mandatory_applications = mandatory_applications;
464 }
465 if let Some(manage_chain) = self.manage_chain {
466 application_permissions.manage_chain = manage_chain;
467 }
468 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
469 application_permissions.call_service_as_oracle = call_service_as_oracle;
470 }
471 if let Some(make_http_requests) = self.make_http_requests {
472 application_permissions.make_http_requests = make_http_requests;
473 }
474 }
475}
476
477#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
478pub enum ResourceControlPolicyConfig {
479 NoFees,
480 Testnet,
481 #[cfg(with_testing)]
482 OnlyFuel,
483 #[cfg(with_testing)]
484 AllCategories,
485}
486
487impl ResourceControlPolicyConfig {
488 pub fn into_policy(self) -> ResourceControlPolicy {
489 match self {
490 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
491 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
492 #[cfg(with_testing)]
493 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
494 #[cfg(with_testing)]
495 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
496 }
497 }
498}
499
500impl std::str::FromStr for ResourceControlPolicyConfig {
501 type Err = String;
502
503 fn from_str(s: &str) -> Result<Self, Self::Err> {
504 clap::ValueEnum::from_str(s, true)
505 }
506}
507
508impl fmt::Display for ResourceControlPolicyConfig {
509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510 write!(f, "{:?}", self)
511 }
512}