1use std::{collections::HashSet, fmt, iter, path::PathBuf};
5
6use linera_base::{
7 data_types::{ApplicationPermissions, TimeDelta},
8 identifiers::{AccountOwner, ApplicationId, ChainId, GenericApplicationId},
9 ownership::{ChainOwnership, TimeoutConfig},
10 time::Duration,
11};
12use linera_core::{
13 client::{
14 chain_client, BlanketMessagePolicy, MessagePolicy, DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
15 DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
16 },
17 node::CrossChainMessageDelivery,
18 DEFAULT_QUORUM_GRACE_PERIOD,
19};
20use linera_execution::ResourceControlPolicy;
21
22#[cfg(not(web))]
23use crate::client_metrics::TimingConfig;
24use crate::util;
25
26#[derive(Debug, thiserror::Error)]
27pub enum Error {
28 #[error("I/O error: {0}")]
29 IoError(#[from] std::io::Error),
30 #[error("there are {public_keys} public keys but {weights} weights")]
31 MisalignedWeights { public_keys: usize, weights: usize },
32 #[error("config error: {0}")]
33 Config(#[from] crate::config::Error),
34}
35
36util::impl_from_infallible!(Error);
37
38#[derive(Clone, clap::Parser)]
39pub struct ClientContextOptions {
40 #[arg(long = "wallet")]
42 pub wallet_state_path: Option<PathBuf>,
43
44 #[arg(long = "keystore")]
46 pub keystore_path: Option<PathBuf>,
47
48 #[arg(long, short = 'w', value_parser = util::parse_ascii_alphanumeric_string)]
53 pub with_wallet: Option<String>,
54
55 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
57 pub send_timeout: Duration,
58
59 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
61 pub recv_timeout: Duration,
62
63 #[arg(long, default_value = "10")]
65 pub max_pending_message_bundles: usize,
66
67 #[arg(
69 long = "chain-worker-ttl-ms",
70 default_value = "30000",
71 env = "LINERA_CHAIN_WORKER_TTL_MS",
72 value_parser = util::parse_millis
73 )]
74 pub chain_worker_ttl: Duration,
75
76 #[arg(
79 long = "sender-chain-worker-ttl-ms",
80 default_value = "1000",
81 env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
82 value_parser = util::parse_millis
83 )]
84 pub sender_chain_worker_ttl: Duration,
85
86 #[arg(
88 long = "retry-delay-ms",
89 default_value = "1000",
90 value_parser = util::parse_millis
91 )]
92 pub retry_delay: Duration,
93
94 #[arg(long, default_value = "10")]
96 pub max_retries: u32,
97
98 #[arg(long)]
100 pub chrome_trace_exporter: bool,
101
102 #[arg(long, env = "LINERA_CHROME_TRACE_FILE")]
105 pub chrome_trace_file: Option<String>,
106
107 #[arg(long, env = "LINERA_OTLP_EXPORTER_ENDPOINT")]
109 pub otlp_exporter_endpoint: Option<String>,
110
111 #[arg(long)]
114 pub wait_for_outgoing_messages: bool,
115
116 #[arg(long)]
118 pub long_lived_services: bool,
119
120 #[arg(long, default_value = "accept")]
122 pub blanket_message_policy: BlanketMessagePolicy,
123
124 #[arg(long, value_parser = util::parse_chain_set)]
128 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
129
130 #[arg(long, value_parser = util::parse_app_set)]
133 pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
134
135 #[arg(long, value_parser = util::parse_app_set)]
138 pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
139
140 #[cfg(not(web))]
142 #[arg(long)]
143 pub timings: bool,
144
145 #[cfg(not(web))]
147 #[arg(long, default_value = "5")]
148 pub timing_interval: u64,
149
150 #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
153 pub quorum_grace_period: f64,
154
155 #[arg(
157 long = "blob-download-timeout-ms",
158 default_value = "1000",
159 value_parser = util::parse_millis
160 )]
161 pub blob_download_timeout: Duration,
162
163 #[arg(
166 long = "cert-batch-download-timeout-ms",
167 default_value = "1000",
168 value_parser = util::parse_millis
169 )]
170 pub certificate_batch_download_timeout: Duration,
171
172 #[arg(
175 long,
176 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
177 )]
178 pub certificate_download_batch_size: u64,
179
180 #[arg(
183 long,
184 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
185 )]
186 pub sender_certificate_download_batch_size: usize,
187
188 #[arg(long, default_value = "100")]
190 pub max_joined_tasks: usize,
191
192 #[arg(
194 long,
195 default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
196 env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
197 )]
198 pub max_accepted_latency_ms: f64,
199
200 #[arg(
202 long,
203 default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
204 env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
205 )]
206 pub cache_ttl_ms: u64,
207
208 #[arg(
210 long,
211 default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
212 env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
213 )]
214 pub cache_max_size: usize,
215
216 #[arg(
218 long,
219 default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
220 env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
221 )]
222 pub max_request_ttl_ms: u64,
223
224 #[arg(
230 long,
231 default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
232 env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
233 )]
234 pub alpha: f64,
235
236 #[arg(
239 long,
240 default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
241 env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
242 )]
243 pub alternative_peers_retry_delay_ms: u64,
244}
245
246impl ClientContextOptions {
247 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
249 let message_policy = MessagePolicy::new(
250 self.blanket_message_policy,
251 self.restrict_chain_ids_to.clone(),
252 self.reject_message_bundles_without_application_ids.clone(),
253 self.reject_message_bundles_with_other_application_ids
254 .clone(),
255 );
256 let cross_chain_message_delivery =
257 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
258 chain_client::Options {
259 max_pending_message_bundles: self.max_pending_message_bundles,
260 message_policy,
261 cross_chain_message_delivery,
262 quorum_grace_period: self.quorum_grace_period,
263 blob_download_timeout: self.blob_download_timeout,
264 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
265 certificate_download_batch_size: self.certificate_download_batch_size,
266 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
267 max_joined_tasks: self.max_joined_tasks,
268 }
269 }
270
271 #[cfg(not(web))]
273 pub(crate) fn to_timing_config(&self) -> TimingConfig {
274 TimingConfig {
275 enabled: self.timings,
276 report_interval_secs: self.timing_interval,
277 }
278 }
279
280 pub(crate) fn to_requests_scheduler_config(
282 &self,
283 ) -> linera_core::client::RequestsSchedulerConfig {
284 linera_core::client::RequestsSchedulerConfig {
285 max_accepted_latency_ms: self.max_accepted_latency_ms,
286 cache_ttl_ms: self.cache_ttl_ms,
287 cache_max_size: self.cache_max_size,
288 max_request_ttl_ms: self.max_request_ttl_ms,
289 alpha: self.alpha,
290 retry_delay_ms: self.alternative_peers_retry_delay_ms,
291 }
292 }
293}
294
295#[derive(Debug, Clone, clap::Args)]
296pub struct ChainOwnershipConfig {
297 #[arg(long, num_args(0..))]
299 pub super_owners: Vec<AccountOwner>,
300
301 #[arg(long, num_args(0..))]
303 pub owners: Vec<AccountOwner>,
304
305 #[arg(long)]
307 pub first_leader: Option<AccountOwner>,
308
309 #[arg(long, num_args(0..))]
314 pub owner_weights: Vec<u64>,
315
316 #[arg(long)]
319 pub multi_leader_rounds: Option<u32>,
320
321 #[arg(long)]
325 pub open_multi_leader_rounds: bool,
326
327 #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
329 pub fast_round_duration: Option<TimeDelta>,
330
331 #[arg(
333 long = "base-timeout-ms",
334 default_value = "10000",
335 value_parser = util::parse_millis_delta
336 )]
337 pub base_timeout: TimeDelta,
338
339 #[arg(
342 long = "timeout-increment-ms",
343 default_value = "1000",
344 value_parser = util::parse_millis_delta
345 )]
346 pub timeout_increment: TimeDelta,
347
348 #[arg(
351 long = "fallback-duration-ms",
352 default_value = "86400000", value_parser = util::parse_millis_delta
354 )]
355 pub fallback_duration: TimeDelta,
356}
357
358impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
359 type Error = Error;
360
361 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
362 let ChainOwnershipConfig {
363 super_owners,
364 owners,
365 first_leader,
366 owner_weights,
367 multi_leader_rounds,
368 fast_round_duration,
369 open_multi_leader_rounds,
370 base_timeout,
371 timeout_increment,
372 fallback_duration,
373 } = config;
374 if !owner_weights.is_empty() && owner_weights.len() != owners.len() {
375 return Err(Error::MisalignedWeights {
376 public_keys: owners.len(),
377 weights: owner_weights.len(),
378 });
379 }
380 let super_owners = super_owners.into_iter().collect();
381 let owners = owners
382 .into_iter()
383 .zip(owner_weights.into_iter().chain(iter::repeat(100)))
384 .collect();
385 let multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
386 let timeout_config = TimeoutConfig {
387 fast_round_duration,
388 base_timeout,
389 timeout_increment,
390 fallback_duration,
391 };
392 Ok(ChainOwnership {
393 super_owners,
394 owners,
395 first_leader,
396 multi_leader_rounds,
397 open_multi_leader_rounds,
398 timeout_config,
399 })
400 }
401}
402
403#[derive(Debug, Clone, clap::Args)]
404pub struct ApplicationPermissionsConfig {
405 #[arg(long)]
408 pub execute_operations: Option<Vec<ApplicationId>>,
409 #[arg(long)]
412 pub mandatory_applications: Option<Vec<ApplicationId>>,
413 #[arg(long)]
415 pub close_chain: Option<Vec<ApplicationId>>,
416 #[arg(long)]
419 pub change_application_permissions: Option<Vec<ApplicationId>>,
420 #[arg(long)]
423 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
424 #[arg(long)]
427 pub make_http_requests: Option<Vec<ApplicationId>>,
428}
429
430impl From<ApplicationPermissionsConfig> for ApplicationPermissions {
431 fn from(config: ApplicationPermissionsConfig) -> ApplicationPermissions {
432 ApplicationPermissions {
433 execute_operations: config.execute_operations,
434 mandatory_applications: config.mandatory_applications.unwrap_or_default(),
435 close_chain: config.close_chain.unwrap_or_default(),
436 change_application_permissions: config
437 .change_application_permissions
438 .unwrap_or_default(),
439 call_service_as_oracle: config.call_service_as_oracle,
440 make_http_requests: config.make_http_requests,
441 }
442 }
443}
444
445#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
446pub enum ResourceControlPolicyConfig {
447 NoFees,
448 Testnet,
449 #[cfg(with_testing)]
450 OnlyFuel,
451 #[cfg(with_testing)]
452 AllCategories,
453}
454
455impl ResourceControlPolicyConfig {
456 pub fn into_policy(self) -> ResourceControlPolicy {
457 match self {
458 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
459 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
460 #[cfg(with_testing)]
461 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
462 #[cfg(with_testing)]
463 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
464 }
465 }
466}
467
468impl std::str::FromStr for ResourceControlPolicyConfig {
469 type Err = String;
470
471 fn from_str(s: &str) -> Result<Self, Self::Err> {
472 clap::ValueEnum::from_str(s, true)
473 }
474}
475
476impl fmt::Display for ResourceControlPolicyConfig {
477 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
478 write!(f, "{:?}", self)
479 }
480}