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