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
19use crate::util;
20
21#[derive(Debug, thiserror::Error)]
22pub enum Error {
23 #[error("I/O error: {0}")]
24 IoError(#[from] std::io::Error),
25 #[error("there are {public_keys} public keys but {weights} weights")]
26 MisalignedWeights { public_keys: usize, weights: usize },
27 #[error("persistence error: {0}")]
28 Persistence(#[from] Box<dyn std::error::Error + Send + Sync>),
29 #[error("config error: {0}")]
30 Config(#[from] crate::config::Error),
31}
32
33#[cfg(feature = "fs")]
34util::impl_from_dynamic!(Error:Persistence, linera_persistent::file::Error);
35
36#[cfg(web)]
37util::impl_from_dynamic!(Error:Persistence, linera_persistent::indexed_db::Error);
38
39util::impl_from_infallible!(Error);
40
41#[derive(Clone, clap::Parser)]
42pub struct ClientContextOptions {
43 #[arg(long = "wallet")]
45 pub wallet_state_path: Option<PathBuf>,
46
47 #[arg(long = "keystore")]
49 pub keystore_path: Option<PathBuf>,
50
51 #[arg(long, short = 'w', value_parser = util::parse_ascii_alphanumeric_string)]
56 pub with_wallet: Option<String>,
57
58 #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
60 pub send_timeout: Duration,
61
62 #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
64 pub recv_timeout: Duration,
65
66 #[arg(long, default_value = "10")]
68 pub max_pending_message_bundles: usize,
69
70 #[arg(
72 long = "chain-worker-ttl-ms",
73 default_value = "30000",
74 value_parser = util::parse_millis
75 )]
76 pub chain_worker_ttl: Duration,
77
78 #[arg(
80 long = "retry-delay-ms",
81 default_value = "1000",
82 value_parser = util::parse_millis
83 )]
84 pub retry_delay: Duration,
85
86 #[arg(long, default_value = "10")]
88 pub max_retries: u32,
89
90 #[arg(long)]
93 pub wait_for_outgoing_messages: bool,
94
95 #[arg(long)]
97 pub long_lived_services: bool,
98
99 #[arg(long, default_value = "accept")]
101 pub blanket_message_policy: BlanketMessagePolicy,
102
103 #[arg(long, value_parser = util::parse_chain_set)]
107 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
108
109 #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
112 pub grace_period: f64,
113
114 #[arg(
116 long = "blob-download-timeout-ms",
117 default_value = "1000",
118 value_parser = util::parse_millis
119 )]
120 pub blob_download_timeout: Duration,
121}
122
123impl ClientContextOptions {
124 pub fn to_chain_client_options(&self) -> ChainClientOptions {
126 let message_policy = MessagePolicy::new(
127 self.blanket_message_policy,
128 self.restrict_chain_ids_to.clone(),
129 );
130 let cross_chain_message_delivery =
131 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
132 ChainClientOptions {
133 max_pending_message_bundles: self.max_pending_message_bundles,
134 message_policy,
135 cross_chain_message_delivery,
136 grace_period: self.grace_period,
137 blob_download_timeout: self.blob_download_timeout,
138 }
139 }
140}
141
142#[derive(Debug, Clone, clap::Args)]
143pub struct ChainOwnershipConfig {
144 #[arg(long, num_args(0..))]
146 super_owners: Vec<AccountOwner>,
147
148 #[arg(long, num_args(0..))]
150 owners: Vec<AccountOwner>,
151
152 #[arg(long, num_args(0..))]
157 owner_weights: Vec<u64>,
158
159 #[arg(long)]
162 multi_leader_rounds: Option<u32>,
163
164 #[arg(long)]
168 open_multi_leader_rounds: bool,
169
170 #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
172 fast_round_duration: Option<TimeDelta>,
173
174 #[arg(
176 long = "base-timeout-ms",
177 default_value = "10000",
178 value_parser = util::parse_millis_delta
179 )]
180 base_timeout: TimeDelta,
181
182 #[arg(
185 long = "timeout-increment-ms",
186 default_value = "1000",
187 value_parser = util::parse_millis_delta
188 )]
189 timeout_increment: TimeDelta,
190
191 #[arg(
194 long = "fallback-duration-ms",
195 default_value = "86400000", value_parser = util::parse_millis_delta
197 )]
198 pub fallback_duration: TimeDelta,
199}
200
201impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
202 type Error = Error;
203
204 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
205 let ChainOwnershipConfig {
206 super_owners,
207 owners,
208 owner_weights,
209 multi_leader_rounds,
210 fast_round_duration,
211 open_multi_leader_rounds,
212 base_timeout,
213 timeout_increment,
214 fallback_duration,
215 } = config;
216 if !owner_weights.is_empty() && owner_weights.len() != owners.len() {
217 return Err(Error::MisalignedWeights {
218 public_keys: owners.len(),
219 weights: owner_weights.len(),
220 });
221 }
222 let super_owners = super_owners.into_iter().collect();
223 let owners = owners
224 .into_iter()
225 .zip(owner_weights.into_iter().chain(iter::repeat(100)))
226 .collect();
227 let multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
228 let timeout_config = TimeoutConfig {
229 fast_round_duration,
230 base_timeout,
231 timeout_increment,
232 fallback_duration,
233 };
234 Ok(ChainOwnership {
235 super_owners,
236 owners,
237 multi_leader_rounds,
238 open_multi_leader_rounds,
239 timeout_config,
240 })
241 }
242}
243
244#[derive(Debug, Clone, clap::Args)]
245pub struct ApplicationPermissionsConfig {
246 #[arg(long)]
249 pub execute_operations: Option<Vec<ApplicationId>>,
250 #[arg(long)]
253 pub mandatory_applications: Option<Vec<ApplicationId>>,
254 #[arg(long)]
256 pub close_chain: Option<Vec<ApplicationId>>,
257 #[arg(long)]
260 pub change_application_permissions: Option<Vec<ApplicationId>>,
261 #[arg(long)]
264 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
265 #[arg(long)]
268 pub make_http_requests: Option<Vec<ApplicationId>>,
269}
270
271impl From<ApplicationPermissionsConfig> for ApplicationPermissions {
272 fn from(config: ApplicationPermissionsConfig) -> ApplicationPermissions {
273 ApplicationPermissions {
274 execute_operations: config.execute_operations,
275 mandatory_applications: config.mandatory_applications.unwrap_or_default(),
276 close_chain: config.close_chain.unwrap_or_default(),
277 change_application_permissions: config
278 .change_application_permissions
279 .unwrap_or_default(),
280 call_service_as_oracle: config.call_service_as_oracle,
281 make_http_requests: config.make_http_requests,
282 }
283 }
284}
285
286#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
287pub enum ResourceControlPolicyConfig {
288 NoFees,
289 Testnet,
290 #[cfg(with_testing)]
291 OnlyFuel,
292 #[cfg(with_testing)]
293 AllCategories,
294}
295
296impl ResourceControlPolicyConfig {
297 pub fn into_policy(self) -> ResourceControlPolicy {
298 match self {
299 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
300 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
301 #[cfg(with_testing)]
302 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
303 #[cfg(with_testing)]
304 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
305 }
306 }
307}
308
309impl std::str::FromStr for ResourceControlPolicyConfig {
310 type Err = String;
311
312 fn from_str(s: &str) -> Result<Self, Self::Err> {
313 clap::ValueEnum::from_str(s, true)
314 }
315}
316
317impl fmt::Display for ResourceControlPolicyConfig {
318 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
319 write!(f, "{:?}", self)
320 }
321}