Skip to main content

linera_client/
client_options.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{
5    collections::{BTreeMap, HashSet},
6    fmt,
7};
8
9use linera_base::{
10    data_types::{ApplicationPermissions, BlanketMessagePolicy, MessagePolicy, TimeDelta},
11    identifiers::{AccountOwner, ApplicationId, ChainId, GenericApplicationId},
12    ownership::ChainOwnership,
13    time::Duration,
14};
15use linera_core::{
16    client::{
17        chain_client, DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
18        DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE, DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS,
19        DEFAULT_MAX_EVENT_STREAM_QUERIES, DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
20    },
21    node::CrossChainMessageDelivery,
22    DEFAULT_QUORUM_GRACE_PERIOD,
23};
24use linera_execution::ResourceControlPolicy;
25
26#[cfg(not(web))]
27use crate::client_metrics::TimingConfig;
28use crate::util;
29
30#[derive(Debug, thiserror::Error)]
31#[allow(missing_docs)]
32pub enum Error {
33    #[error("I/O error: {0}")]
34    IoError(#[from] std::io::Error),
35    #[error("there are {public_keys} public keys but {weights} weights")]
36    MisalignedWeights { public_keys: usize, weights: usize },
37    #[error("config error: {0}")]
38    Config(#[from] crate::config::GenesisConfigError),
39}
40
41util::impl_from_infallible!(Error);
42
43/// Command-line options controlling the behavior of the chain client.
44#[derive(Clone, clap::Parser, serde::Deserialize, tsify::Tsify)]
45#[tsify(from_wasm_abi)]
46#[group(skip)]
47#[serde(default, rename_all = "camelCase")]
48pub struct Options {
49    /// Timeout for sending queries (milliseconds)
50    #[arg(long = "send-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
51    pub send_timeout: Duration,
52
53    /// Timeout for receiving responses (milliseconds)
54    #[arg(long = "recv-timeout-ms", default_value = "4000", value_parser = util::parse_millis)]
55    pub recv_timeout: Duration,
56
57    /// The maximum number of incoming message bundles to include in a block proposal.
58    #[arg(long, default_value = "300")]
59    pub max_pending_message_bundles: usize,
60
61    /// Maximum number of message bundles to discard from a block proposal due to block limit
62    /// errors before discarding all remaining bundles.
63    ///
64    /// Discarded bundles can be retried in the next block.
65    #[arg(long, default_value = "3")]
66    pub max_block_limit_errors: u32,
67
68    /// The maximum number of new stream events to include in a block proposal.
69    #[arg(long, default_value = "10")]
70    pub max_new_events_per_block: usize,
71
72    /// Time budget for staging message bundles in milliseconds. When set, limits bundle
73    /// execution by wall-clock time, in addition to the count limit from
74    /// `max_pending_message_bundles`.
75    #[arg(long = "staging-bundles-time-budget-ms", value_parser = util::parse_millis)]
76    pub staging_bundles_time_budget: Option<Duration>,
77
78    /// Comma-separated list of chain IDs whose incoming bundles should be processed first.
79    #[arg(long, value_parser = util::parse_chain_set)]
80    pub prioritize_bundles_from: Option<HashSet<ChainId>>,
81
82    /// Comma-separated list of chain IDs whose incoming bundles should be ignored.
83    #[arg(long, value_parser = util::parse_chain_set)]
84    pub ignore_bundles_from: Option<HashSet<ChainId>>,
85
86    /// The duration in milliseconds after which an idle chain worker will free its memory.
87    #[arg(
88        long = "chain-worker-ttl-ms",
89        default_value = "30000",
90        env = "LINERA_CHAIN_WORKER_TTL_MS",
91        value_parser = util::parse_millis,
92    )]
93    pub chain_worker_ttl: Duration,
94
95    /// The duration, in milliseconds, after which an idle sender chain worker will
96    /// free its memory.
97    #[arg(
98        long = "sender-chain-worker-ttl-ms",
99        default_value = "1000",
100        env = "LINERA_SENDER_CHAIN_WORKER_TTL_MS",
101        value_parser = util::parse_millis
102    )]
103    pub sender_chain_worker_ttl: Duration,
104
105    /// Maximum number of cross-chain requests coalesced into a single batch by the
106    /// per-chain driver. Bounds the worst-case write-lock hold time.
107    #[arg(long, default_value_t = 1000)]
108    pub cross_chain_batch_size_limit: usize,
109
110    /// Delay increment for retrying to connect to a validator.
111    #[arg(
112        long = "retry-delay-ms",
113        default_value = "1000",
114        value_parser = util::parse_millis
115    )]
116    pub retry_delay: Duration,
117
118    /// Number of times to retry connecting to a validator.
119    #[arg(long, default_value = "10")]
120    pub max_retries: u32,
121
122    /// Maximum backoff delay for retrying to connect to a validator.
123    #[arg(
124        long = "max-backoff-ms",
125        default_value = "30000",
126        value_parser = util::parse_millis
127    )]
128    pub max_backoff: Duration,
129
130    /// Initial probe interval (ms) for the notification circuit breaker. When a validator's
131    /// notification stream exhausts retries, the circuit breaker waits this long before
132    /// probing again. Doubles on each failed probe.
133    #[arg(
134        long = "notification-circuit-breaker-initial-probe-interval-ms",
135        default_value = "300000",
136        value_parser = util::parse_millis
137    )]
138    pub notification_circuit_breaker_initial_probe_interval: Duration,
139
140    /// Maximum probe interval (ms) for the notification circuit breaker. The probe interval
141    /// doubles on each failure but is capped at this value.
142    #[arg(
143        long = "notification-circuit-breaker-max-probe-interval-ms",
144        default_value = "3600000",
145        value_parser = util::parse_millis
146    )]
147    pub notification_circuit_breaker_max_probe_interval: Duration,
148
149    /// Whether to wait until a quorum of validators has confirmed that all sent cross-chain
150    /// messages have been delivered.
151    #[arg(long)]
152    pub wait_for_outgoing_messages: bool,
153
154    /// Whether to allow creating blocks in the fast round. Fast blocks have lower latency but
155    /// must be used carefully so that there are never any conflicting fast block proposals.
156    #[arg(long)]
157    pub allow_fast_blocks: bool,
158
159    /// (EXPERIMENTAL) Whether application services can persist in some cases between queries.
160    #[arg(long)]
161    pub long_lived_services: bool,
162
163    /// The policy for handling incoming messages.
164    #[arg(long, default_value_t, value_enum)]
165    pub blanket_message_policy: BlanketMessagePolicy,
166
167    /// A set of chains to restrict incoming messages and events from. By default, messages and
168    /// events from all chains are accepted. To reject all of them, specify an empty string. The
169    /// admin chain's event stream is always followed regardless of this setting.
170    #[arg(long, value_parser = util::parse_chain_set)]
171    pub restrict_chain_ids_to: Option<HashSet<ChainId>>,
172
173    /// A set of application IDs. If specified, only bundles with at least one message from one of
174    /// these applications will be accepted.
175    #[arg(long, value_parser = util::parse_app_set)]
176    pub reject_message_bundles_without_application_ids: Option<HashSet<GenericApplicationId>>,
177
178    /// A set of application IDs. If specified, only bundles where all messages are from one of
179    /// these applications will be accepted.
180    #[arg(long, value_parser = util::parse_app_set)]
181    pub reject_message_bundles_with_other_application_ids: Option<HashSet<GenericApplicationId>>,
182
183    /// A set of application IDs. If specified, only event streams created by applications from
184    /// this set are processed and followed. The admin chain's event stream is always followed.
185    #[arg(long, value_parser = util::parse_app_set)]
186    pub process_events_from_application_ids: Option<HashSet<GenericApplicationId>>,
187
188    /// A set of application IDs whose messages must never be rejected. Bundles whose messages
189    /// are all from one of these applications bypass the other rejection rules (except
190    /// `--restrict-chain-ids-to`), and on execution failure they (and subsequent bundles from
191    /// the same sender) are removed from the block for later retry instead of being rejected,
192    /// with a warning logged. Bundles that contain any message from an application not on this
193    /// list can be rejected.
194    #[arg(long, value_parser = util::parse_app_set)]
195    pub never_reject_application_ids: Option<HashSet<GenericApplicationId>>,
196
197    /// Enable timing reports during operations
198    #[cfg(not(web))]
199    #[arg(long)]
200    pub timings: bool,
201
202    /// Interval in seconds between timing reports (defaults to 5)
203    #[cfg(not(web))]
204    #[arg(long, default_value = "5")]
205    pub timing_interval: u64,
206
207    /// An additional delay, after reaching a quorum, to wait for additional validator signatures,
208    /// as a fraction of time taken to reach quorum.
209    #[arg(long, default_value_t = DEFAULT_QUORUM_GRACE_PERIOD)]
210    pub quorum_grace_period: f64,
211
212    /// The delay when downloading a blob, after which we try a second validator, in milliseconds.
213    #[arg(
214        long = "blob-download-hedge-delay-ms",
215        default_value = "1000",
216        value_parser = util::parse_millis,
217    )]
218    pub blob_download_hedge_delay: Duration,
219
220    /// The delay when downloading a batch of certificates, after which we try a second validator,
221    /// in milliseconds.
222    #[arg(
223        long = "cert-batch-download-hedge-delay-ms",
224        default_value = "1000",
225        value_parser = util::parse_millis
226    )]
227    pub certificate_batch_download_hedge_delay: Duration,
228
229    /// Maximum number of certificates that we download at a time from one validator when
230    /// synchronizing one of our chains.
231    #[arg(
232        long,
233        default_value_t = DEFAULT_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
234    )]
235    pub certificate_download_batch_size: u64,
236
237    /// Maximum number of certificates read from local storage and uploaded to a validator
238    /// at a time when synchronizing a chain.
239    #[arg(
240        long,
241        default_value_t = DEFAULT_CERTIFICATE_UPLOAD_BATCH_SIZE,
242    )]
243    pub certificate_upload_batch_size: u64,
244
245    /// Maximum number of sender certificates we try to download and receive in one go
246    /// when syncing sender chains.
247    #[arg(
248        long,
249        default_value_t = DEFAULT_SENDER_CERTIFICATE_DOWNLOAD_BATCH_SIZE,
250    )]
251    pub sender_certificate_download_batch_size: usize,
252
253    /// Maximum number of certificate batches downloaded concurrently during chain sync.
254    #[arg(long, default_value_t = DEFAULT_MAX_CONCURRENT_BATCH_DOWNLOADS)]
255    pub max_concurrent_batch_downloads: usize,
256
257    /// Maximum number of tasks that can are joined concurrently in the client.
258    #[arg(long, default_value = "100")]
259    pub max_joined_tasks: usize,
260
261    /// Maximum number of event stream IDs to include in a single `PreviousEventBlocks`
262    /// request. Larger sets are split into multiple requests.
263    #[arg(long, default_value_t = DEFAULT_MAX_EVENT_STREAM_QUERIES)]
264    pub max_event_stream_queries: usize,
265
266    /// Maximum expected latency in milliseconds for score normalization.
267    #[arg(
268        long,
269        default_value_t = linera_core::client::requests_scheduler::MAX_ACCEPTED_LATENCY_MS,
270        env = "LINERA_REQUESTS_SCHEDULER_MAX_ACCEPTED_LATENCY_MS"
271    )]
272    pub max_accepted_latency_ms: f64,
273
274    /// Time-to-live for cached responses in milliseconds.
275    #[arg(
276        long,
277        default_value_t = linera_core::client::requests_scheduler::CACHE_TTL_MS,
278        env = "LINERA_REQUESTS_SCHEDULER_CACHE_TTL_MS"
279    )]
280    pub cache_ttl_ms: u64,
281
282    /// Maximum number of entries in the cache.
283    #[arg(
284        long,
285        default_value_t = linera_core::client::requests_scheduler::CACHE_MAX_SIZE,
286        env = "LINERA_REQUESTS_SCHEDULER_CACHE_MAX_SIZE"
287    )]
288    pub cache_max_size: usize,
289
290    /// Maximum latency for an in-flight request before we stop deduplicating it (in milliseconds).
291    #[arg(
292        long,
293        default_value_t = linera_core::client::requests_scheduler::MAX_REQUEST_TTL_MS,
294        env = "LINERA_REQUESTS_SCHEDULER_MAX_REQUEST_TTL_MS"
295    )]
296    pub max_request_ttl_ms: u64,
297
298    /// Smoothing factor for Exponential Moving Averages (0 < alpha < 1).
299    /// Higher values give more weight to recent observations.
300    /// Typical values are between 0.01 and 0.5.
301    /// A value of 0.1 means that 10% of the new observation is considered
302    /// and 90% of the previous average is retained.
303    #[arg(
304        long,
305        default_value_t = linera_core::client::requests_scheduler::ALPHA_SMOOTHING_FACTOR,
306        env = "LINERA_REQUESTS_SCHEDULER_ALPHA"
307    )]
308    pub alpha: f64,
309
310    /// Delay in milliseconds between starting requests to different peers.
311    /// This helps to stagger requests and avoid overwhelming the network.
312    #[arg(
313        long,
314        default_value_t = linera_core::client::requests_scheduler::STAGGERED_DELAY_MS,
315        env = "LINERA_REQUESTS_SCHEDULER_ALTERNATIVE_PEERS_RETRY_DELAY_MS"
316    )]
317    pub alternative_peers_retry_delay_ms: u64,
318
319    /// Configuration for the chain listener.
320    #[serde(flatten)]
321    #[clap(flatten)]
322    pub chain_listener_config: crate::chain_listener::ChainListenerConfig,
323}
324
325impl Default for Options {
326    fn default() -> Self {
327        use clap::Parser;
328
329        #[derive(Parser)]
330        struct OptionsParser {
331            #[clap(flatten)]
332            options: Options,
333        }
334
335        OptionsParser::try_parse_from(std::iter::empty::<std::ffi::OsString>())
336            .expect("Options has no required arguments")
337            .options
338    }
339}
340
341impl Options {
342    /// Creates [`chain_client::Options`] with the corresponding values.
343    pub(crate) fn to_chain_client_options(&self) -> chain_client::Options {
344        let message_policy = MessagePolicy {
345            blanket: self.blanket_message_policy,
346            restrict_chain_ids_to: self.restrict_chain_ids_to.clone(),
347            ignore_chain_ids: self.ignore_bundles_from.clone().unwrap_or_default(),
348            reject_message_bundles_without_application_ids: self
349                .reject_message_bundles_without_application_ids
350                .clone(),
351            reject_message_bundles_with_other_application_ids: self
352                .reject_message_bundles_with_other_application_ids
353                .clone(),
354            process_events_from_application_ids: self.process_events_from_application_ids.clone(),
355            never_reject_application_ids: self
356                .never_reject_application_ids
357                .clone()
358                .unwrap_or_default(),
359        };
360        let cross_chain_message_delivery =
361            CrossChainMessageDelivery::new(self.wait_for_outgoing_messages);
362        chain_client::Options {
363            max_pending_message_bundles: self.max_pending_message_bundles,
364            max_block_limit_errors: self.max_block_limit_errors,
365            max_new_events_per_block: self.max_new_events_per_block,
366            staging_bundles_time_budget: self.staging_bundles_time_budget,
367            priority_bundle_origins: self.prioritize_bundles_from.clone().unwrap_or_default(),
368            message_policy,
369            cross_chain_message_delivery,
370            quorum_grace_period: self.quorum_grace_period,
371            blob_download_hedge_delay: self.blob_download_hedge_delay,
372            certificate_batch_download_hedge_delay: self.certificate_batch_download_hedge_delay,
373            certificate_download_batch_size: self.certificate_download_batch_size,
374            certificate_upload_batch_size: self.certificate_upload_batch_size,
375            sender_certificate_download_batch_size: self.sender_certificate_download_batch_size,
376            max_concurrent_batch_downloads: self.max_concurrent_batch_downloads,
377            max_joined_tasks: self.max_joined_tasks,
378            allow_fast_blocks: self.allow_fast_blocks,
379            notification_circuit_breaker_initial_probe_interval: self
380                .notification_circuit_breaker_initial_probe_interval,
381            notification_circuit_breaker_max_probe_interval: self
382                .notification_circuit_breaker_max_probe_interval,
383            max_event_stream_queries: self.max_event_stream_queries,
384        }
385    }
386
387    /// Creates [`TimingConfig`] with the corresponding values.
388    #[cfg(not(web))]
389    pub(crate) fn to_timing_config(&self) -> TimingConfig {
390        TimingConfig {
391            enabled: self.timings,
392            report_interval_secs: self.timing_interval,
393        }
394    }
395
396    /// Creates [`RequestsSchedulerConfig`] with the corresponding values.
397    pub(crate) fn to_requests_scheduler_config(
398        &self,
399    ) -> linera_core::client::RequestsSchedulerConfig {
400        linera_core::client::RequestsSchedulerConfig {
401            max_accepted_latency_ms: self.max_accepted_latency_ms,
402            cache_ttl_ms: self.cache_ttl_ms,
403            cache_max_size: self.cache_max_size,
404            max_request_ttl_ms: self.max_request_ttl_ms,
405            alpha: self.alpha,
406            retry_delay_ms: self.alternative_peers_retry_delay_ms,
407        }
408    }
409}
410
411/// Command-line options for configuring the ownership of a chain.
412#[derive(Debug, Clone, clap::Args)]
413pub struct ChainOwnershipConfig {
414    /// A JSON list of the new super owners. Absence of the option leaves the current
415    /// set of super owners unchanged.
416    // NOTE (applies to all fields): we need the std::option:: and std::vec:: qualifiers in order
417    // to throw off the #[derive(Args)] macro's automatic inference of the type it should expect
418    // from the parser. Without it, it infers the inner type (so either ApplicationId or
419    // Vec<ApplicationId>), which is not what we want here - we want the parsers to return the full
420    // expected types.
421    #[arg(long, value_parser = util::parse_json::<Vec<AccountOwner>>)]
422    pub super_owners: Option<std::vec::Vec<AccountOwner>>,
423
424    /// A JSON map of the new owners to their weights. Absence of the option leaves the current
425    /// set of owners unchanged.
426    #[arg(long, value_parser = util::parse_json::<BTreeMap<AccountOwner, u64>>)]
427    pub owners: Option<BTreeMap<AccountOwner, u64>>,
428
429    /// The number of rounds in which every owner can propose blocks, i.e. the first round
430    /// number in which only a single designated leader is allowed to propose blocks. "null" is
431    /// equivalent to 2^32 - 1. Absence of the option leaves the current setting unchanged.
432    #[arg(long, value_parser = util::parse_json::<Option<u32>>)]
433    pub multi_leader_rounds: Option<std::option::Option<u32>>,
434
435    /// Whether the multi-leader rounds are unrestricted, i.e. not limited to chain owners.
436    /// This should only be `true` on chains with restrictive application permissions and an
437    /// application-based mechanism to select block proposers.
438    #[arg(long)]
439    pub open_multi_leader_rounds: bool,
440
441    /// The duration of the fast round, in milliseconds. "null" means the fast round will
442    /// not time out. Absence of the option leaves the current setting unchanged.
443    #[arg(long = "fast-round-ms", value_parser = util::parse_json_optional_millis_delta)]
444    pub fast_round_duration: Option<std::option::Option<TimeDelta>>,
445
446    /// The duration of the first single-leader and all multi-leader rounds. Absence of
447    /// the option leaves the current setting unchanged.
448    #[arg(
449        long = "base-timeout-ms",
450        value_parser = util::parse_millis_delta
451    )]
452    pub base_timeout: Option<TimeDelta>,
453
454    /// The number of milliseconds by which the timeout increases after each
455    /// single-leader round. Absence of the option leaves the current setting unchanged.
456    #[arg(
457        long = "timeout-increment-ms",
458        value_parser = util::parse_millis_delta
459    )]
460    pub timeout_increment: Option<TimeDelta>,
461
462    /// The age of an incoming tracked or protected message after which the validators start
463    /// transitioning the chain to fallback mode, in milliseconds. Absence of the option
464    /// leaves the current setting unchanged.
465    #[arg(
466        long = "fallback-duration-ms",
467        value_parser = util::parse_millis_delta
468    )]
469    pub fallback_duration: Option<TimeDelta>,
470}
471
472impl ChainOwnershipConfig {
473    /// Applies the configured ownership overrides to the given chain ownership.
474    pub fn update(self, chain_ownership: &mut ChainOwnership) -> Result<(), Error> {
475        let ChainOwnershipConfig {
476            super_owners,
477            owners,
478            multi_leader_rounds,
479            fast_round_duration,
480            open_multi_leader_rounds,
481            base_timeout,
482            timeout_increment,
483            fallback_duration,
484        } = self;
485
486        if let Some(owners) = owners {
487            chain_ownership.owners = owners;
488        }
489
490        if let Some(super_owners) = super_owners {
491            chain_ownership.super_owners = super_owners.into_iter().collect();
492        }
493
494        if let Some(multi_leader_rounds) = multi_leader_rounds {
495            chain_ownership.multi_leader_rounds = multi_leader_rounds.unwrap_or(u32::MAX);
496        }
497
498        chain_ownership.open_multi_leader_rounds = open_multi_leader_rounds;
499
500        if let Some(fast_round_duration) = fast_round_duration {
501            chain_ownership.timeout_config.fast_round_duration = fast_round_duration;
502        }
503        if let Some(base_timeout) = base_timeout {
504            chain_ownership.timeout_config.base_timeout = base_timeout;
505        }
506        if let Some(timeout_increment) = timeout_increment {
507            chain_ownership.timeout_config.timeout_increment = timeout_increment;
508        }
509        if let Some(fallback_duration) = fallback_duration {
510            chain_ownership.timeout_config.fallback_duration = fallback_duration;
511        }
512
513        Ok(())
514    }
515}
516
517impl TryFrom<ChainOwnershipConfig> for ChainOwnership {
518    type Error = Error;
519
520    fn try_from(config: ChainOwnershipConfig) -> Result<ChainOwnership, Error> {
521        let mut chain_ownership = ChainOwnership::default();
522        config.update(&mut chain_ownership)?;
523        Ok(chain_ownership)
524    }
525}
526
527/// Command-line options for configuring application permissions on a chain.
528#[derive(Debug, Clone, clap::Args)]
529pub struct ApplicationPermissionsConfig {
530    /// A JSON list of applications allowed to execute operations on this chain. If set to null, all
531    /// operations will be allowed. Otherwise, only operations from the specified applications are
532    /// allowed, and no system operations. Absence of the option leaves current permissions
533    /// unchanged.
534    // NOTE (applies to all fields): we need the std::option:: and std::vec:: qualifiers in order
535    // to throw off the #[derive(Args)] macro's automatic inference of the type it should expect
536    // from the parser. Without it, it infers the inner type (so either ApplicationId or
537    // Vec<ApplicationId>), which is not what we want here - we want the parsers to return the full
538    // expected types.
539    #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
540    pub execute_operations: Option<std::option::Option<Vec<ApplicationId>>>,
541    /// A JSON list of applications, such that at least one operation or incoming message from each
542    /// of these applications must occur in every block. Absence of the option leaves
543    /// current mandatory applications unchanged.
544    #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
545    pub mandatory_applications: Option<std::vec::Vec<ApplicationId>>,
546    /// A JSON list of applications allowed to close the chain. Absence of the option leaves
547    /// the current list unchanged.
548    #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
549    pub close_chain: Option<std::vec::Vec<ApplicationId>>,
550    /// A JSON list of applications allowed to change the application permissions on the current
551    /// chain using the system API. Absence of the option leaves the current list unchanged.
552    #[arg(long, value_parser = util::parse_json::<Vec<ApplicationId>>)]
553    pub change_application_permissions: Option<std::vec::Vec<ApplicationId>>,
554    /// A JSON list of applications that are allowed to call services as oracles on the current
555    /// chain using the system API. If set to null, all applications will be able to do
556    /// so. Absence of the option leaves the current value of the setting unchanged.
557    #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
558    pub call_service_as_oracle: Option<std::option::Option<Vec<ApplicationId>>>,
559    /// A JSON list of applications that are allowed to make HTTP requests on the current chain
560    /// using the system API. If set to null, all applications will be able to do so.
561    /// Absence of the option leaves the current value of the setting unchanged.
562    #[arg(long, value_parser = util::parse_json::<Option<Vec<ApplicationId>>>)]
563    pub make_http_requests: Option<std::option::Option<Vec<ApplicationId>>>,
564}
565
566impl ApplicationPermissionsConfig {
567    /// Applies the configured permission overrides to the given application permissions.
568    pub fn update(self, application_permissions: &mut ApplicationPermissions) {
569        if let Some(execute_operations) = self.execute_operations {
570            application_permissions.execute_operations = execute_operations;
571        }
572        if let Some(mandatory_applications) = self.mandatory_applications {
573            application_permissions.mandatory_applications = mandatory_applications;
574        }
575        if let Some(close_chain) = self.close_chain {
576            application_permissions.close_chain = close_chain;
577        }
578        if let Some(change_application_permissions) = self.change_application_permissions {
579            application_permissions.change_application_permissions = change_application_permissions;
580        }
581        if let Some(call_service_as_oracle) = self.call_service_as_oracle {
582            application_permissions.call_service_as_oracle = call_service_as_oracle;
583        }
584        if let Some(make_http_requests) = self.make_http_requests {
585            application_permissions.make_http_requests = make_http_requests;
586        }
587    }
588}
589
590/// A named preset selecting which resource control policy the chain should use.
591#[derive(clap::ValueEnum, Clone, Copy, Debug, PartialEq, Eq)]
592pub enum ResourceControlPolicyConfig {
593    /// Charges nothing for any resource, with no usage limits.
594    NoFees,
595    /// Uses the fees and limits that match the public Testnet.
596    Testnet,
597    /// Charges only for fuel, leaving all other resources free (for testing).
598    #[cfg(with_testing)]
599    OnlyFuel,
600    /// Charges a small non-zero amount in every fee category (for testing).
601    #[cfg(with_testing)]
602    AllCategories,
603}
604
605impl ResourceControlPolicyConfig {
606    /// Converts this config into the corresponding resource control policy.
607    pub fn into_policy(self) -> ResourceControlPolicy {
608        match self {
609            ResourceControlPolicyConfig::NoFees => ResourceControlPolicy::no_fees(),
610            ResourceControlPolicyConfig::Testnet => ResourceControlPolicy::testnet(),
611            #[cfg(with_testing)]
612            ResourceControlPolicyConfig::OnlyFuel => ResourceControlPolicy::only_fuel(),
613            #[cfg(with_testing)]
614            ResourceControlPolicyConfig::AllCategories => ResourceControlPolicy::all_categories(),
615        }
616    }
617}
618
619impl std::str::FromStr for ResourceControlPolicyConfig {
620    type Err = String;
621
622    fn from_str(s: &str) -> Result<Self, Self::Err> {
623        clap::ValueEnum::from_str(s, true)
624    }
625}
626
627impl fmt::Display for ResourceControlPolicyConfig {
628    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
629        write!(f, "{self:?}")
630    }
631}