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, 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    /// 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 maximal number of chains loaded in memory at a given time.
71    #[arg(long, default_value = "40")]
72    pub max_loaded_chains: NonZeroUsize,
73
74    /// Delay increment for retrying to connect to a validator.
75    #[arg(
76        long = "retry-delay-ms",
77        default_value = "1000",
78        value_parser = util::parse_millis
79    )]
80    pub retry_delay: Duration,
81
82    /// Number of times to retry connecting to a validator.
83    #[arg(long, default_value = "10")]
84    pub max_retries: u32,
85
86    /// Whether to wait until a quorum of validators has confirmed that all sent cross-chain
87    /// messages have been delivered.
88    #[arg(long)]
89    pub wait_for_outgoing_messages: bool,
90
91    /// (EXPERIMENTAL) Whether application services can persist in some cases between queries.
92    #[arg(long)]
93    pub long_lived_services: bool,
94
95    /// The policy for handling incoming messages.
96    #[arg(long, default_value = "accept")]
97    pub blanket_message_policy: BlanketMessagePolicy,
98
99    /// A set of chains to restrict incoming messages from. By default, messages
100    /// from all chains are accepted. To reject messages from all chains, specify
101    /// an empty string.
102    #[arg(long, value_parser = util::parse_chain_set)]
103    pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
104
105    /// An additional delay, after reaching a quorum, to wait for additional validator signatures,
106    /// as a fraction of time taken to reach quorum.
107    #[arg(long, default_value_t = DEFAULT_GRACE_PERIOD)]
108    pub grace_period: f64,
109
110    /// The delay when downloading a blob, after which we try a second validator, in milliseconds.
111    #[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    /// Creates [`ChainClientOptions`] with the corresponding values.
121    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    /// The new super owners.
141    #[arg(long, num_args(0..))]
142    super_owners: Vec<AccountOwner>,
143
144    /// The new regular owners.
145    #[arg(long, num_args(0..))]
146    owners: Vec<AccountOwner>,
147
148    /// Weights for the new owners.
149    ///
150    /// If they are specified there must be exactly one weight for each owner.
151    /// If no weights are given, every owner will have weight 100.
152    #[arg(long, num_args(0..))]
153    owner_weights: Vec<u64>,
154
155    /// The number of rounds in which every owner can propose blocks, i.e. the first round
156    /// number in which only a single designated leader is allowed to propose blocks.
157    #[arg(long)]
158    multi_leader_rounds: Option<u32>,
159
160    /// Whether the multi-leader rounds are unrestricted, i.e. not limited to chain owners.
161    /// This should only be `true` on chains with restrictive application permissions and an
162    /// application-based mechanism to select block proposers.
163    #[arg(long)]
164    open_multi_leader_rounds: bool,
165
166    /// The duration of the fast round, in milliseconds.
167    #[arg(long = "fast-round-ms", value_parser = util::parse_millis_delta)]
168    fast_round_duration: Option<TimeDelta>,
169
170    /// The duration of the first single-leader and all multi-leader rounds.
171    #[arg(
172        long = "base-timeout-ms",
173        default_value = "10000",
174        value_parser = util::parse_millis_delta
175    )]
176    base_timeout: TimeDelta,
177
178    /// The number of milliseconds by which the timeout increases after each
179    /// single-leader round.
180    #[arg(
181        long = "timeout-increment-ms",
182        default_value = "1000",
183        value_parser = util::parse_millis_delta
184    )]
185    timeout_increment: TimeDelta,
186
187    /// The age of an incoming tracked or protected message after which the validators start
188    /// transitioning the chain to fallback mode, in milliseconds.
189    #[arg(
190        long = "fallback-duration-ms",
191        default_value = "86400000", // 1 day
192        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    /// If present, only operations from the specified applications are allowed, and
243    /// no system operations. Otherwise all operations are allowed.
244    #[arg(long)]
245    pub execute_operations: Option<Vec<ApplicationId>>,
246    /// At least one operation or incoming message from each of these applications must occur in
247    /// every block.
248    #[arg(long)]
249    pub mandatory_applications: Option<Vec<ApplicationId>>,
250    /// These applications are allowed to close the current chain using the system API.
251    #[arg(long)]
252    pub close_chain: Option<Vec<ApplicationId>>,
253    /// These applications are allowed to change the application permissions on the current chain
254    /// using the system API.
255    #[arg(long)]
256    pub change_application_permissions: Option<Vec<ApplicationId>>,
257    /// These applications are allowed to call services as oracles on the current chain using the
258    /// system API.
259    #[arg(long)]
260    pub call_service_as_oracle: Option<Vec<ApplicationId>>,
261    /// These applications are allowed to make HTTP requests on the current chain using the system
262    /// API.
263    #[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}