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::{BlanketMessagePolicy, ChainClientOptions, MessagePolicy},
14 node::CrossChainMessageDelivery,
15 DEFAULT_GRACE_PERIOD,
16};
17use linera_execution::ResourceControlPolicy;
18
19#[cfg(not(web))]
20use crate::client_metrics::TimingConfig;
21use crate::util;
22
23#[derive(Debug, thiserror::Error)]
24pub enum Error {
25 #[error("I/O error: {0}")]
26 IoError(#[from] std::io::Error),
27 #[error("there are {public_keys} public keys but {weights} weights")]
28 MisalignedWeights { public_keys: usize, weights: usize },
29 #[error("persistence error: {0}")]
30 Persistence(#[from] Box<dyn std::error::Error + Send + Sync>),
31 #[error("config error: {0}")]
32 Config(#[from] crate::config::Error),
33}
34
35#[cfg(feature = "fs")]
36util::impl_from_dynamic!(Error:Persistence, linera_persistent::file::Error);
37
38#[cfg(web)]
39util::impl_from_dynamic!(Error:Persistence, linera_persistent::indexed_db::Error);
40
41util::impl_from_infallible!(Error);
42
43#[derive(Clone, clap::Parser)]
44pub struct ClientContextOptions {
45 #[arg(long = "wallet")]
47 pub wallet_state_path: Option<PathBuf>,
48
49 #[arg(long = "keystore")]
51 pub keystore_path: Option<PathBuf>,
52
53 #[arg(long, short = 'w', value_parser = util::parse_ascii_alphanumeric_string)]
58 pub with_wallet: Option<String>,
59
60 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
62 pub send_timeout: Duration,
63
64 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
66 pub recv_timeout: Duration,
67
68 #[arg(long, default_value = "10")]
70 pub max_pending_message_bundles: usize,
71
72 #[arg(
74 long = "chain-worker-ttl-ms",
75 default_value = "30000",
76 value_parser = util::parse_millis
77 )]
78 pub chain_worker_ttl: Duration,
79
80 #[arg(
82 long = "retry-delay-ms",
83 default_value = "1000",
84 value_parser = util::parse_millis
85 )]
86 pub retry_delay: Duration,
87
88 #[arg(long, default_value = "10")]
90 pub max_retries: u32,
91
92 #[arg(long)]
95 pub wait_for_outgoing_messages: bool,
96
97 #[arg(long)]
99 pub long_lived_services: bool,
100
101 #[arg(long, default_value = "accept")]
103 pub blanket_message_policy: BlanketMessagePolicy,
104
105 #[arg(long, value_parser = util::parse_chain_set)]
109 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
110
111 #[cfg(not(web))]
113 #[arg(long)]
114 pub timings: bool,
115
116 #[cfg(not(web))]
118 #[arg(long, default_value = "5")]
119 pub timing_interval: u64,
120
121 #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
124 pub grace_period: f64,
125
126 #[arg(
128 long = "blob-download-timeout-ms",
129 default_value = "1000",
130 value_parser = util::parse_millis
131 )]
132 pub blob_download_timeout: Duration,
133}
134
135impl ClientContextOptions {
136 pub(crate) fn to_chain_client_options(&self) -> ChainClientOptions {
138 let message_policy = MessagePolicy::new(
139 self.blanket_message_policy,
140 self.restrict_chain_ids_to.clone(),
141 );
142 let cross_chain_message_delivery =
143 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
144 ChainClientOptions {
145 max_pending_message_bundles: self.max_pending_message_bundles,
146 message_policy,
147 cross_chain_message_delivery,
148 grace_period: self.grace_period,
149 blob_download_timeout: self.blob_download_timeout,
150 }
151 }
152
153 #[cfg(not(web))]
155 pub(crate) fn to_timing_config(&self) -> TimingConfig {
156 TimingConfig {
157 enabled: self.timings,
158 report_interval_secs: self.timing_interval,
159 }
160 }
161}
162
163#[derive(Debug, Clone, clap::Args)]
164pub struct ChainOwnershipConfig {
165 #[arg(long, num_args(0..))]
167 super_owners: Vec<AccountOwner>,
168
169 #[arg(long, num_args(0..))]
171 owners: Vec<AccountOwner>,
172
173 #[arg(long, num_args(0..))]
178 owner_weights: Vec<u64>,
179
180 #[arg(long)]
183 multi_leader_rounds: Option<u32>,
184
185 #[arg(long)]
189 open_multi_leader_rounds: bool,
190
191 #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
193 fast_round_duration: Option<TimeDelta>,
194
195 #[arg(
197 long = "base-timeout-ms",
198 default_value = "10000",
199 value_parser = util::parse_millis_delta
200 )]
201 base_timeout: TimeDelta,
202
203 #[arg(
206 long = "timeout-increment-ms",
207 default_value = "1000",
208 value_parser = util::parse_millis_delta
209 )]
210 timeout_increment: TimeDelta,
211
212 #[arg(
215 long = "fallback-duration-ms",
216 default_value = "86400000", value_parser = util::parse_millis_delta
218 )]
219 pub fallback_duration: TimeDelta,
220}
221
222impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
223 type Error = Error;
224
225 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
226 let ChainOwnershipConfig {
227 super_owners,
228 owners,
229 owner_weights,
230 multi_leader_rounds,
231 fast_round_duration,
232 open_multi_leader_rounds,
233 base_timeout,
234 timeout_increment,
235 fallback_duration,
236 } = config;
237 if !owner_weights.is_empty() && owner_weights.len() != owners.len() {
238 return Err(Error::MisalignedWeights {
239 public_keys: owners.len(),
240 weights: owner_weights.len(),
241 });
242 }
243 let super_owners = super_owners.into_iter().collect();
244 let owners = owners
245 .into_iter()
246 .zip(owner_weights.into_iter().chain(iter::repeat(100)))
247 .collect();
248 let multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
249 let timeout_config = TimeoutConfig {
250 fast_round_duration,
251 base_timeout,
252 timeout_increment,
253 fallback_duration,
254 };
255 Ok(ChainOwnership {
256 super_owners,
257 owners,
258 multi_leader_rounds,
259 open_multi_leader_rounds,
260 timeout_config,
261 })
262 }
263}
264
265#[derive(Debug, Clone, clap::Args)]
266pub struct ApplicationPermissionsConfig {
267 #[arg(long)]
270 pub execute_operations: Option<Vec<ApplicationId>>,
271 #[arg(long)]
274 pub mandatory_applications: Option<Vec<ApplicationId>>,
275 #[arg(long)]
277 pub close_chain: Option<Vec<ApplicationId>>,
278 #[arg(long)]
281 pub change_application_permissions: Option<Vec<ApplicationId>>,
282 #[arg(long)]
285 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
286 #[arg(long)]
289 pub make_http_requests: Option<Vec<ApplicationId>>,
290}
291
292impl From<ApplicationPermissionsConfig> for ApplicationPermissions {
293 fn from(config: ApplicationPermissionsConfig) -> ApplicationPermissions {
294 ApplicationPermissions {
295 execute_operations: config.execute_operations,
296 mandatory_applications: config.mandatory_applications.unwrap_or_default(),
297 close_chain: config.close_chain.unwrap_or_default(),
298 change_application_permissions: config
299 .change_application_permissions
300 .unwrap_or_default(),
301 call_service_as_oracle: config.call_service_as_oracle,
302 make_http_requests: config.make_http_requests,
303 }
304 }
305}
306
307#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
308pub enum ResourceControlPolicyConfig {
309 NoFees,
310 Testnet,
311 #[cfg(with_testing)]
312 OnlyFuel,
313 #[cfg(with_testing)]
314 AllCategories,
315}
316
317impl ResourceControlPolicyConfig {
318 pub fn into_policy(self) -> ResourceControlPolicy {
319 match self {
320 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
321 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
322 #[cfg(with_testing)]
323 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
324 #[cfg(with_testing)]
325 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
326 }
327 }
328}
329
330impl std::str::FromStr for ResourceControlPolicyConfig {
331 type Err = String;
332
333 fn from_str(s: &str) -> Result<Self, Self::Err> {
334 clap::ValueEnum::from_str(s, true)
335 }
336}
337
338impl fmt::Display for ResourceControlPolicyConfig {
339 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
340 write!(f, "{:?}", self)
341 }
342}