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