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