1use std::{collections::HashSet, fmt, iter, num::NonZeroUsize, 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(long, default_value = "40")]
72 pub max_loaded_chains: NonZeroUsize,
73
74 #[arg(
76 long = "retry-delay-ms",
77 default_value = "1000",
78 value_parser = util::parse_millis
79 )]
80 pub retry_delay: Duration,
81
82 #[arg(long, default_value = "10")]
84 pub max_retries: u32,
85
86 #[arg(long)]
89 pub wait_for_outgoing_messages: bool,
90
91 #[arg(long)]
93 pub long_lived_services: bool,
94
95 #[arg(long, default_value = "accept")]
97 pub blanket_message_policy: BlanketMessagePolicy,
98
99 #[arg(long, value_parser = util::parse_chain_set)]
103 pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
104
105 #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
108 pub grace_period: f64,
109
110 #[arg(
112 long = "blob-download-timeout-ms",
113 default_value = "1000",
114 value_parser = util::parse_millis
115 )]
116 pub blob_download_timeout: Duration,
117}
118
119impl ClientContextOptions {
120 pub fn to_chain_client_options(&self) -> ChainClientOptions {
122 let message_policy = MessagePolicy::new(
123 self.blanket_message_policy,
124 self.restrict_chain_ids_to.clone(),
125 );
126 let cross_chain_message_delivery =
127 CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
128 ChainClientOptions {
129 max_pending_message_bundles: self.max_pending_message_bundles,
130 message_policy,
131 cross_chain_message_delivery,
132 grace_period: self.grace_period,
133 blob_download_timeout: self.blob_download_timeout,
134 }
135 }
136}
137
138#[derive(Debug, Clone, clap::Args)]
139pub struct ChainOwnershipConfig {
140 #[arg(long, num_args(0..))]
142 super_owners: Vec<AccountOwner>,
143
144 #[arg(long, num_args(0..))]
146 owners: Vec<AccountOwner>,
147
148 #[arg(long, num_args(0..))]
153 owner_weights: Vec<u64>,
154
155 #[arg(long)]
158 multi_leader_rounds: Option<u32>,
159
160 #[arg(long)]
164 open_multi_leader_rounds: bool,
165
166 #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
168 fast_round_duration: Option<TimeDelta>,
169
170 #[arg(
172 long = "base-timeout-ms",
173 default_value = "10000",
174 value_parser = util::parse_millis_delta
175 )]
176 base_timeout: TimeDelta,
177
178 #[arg(
181 long = "timeout-increment-ms",
182 default_value = "1000",
183 value_parser = util::parse_millis_delta
184 )]
185 timeout_increment: TimeDelta,
186
187 #[arg(
190 long = "fallback-duration-ms",
191 default_value = "86400000", value_parser = util::parse_millis_delta
193 )]
194 pub fallback_duration: TimeDelta,
195}
196
197impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
198 type Error = Error;
199
200 fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
201 let ChainOwnershipConfig {
202 super_owners,
203 owners,
204 owner_weights,
205 multi_leader_rounds,
206 fast_round_duration,
207 open_multi_leader_rounds,
208 base_timeout,
209 timeout_increment,
210 fallback_duration,
211 } = config;
212 if !owner_weights.is_empty() && owner_weights.len() != owners.len() {
213 return Err(Error::MisalignedWeights {
214 public_keys: owners.len(),
215 weights: owner_weights.len(),
216 });
217 }
218 let super_owners = super_owners.into_iter().collect();
219 let owners = owners
220 .into_iter()
221 .zip(owner_weights.into_iter().chain(iter::repeat(100)))
222 .collect();
223 let multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
224 let timeout_config = TimeoutConfig {
225 fast_round_duration,
226 base_timeout,
227 timeout_increment,
228 fallback_duration,
229 };
230 Ok(ChainOwnership {
231 super_owners,
232 owners,
233 multi_leader_rounds,
234 open_multi_leader_rounds,
235 timeout_config,
236 })
237 }
238}
239
240#[derive(Debug, Clone, clap::Args)]
241pub struct ApplicationPermissionsConfig {
242 #[arg(long)]
245 pub execute_operations: Option<Vec<ApplicationId>>,
246 #[arg(long)]
249 pub mandatory_applications: Option<Vec<ApplicationId>>,
250 #[arg(long)]
252 pub close_chain: Option<Vec<ApplicationId>>,
253 #[arg(long)]
256 pub change_application_permissions: Option<Vec<ApplicationId>>,
257 #[arg(long)]
260 pub call_service_as_oracle: Option<Vec<ApplicationId>>,
261 #[arg(long)]
264 pub make_http_requests: Option<Vec<ApplicationId>>,
265}
266
267impl From<ApplicationPermissionsConfig> for ApplicationPermissions {
268 fn from(config: ApplicationPermissionsConfig) -> ApplicationPermissions {
269 ApplicationPermissions {
270 execute_operations: config.execute_operations,
271 mandatory_applications: config.mandatory_applications.unwrap_or_default(),
272 close_chain: config.close_chain.unwrap_or_default(),
273 change_application_permissions: config
274 .change_application_permissions
275 .unwrap_or_default(),
276 call_service_as_oracle: config.call_service_as_oracle,
277 make_http_requests: config.make_http_requests,
278 }
279 }
280}
281
282#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
283pub enum ResourceControlPolicyConfig {
284 NoFees,
285 Testnet,
286 #[cfg(with_testing)]
287 OnlyFuel,
288 #[cfg(with_testing)]
289 AllCategories,
290}
291
292impl ResourceControlPolicyConfig {
293 pub fn into_policy(self) -> ResourceControlPolicy {
294 match self {
295 ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
296 ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
297 #[cfg(with_testing)]
298 ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
299 #[cfg(with_testing)]
300 ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
301 }
302 }
303}
304
305impl std::str::FromStr for ResourceControlPolicyConfig {
306 type Err = String;
307
308 fn from_str(s: &str) -> Result<Self, Self::Err> {
309 clap::ValueEnum::from_str(s, true)
310 }
311}
312
313impl fmt::Display for ResourceControlPolicyConfig {
314 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
315 write!(f, "{:?}", self)
316 }
317}