1use std::{collections::HashSet, fmt, iter, path::PathBuf};
5
6use linera_base::{
7 data_types::{ApplicationPermissions, TimeDelta},
8 identifiers::{AccountOwner, ApplicationId, ChainId},
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_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("persistence error: {0}")]
33 Persistence(#[from] Box<dyn std::error::Error + Send + Sync>),
34 #[error("config error: {0}")]
35 Config(#[from] crate::config::Error),
36}
37
38#[cfg(feature = "fs")]
39util::impl_from_dynamic!(Error:Persistence, linera_persistent::file::Error);
40
41#[cfg(web)]
42util::impl_from_dynamic!(Error:Persistence, linera_persistent::indexed_db::Error);
43
44util::impl_from_infallible!(Error);
45
46#[derive(Clone, clap::Parser)]
47pub struct ClientContextOptions {
48 #[arg(long = "wallet")]
50 pub wallet_state_path: Option<PathBuf>,
51
52 #[arg(long = "keystore")]
54 pub keystore_path: Option<PathBuf>,
55
56 #[arg(long, short = 'w', value_parser = util::parse_ascii_alphanumeric_string)]
61 pub with_wallet: Option<String>,
62
63 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
65 pub send_timeout: Duration,
66
67 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
69 pub recv_timeout: Duration,
70
71 #[arg(long, default_value = "10")]
73 pub max_pending_message_bundles: usize,
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(long)]
108 pub chrome_trace_exporter: bool,
109
110 #[arg(long, env = "LINERA_CHROME_TRACE_FILE")]
113 pub chrome_trace_file: Option<String>,
114
115 #[arg(long, env = "LINERA_OTLP_EXPORTER_ENDPOINT")]
117 pub otlp_exporter_endpoint: Option<String>,
118
119 #[arg(long)]
122 pub wait_for_outgoing_messages: bool,
123
124 #[arg(long)]
126 pub long_lived_services: bool,
127
128 #[arg(long, default_value = "accept")]
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 #[cfg(not(web))]
140 #[arg(long)]
141 pub timings: bool,
142
143 #[cfg(not(web))]
145 #[arg(long, default_value = "5")]
146 pub timing_interval: u64,
147
148 #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
151 pub grace_period: f64,
152
153 #[arg(
155 long = "blob-download-timeout-ms",
156 default_value = "1000",
157 value_parser = util::parse_millis
158 )]
159 pub blob_download_timeout: Duration,
160
161 #[arg(
164 long = "cert-batch-download-timeout-ms",
165 default_value = "1000",
166 value_parser = util::parse_millis
167 )]
168 pub certificate_batch_download_timeout: Duration,
169
170 #[arg(
173 long,
174 default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
175 )]
176 pub certificate_download_batch_size: u64,
177
178 #[arg(
181 long,
182 default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
183 )]
184 pub sender_certificate_download_batch_size: usize,
185
186 #[arg(long, default_value = "100")]
188 pub max_joined_tasks: usize,
189}
190
191impl ClientContextOptions {
192 pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
194 let message_policy = MessagePolicy::new(
195 self.blanket_message_policy,
196 self.restrict_chain_ids_to.clone(),
197 );
198 let cross_chain_message_delivery =
199 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
200 chain_client::Options {
201 max_pending_message_bundles: self.max_pending_message_bundles,
202 message_policy,
203 cross_chain_message_delivery,
204 grace_period: self.grace_period,
205 blob_download_timeout: self.blob_download_timeout,
206 certificate_batch_download_timeout: self.certificate_batch_download_timeout,
207 certificate_download_batch_size: self.certificate_download_batch_size,
208 sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
209 max_joined_tasks: self.max_joined_tasks,
210 }
211 }
212
213 #[cfg(not(web))]
215 pub(crate) fn to_timing_config(&self) -> TimingConfig {
216 TimingConfig {
217 enabled: self.timings,
218 report_interval_secs: self.timing_interval,
219 }
220 }
221}
222
223#[derive(Debug, Clone, clap::Args)]
224pub struct ChainOwnershipConfig {
225 #[arg(long, num_args(0..))]
227 pub super_owners: Vec<AccountOwner>,
228
229 #[arg(long, num_args(0..))]
231 pub owners: Vec<AccountOwner>,
232
233 #[arg(long, num_args(0..))]
238 pub owner_weights: Vec<u64>,
239
240 #[arg(long)]
243 pub multi_leader_rounds: Option<u32>,
244
245 #[arg(long)]
249 pub open_multi_leader_rounds: bool,
250
251 #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
253 pub fast_round_duration: Option<TimeDelta>,
254
255 #[arg(
257 long = "base-timeout-ms",
258 default_value = "10000",
259 value_parser = util::parse_millis_delta
260 )]
261 pub base_timeout: TimeDelta,
262
263 #[arg(
266 long = "timeout-increment-ms",
267 default_value = "1000",
268 value_parser = util::parse_millis_delta
269 )]
270 pub timeout_increment: TimeDelta,
271
272 #[arg(
275 long = "fallback-duration-ms",
276 default_value = "86400000", value_parser = util::parse_millis_delta
278 )]
279 pub fallback_duration: TimeDelta,
280}
281
282impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
283 type Error = Error;
284
285 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
286 let ChainOwnershipConfig {
287 super_owners,
288 owners,
289 owner_weights,
290 multi_leader_rounds,
291 fast_round_duration,
292 open_multi_leader_rounds,
293 base_timeout,
294 timeout_increment,
295 fallback_duration,
296 } = config;
297 if !owner_weights.is_empty() && owner_weights.len() != owners.len() {
298 return Err(Error::MisalignedWeights {
299 public_keys: owners.len(),
300 weights: owner_weights.len(),
301 });
302 }
303 let super_owners = super_owners.into_iter().collect();
304 let owners = owners
305 .into_iter()
306 .zip(owner_weights.into_iter().chain(iter::repeat(100)))
307 .collect();
308 let multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
309 let timeout_config = TimeoutConfig {
310 fast_round_duration,
311 base_timeout,
312 timeout_increment,
313 fallback_duration,
314 };
315 Ok(ChainOwnership {
316 super_owners,
317 owners,
318 multi_leader_rounds,
319 open_multi_leader_rounds,
320 timeout_config,
321 })
322 }
323}
324
325#[derive(Debug, Clone, clap::Args)]
326pub struct ApplicationPermissionsConfig {
327 #[arg(long)]
330 pub execute_operations: Option<Vec<ApplicationId>>,
331 #[arg(long)]
334 pub mandatory_applications: Option<Vec<ApplicationId>>,
335 #[arg(long)]
337 pub close_chain: Option<Vec<ApplicationId>>,
338 #[arg(long)]
341 pub change_application_permissions: Option<Vec<ApplicationId>>,
342 #[arg(long)]
345 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
346 #[arg(long)]
349 pub make_http_requests: Option<Vec<ApplicationId>>,
350}
351
352impl From<ApplicationPermissionsConfig> for ApplicationPermissions {
353 fn from(config: ApplicationPermissionsConfig) -> ApplicationPermissions {
354 ApplicationPermissions {
355 execute_operations: config.execute_operations,
356 mandatory_applications: config.mandatory_applications.unwrap_or_default(),
357 close_chain: config.close_chain.unwrap_or_default(),
358 change_application_permissions: config
359 .change_application_permissions
360 .unwrap_or_default(),
361 call_service_as_oracle: config.call_service_as_oracle,
362 make_http_requests: config.make_http_requests,
363 }
364 }
365}
366
367#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
368pub enum ResourceControlPolicyConfig {
369 NoFees,
370 Testnet,
371 #[cfg(with_testing)]
372 OnlyFuel,
373 #[cfg(with_testing)]
374 AllCategories,
375}
376
377impl ResourceControlPolicyConfig {
378 pub fn into_policy(self) -> ResourceControlPolicy {
379 match self {
380 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
381 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
382 #[cfg(with_testing)]
383 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
384 #[cfg(with_testing)]
385 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
386 }
387 }
388}
389
390impl std::str::FromStr for ResourceControlPolicyConfig {
391 type Err = String;
392
393 fn from_str(s: &str) -> Result<Self, Self::Err> {
394 clap::ValueEnum::from_str(s, true)
395 }
396}
397
398impl fmt::Display for ResourceControlPolicyConfig {
399 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
400 write!(f, "{:?}", self)
401 }
402}