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 ChainClientOptions, 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(long, default_value = "3")]
63 pub max_block_limit_errors: u32,
64
65 #[arg(long, default_value = "10")]
67 pub max_new_events_per_block: usize,
68
69 #[arg(
71 long = "chain-worker-ttl-ms",
72 default_value = "30000",
73 env = "LINERA_CHAIN_WORKER_TTL_MS",
74 value_parser = util::parse_millis,
75 )]
76 pub chain_worker_ttl: Duration,
77
78 #[arg(
81 long = "sender-chain-worker-ttl-ms",
82 default_value = "1000",
83 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
84 value_parser = util::parse_millis
85 )]
86 pub sender_chain_worker_ttl: Duration,
87
88 #[arg(
90 long = "retry-delay-ms",
91 default_value = "1000",
92 value_parser = util::parse_millis
93 )]
94 pub retry_delay: Duration,
95
96 #[arg(long, default_value = "10")]
98 pub max_retries: u32,
99
100 #[arg(
102 long = "max-backoff-ms",
103 default_value = "30000",
104 value_parser = util::parse_millis
105 )]
106 pub max_backoff: Duration,
107
108 #[arg(long)]
111 pub wait_for_outgoing_messages: bool,
112
113 #[arg(long)]
116 pub allow_fast_blocks: bool,
117
118 #[arg(long)]
120 pub long_lived_services: bool,
121
122 #[arg(long, default_value_t, value_enum)]
124 pub blanket_message_policy: BlanketMessagePolicy,
125
126 #[arg(long, value_parser = util::parse_chain_set)]
130 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
131
132 #[arg(long, value_parser = util::parse_app_set)]
135 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
136
137 #[arg(long, value_parser = util::parse_app_set)]
140 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
141
142 #[cfg(not(web))]
144 #[arg(long)]
145 pub timings: bool,
146
147 #[cfg(not(web))]
149 #[arg(long, default_value = "5")]
150 pub timing_interval: u64,
151
152 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
155 pub quorum_grace_period: f64,
156
157 #[arg(
159 long = "blob-download-timeout-ms",
160 default_value = "1000",
161 value_parser = util::parse_millis,
162 )]
163 pub blob_download_timeout: Duration,
164
165 #[arg(
168 long = "cert-batch-download-timeout-ms",
169 default_value = "1000",
170 value_parser = util::parse_millis
171 )]
172 pub certificate_batch_download_timeout: Duration,
173
174 #[arg(
177 long,
178 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
179 )]
180 pub certificate_download_batch_size: u64,
181
182 #[arg(
185 long,
186 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
187 )]
188 pub sender_certificate_download_batch_size: usize,
189
190 #[arg(long, default_value = "100")]
192 pub max_joined_tasks: usize,
193
194 #[arg(
196 long,
197 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
198 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
199 )]
200 pub max_accepted_latency_ms: f64,
201
202 #[arg(
204 long,
205 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
206 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
207 )]
208 pub cache_ttl_ms: u64,
209
210 #[arg(
212 long,
213 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
214 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
215 )]
216 pub cache_max_size: usize,
217
218 #[arg(
220 long,
221 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
222 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
223 )]
224 pub max_request_ttl_ms: u64,
225
226 #[arg(
232 long,
233 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
234 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
235 )]
236 pub alpha: f64,
237
238 #[arg(
241 long,
242 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
243 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
244 )]
245 pub alternative_peers_retry_delay_ms: u64,
246
247 #[serde(flatten)]
248 #[clap(flatten)]
249 pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
250}
251
252impl Default for Options {
253 fn default() -> Self {
254 use clap::Parser;
255
256 #[derive(Parser)]
257 struct OptionsParser {
258 #[clap(flatten)]
259 options: Options,
260 }
261
262 OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
263 .expect("Options has no required arguments")
264 .options
265 }
266}
267
268impl Options {
269 pub(crate) fn to_chain_client_options(&self) -> ChainClientOptions {
271 let message_policy = MessagePolicy::new(
272 self.blanket_message_policy,
273 self.restrict_chain_ids_to.clone(),
274 self.reject_message_bundles_without_application_ids.clone(),
275 self.reject_message_bundles_with_other_application_ids
276 .clone(),
277 );
278 let cross_chain_message_delivery =
279 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
280 ChainClientOptions {
281 max_pending_message_bundles: self.max_pending_message_bundles,
282 max_block_limit_errors: self.max_block_limit_errors,
283 max_new_events_per_block: self.max_new_events_per_block,
284 message_policy,
285 cross_chain_message_delivery,
286 quorum_grace_period: self.quorum_grace_period,
287 blob_download_timeout: self.blob_download_timeout,
288 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
289 certificate_download_batch_size: self.certificate_download_batch_size,
290 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
291 max_joined_tasks: self.max_joined_tasks,
292 allow_fast_blocks: self.allow_fast_blocks,
293 }
294 }
295
296 #[cfg(not(web))]
298 pub(crate) fn to_timing_config(&self) -> TimingConfig {
299 TimingConfig {
300 enabled: self.timings,
301 report_interval_secs: self.timing_interval,
302 }
303 }
304
305 pub(crate) fn to_requests_scheduler_config(
307 &self,
308 ) -> linera_core::client::RequestsSchedulerConfig {
309 linera_core::client::RequestsSchedulerConfig {
310 max_accepted_latency_ms: self.max_accepted_latency_ms,
311 cache_ttl_ms: self.cache_ttl_ms,
312 cache_max_size: self.cache_max_size,
313 max_request_ttl_ms: self.max_request_ttl_ms,
314 alpha: self.alpha,
315 retry_delay_ms: self.alternative_peers_retry_delay_ms,
316 }
317 }
318}
319
320#[derive(Debug, Clone, clap::Args)]
321pub struct ChainOwnershipConfig {
322 #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
330 pub super_owners: Option<std::vec::Vec<AccountOwner>>,
331
332 #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
335 pub owners: Option<BTreeMap<AccountOwner, u64>>,
336
337 #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
341 pub multi_leader_rounds: Option<std::option::Option<u32>>,
342
343 #[arg(long)]
347 pub open_multi_leader_rounds: bool,
348
349 #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
352 pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
353
354 #[arg(
357 long = "base-timeout-ms",
358 value_parser = util::parse_millis_delta
359 )]
360 pub base_timeout: Option<TimeDelta>,
361
362 #[arg(
365 long = "timeout-increment-ms",
366 value_parser = util::parse_millis_delta
367 )]
368 pub timeout_increment: Option<TimeDelta>,
369
370 #[arg(
374 long = "fallback-duration-ms",
375 value_parser = util::parse_millis_delta
376 )]
377 pub fallback_duration: Option<TimeDelta>,
378}
379
380impl ChainOwnershipConfig {
381 pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
382 let ChainOwnershipConfig {
383 super_owners,
384 owners,
385 multi_leader_rounds,
386 fast_round_duration,
387 open_multi_leader_rounds,
388 base_timeout,
389 timeout_increment,
390 fallback_duration,
391 } = self;
392
393 if let Some(owners) = owners {
394 chain_ownership.owners = owners;
395 }
396
397 if let Some(super_owners) = super_owners {
398 chain_ownership.super_owners = super_owners.into_iter().collect();
399 }
400
401 if let Some(multi_leader_rounds) = multi_leader_rounds {
402 chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
403 }
404
405 chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
406
407 if let Some(fast_round_duration) = fast_round_duration {
408 chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
409 }
410 if let Some(base_timeout) = base_timeout {
411 chain_ownership.timeout_config.base_timeout = base_timeout;
412 }
413 if let Some(timeout_increment) = timeout_increment {
414 chain_ownership.timeout_config.timeout_increment = timeout_increment;
415 }
416 if let Some(fallback_duration) = fallback_duration {
417 chain_ownership.timeout_config.fallback_duration = fallback_duration;
418 }
419
420 Ok(())
421 }
422}
423
424impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
425 type Error = Error;
426
427 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
428 let mut chain_ownership = ChainOwnership::default();
429 config.update(&mut chain_ownership)?;
430 Ok(chain_ownership)
431 }
432}
433
434#[derive(Debug, Clone, clap::Args)]
435pub struct ApplicationPermissionsConfig {
436 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
446 pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
447 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
451 pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
452 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
455 pub close_chain: Option<std::vec::Vec<ApplicationId>>,
456 #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
459 pub change_application_permissions: Option<std::vec::Vec<ApplicationId>>,
460 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
464 pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
465 #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
469 pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
470}
471
472impl ApplicationPermissionsConfig {
473 pub fn update(self, application_permissions: &mut ApplicationPermissions) {
474 if let Some(execute_operations) = self.execute_operations {
475 application_permissions.execute_operations = execute_operations;
476 }
477 if let Some(mandatory_applications) = self.mandatory_applications {
478 application_permissions.mandatory_applications = mandatory_applications;
479 }
480 if let Some(close_chain) = self.close_chain {
481 application_permissions.close_chain = close_chain;
482 }
483 if let Some(change_application_permissions) = self.change_application_permissions {
484 application_permissions.change_application_permissions = change_application_permissions;
485 }
486 if let Some(call_service_as_oracle) = self.call_service_as_oracle {
487 application_permissions.call_service_as_oracle = call_service_as_oracle;
488 }
489 if let Some(make_http_requests) = self.make_http_requests {
490 application_permissions.make_http_requests = make_http_requests;
491 }
492 }
493}
494
495#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
496pub enum ResourceControlPolicyConfig {
497 NoFees,
498 Testnet,
499 #[cfg(with_testing)]
500 OnlyFuel,
501 #[cfg(with_testing)]
502 AllCategories,
503}
504
505impl ResourceControlPolicyConfig {
506 pub fn into_policy(self) -> ResourceControlPolicy {
507 match self {
508 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
509 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
510 #[cfg(with_testing)]
511 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
512 #[cfg(with_testing)]
513 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
514 }
515 }
516}
517
518impl std::str::FromStr for ResourceControlPolicyConfig {
519 type Err = String;
520
521 fn from_str(s: &str) -> Result<Self, Self::Err> {
522 clap::ValueEnum::from_str(s, true)
523 }
524}
525
526impl fmt::Display for ResourceControlPolicyConfig {
527 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
528 write!(f, "{:?}", self)
529 }
530}