linera_client/
client_options.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use 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    /// Sets the file storing the private state of user chains (an empty one will be created if missing)
44    #[arg(long = "wallet")]
45    pub wallet_state_path: Option<PathBuf>,
46
47    /// Sets the file storing the keystore state.
48    #[arg(long = "keystore")]
49    pub keystore_path: Option<PathBuf>,
50
51    /// Given an ASCII alphanumeric parameter `X`, read the wallet state and the wallet
52    /// storage config from the environment variables `LINERA_WALLET_{X}` and
53    /// `LINERA_STORAGE_{X}` instead of `LINERA_WALLET` and
54    /// `LINERA_STORAGE`.
55    #[arg(long, short = 'w', value_parser = util::parse_ascii_alphanumeric_string)]
56    pub with_wallet: Option<String>,
57
58    /// Timeout for sending queries (milliseconds)
59    #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
60    pub send_timeout: Duration,
61
62    /// Timeout for receiving responses (milliseconds)
63    #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
64    pub recv_timeout: Duration,
65
66    /// The maximum number of incoming message bundles to include in a block proposal.
67    #[arg(long, default_value = "10")]
68    pub max_pending_message_bundles: usize,
69
70    /// The duration in milliseconds after which an idle chain worker will free its memory.
71    #[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    /// Delay increment for retrying to connect to a validator.
79    #[arg(
80        long = "retry-delay-ms",
81        default_value = "1000",
82        value_parser = util::parse_millis
83    )]
84    pub retry_delay: Duration,
85
86    /// Number of times to retry connecting to a validator.
87    #[arg(long, default_value = "10")]
88    pub max_retries: u32,
89
90    /// Whether to wait until a quorum of validators has confirmed that all sent cross-chain
91    /// messages have been delivered.
92    #[arg(long)]
93    pub wait_for_outgoing_messages: bool,
94
95    /// (EXPERIMENTAL) Whether application services can persist in some cases between queries.
96    #[arg(long)]
97    pub long_lived_services: bool,
98
99    /// The policy for handling incoming messages.
100    #[arg(long, default_value = "accept")]
101    pub blanket_message_policy: BlanketMessagePolicy,
102
103    /// A set of chains to restrict incoming messages from. By default, messages
104    /// from all chains are accepted. To reject messages from all chains, specify
105    /// an empty string.
106    #[arg(long, value_parser = util::parse_chain_set)]
107    pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
108
109    /// An additional delay, after reaching a quorum, to wait for additional validator signatures,
110    /// as a fraction of time taken to reach quorum.
111    #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
112    pub grace_period: f64,
113
114    /// The delay when downloading a blob, after which we try a second validator, in milliseconds.
115    #[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    /// Creates [`ChainClientOptions`] with the corresponding values.
125    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    /// The new super owners.
145    #[arg(long, num_args(0..))]
146    super_owners: Vec<AccountOwner>,
147
148    /// The new regular owners.
149    #[arg(long, num_args(0..))]
150    owners: Vec<AccountOwner>,
151
152    /// Weights for the new owners.
153    ///
154    /// If they are specified there must be exactly one weight for each owner.
155    /// If no weights are given, every owner will have weight 100.
156    #[arg(long, num_args(0..))]
157    owner_weights: Vec<u64>,
158
159    /// The number of rounds in which every owner can propose blocks, i.e. the first round
160    /// number in which only a single designated leader is allowed to propose blocks.
161    #[arg(long)]
162    multi_leader_rounds: Option<u32>,
163
164    /// Whether the multi-leader rounds are unrestricted, i.e. not limited to chain owners.
165    /// This should only be `true` on chains with restrictive application permissions and an
166    /// application-based mechanism to select block proposers.
167    #[arg(long)]
168    open_multi_leader_rounds: bool,
169
170    /// The duration of the fast round, in milliseconds.
171    #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
172    fast_round_duration: Option<TimeDelta>,
173
174    /// The duration of the first single-leader and all multi-leader rounds.
175    #[arg(
176        long = "base-timeout-ms",
177        default_value = "10000",
178        value_parser = util::parse_millis_delta
179    )]
180    base_timeout: TimeDelta,
181
182    /// The number of milliseconds by which the timeout increases after each
183    /// single-leader round.
184    #[arg(
185        long = "timeout-increment-ms",
186        default_value = "1000",
187        value_parser = util::parse_millis_delta
188    )]
189    timeout_increment: TimeDelta,
190
191    /// The age of an incoming tracked or protected message after which the validators start
192    /// transitioning the chain to fallback mode, in milliseconds.
193    #[arg(
194        long = "fallback-duration-ms",
195        default_value = "86400000", // 1 day
196        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    /// If present, only operations from the specified applications are allowed, and
247    /// no system operations. Otherwise all operations are allowed.
248    #[arg(long)]
249    pub execute_operations: Option<Vec<ApplicationId>>,
250    /// At least one operation or incoming message from each of these applications must occur in
251    /// every block.
252    #[arg(long)]
253    pub mandatory_applications: Option<Vec<ApplicationId>>,
254    /// These applications are allowed to close the current chain using the system API.
255    #[arg(long)]
256    pub close_chain: Option<Vec<ApplicationId>>,
257    /// These applications are allowed to change the application permissions on the current chain
258    /// using the system API.
259    #[arg(long)]
260    pub change_application_permissions: Option<Vec<ApplicationId>>,
261    /// These applications are allowed to call services as oracles on the current chain using the
262    /// system API.
263    #[arg(long)]
264    pub call_service_as_oracle: Option<Vec<ApplicationId>>,
265    /// These applications are allowed to make HTTP requests on the current chain using the system
266    /// API.
267    #[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}