Skip to main content

linera_service/cli/
command.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use std::{borrow::Cow, num::NonZeroU16, path::PathBuf};
5
6use chrono::{DateTime, Utc};
7use linera_base::{
8    crypto::{AccountPublicKey, CryptoHash, ValidatorPublicKey},
9    data_types::{Amount, BlockHeight, Epoch, Timestamp},
10    identifiers::{Account, AccountOwner, ApplicationId, ChainId, ModuleId, StreamId},
11    time::Duration,
12    vm::VmRuntime,
13};
14use linera_client::{
15    chain_listener::ChainListenerConfig,
16    client_options::{
17        ApplicationPermissionsConfig, ChainOwnershipConfig, ResourceControlPolicyConfig,
18    },
19    util,
20};
21use linera_rpc::config::CrossChainConfig;
22
23use crate::{
24    cli::validator, query_subscription::parse_subscription_ttl, task_processor::parse_operator,
25};
26
27const DEFAULT_TOKENS_PER_CHAIN: Amount = Amount::from_millis(100);
28const DEFAULT_TRANSACTIONS_PER_BLOCK: usize = 1;
29const DEFAULT_WRAP_UP_MAX_IN_FLIGHT: usize = 5;
30const DEFAULT_NUM_CHAINS: usize = 10;
31const DEFAULT_BPS: usize = 10;
32
33/// Specification for a validator to be added to the committee.
34#[derive(Clone, Debug)]
35pub struct ValidatorToAdd {
36    /// The validator's public key.
37    pub public_key: ValidatorPublicKey,
38    /// The validator's account public key.
39    pub account_key: AccountPublicKey,
40    /// The network address of the validator.
41    pub address: String,
42    /// The number of votes assigned to the validator.
43    pub votes: u64,
44}
45
46impl std::str::FromStr for ValidatorToAdd {
47    type Err = anyhow::Error;
48
49    fn from_str(s: &str) -> Result<Self, Self::Err> {
50        let parts: Vec<&str> = s.split(',').collect();
51        anyhow::ensure!(
52            parts.len() == 4,
53            "Validator spec must be in format: public_key,account_key,address,votes"
54        );
55
56        Ok(ValidatorToAdd {
57            public_key: parts[0].parse()?,
58            account_key: parts[1].parse()?,
59            address: parts[2].to_string(),
60            votes: parts[3].parse()?,
61        })
62    }
63}
64
65#[derive(Clone, clap::Args, serde::Serialize)]
66#[serde(rename_all = "kebab-case")]
67/// Options controlling the behavior of the benchmark command.
68pub struct BenchmarkOptions {
69    /// How many chains to use.
70    #[arg(long, default_value_t = DEFAULT_NUM_CHAINS)]
71    pub num_chains: usize,
72
73    /// How many tokens to assign to each newly created chain.
74    /// These need to cover the transaction fees per chain for the benchmark.
75    #[arg(long, default_value_t = DEFAULT_TOKENS_PER_CHAIN)]
76    pub tokens_per_chain: Amount,
77
78    /// How many transactions to put in each block.
79    #[arg(long, default_value_t = DEFAULT_TRANSACTIONS_PER_BLOCK)]
80    pub transactions_per_block: usize,
81
82    /// The application ID of a fungible token on the wallet's default chain.
83    /// If none is specified, the benchmark uses the native token.
84    #[arg(long)]
85    pub fungible_application_id: Option<ApplicationId>,
86
87    /// The fixed BPS (Blocks Per Second) rate that block proposals will be sent at.
88    #[arg(long, default_value_t = DEFAULT_BPS)]
89    pub bps: usize,
90
91    /// If provided, will close the chains after the benchmark is finished. Keep in mind that
92    /// closing the chains might take a while, and will increase the validator latency while
93    /// they're being closed.
94    #[arg(long)]
95    pub close_chains: bool,
96
97    /// A comma-separated list of host:port pairs to query for health metrics.
98    /// If provided, the benchmark will check these endpoints for validator health
99    /// and terminate if any validator is unhealthy.
100    /// Example: "127.0.0.1:21100,validator-1.some-network.linera.net:21100"
101    #[arg(long)]
102    pub health_check_endpoints: Option<String>,
103
104    /// The maximum number of in-flight requests to validators when wrapping up the benchmark.
105    /// While wrapping up, this controls the concurrency level when processing inboxes and
106    /// closing chains.
107    #[arg(long, default_value_t = DEFAULT_WRAP_UP_MAX_IN_FLIGHT)]
108    pub wrap_up_max_in_flight: usize,
109
110    /// Confirm before starting the benchmark.
111    #[arg(long)]
112    pub confirm_before_start: bool,
113
114    /// How long to run the benchmark for. If not provided, the benchmark will run until
115    /// it is interrupted.
116    #[arg(long)]
117    pub runtime_in_seconds: Option<u64>,
118
119    /// The delay between chains, in milliseconds. For example, if set to 200ms, the first
120    /// chain will start, then the second will start 200 ms after the first one, the third
121    /// 200 ms after the second one, and so on.
122    /// This is used for slowly ramping up the TPS, so we don't pound the validators with the full
123    /// TPS all at once.
124    #[arg(long)]
125    pub delay_between_chains_ms: Option<u64>,
126
127    /// Path to YAML file containing chain IDs to send transfers to.
128    /// If not provided, only transfers between chains in the same wallet.
129    #[arg(long)]
130    pub config_path: Option<PathBuf>,
131
132    /// Transaction distribution mode. If false (default), distributes transactions evenly
133    /// across chains within each block. If true, sends all transactions in each block
134    /// to a single chain, rotating through chains for subsequent blocks.
135    #[arg(long)]
136    pub single_destination_per_block: bool,
137}
138
139impl Default for BenchmarkOptions {
140    fn default() -> Self {
141        Self {
142            num_chains: DEFAULT_NUM_CHAINS,
143            tokens_per_chain: DEFAULT_TOKENS_PER_CHAIN,
144            transactions_per_block: DEFAULT_TRANSACTIONS_PER_BLOCK,
145            wrap_up_max_in_flight: DEFAULT_WRAP_UP_MAX_IN_FLIGHT,
146            fungible_application_id: None,
147            bps: DEFAULT_BPS,
148            close_chains: false,
149            health_check_endpoints: None,
150            confirm_before_start: false,
151            runtime_in_seconds: None,
152            delay_between_chains_ms: None,
153            config_path: None,
154            single_destination_per_block: false,
155        }
156    }
157}
158
159#[derive(Clone, clap::Subcommand, serde::Serialize)]
160#[serde(rename_all = "kebab-case")]
161/// The benchmarking subcommands.
162pub enum BenchmarkCommand {
163    /// Start a single benchmark process, maintaining a given TPS.
164    Single {
165        /// The benchmark options.
166        #[command(flatten)]
167        options: BenchmarkOptions,
168    },
169
170    /// Run multiple benchmark processes in parallel.
171    Multi {
172        /// The benchmark options.
173        #[command(flatten)]
174        options: BenchmarkOptions,
175
176        /// The number of benchmark processes to run in parallel.
177        #[arg(long, default_value = "1")]
178        processes: usize,
179
180        /// The faucet (which implicitly defines the network)
181        #[arg(long)]
182        faucet: String,
183
184        /// If specified, a directory with a random name will be created in this directory, and the
185        /// client state will be stored there.
186        /// If not specified, a temporary directory will be used for each client.
187        #[arg(long)]
188        client_state_dir: Option<String>,
189
190        /// The delay between starting the benchmark processes, in seconds.
191        /// If --cross-wallet-transfers is true, this will be ignored.
192        #[arg(long, default_value = "10")]
193        delay_between_processes: u64,
194
195        /// Whether to send transfers between chains in different wallets.
196        #[arg(long)]
197        cross_wallet_transfers: bool,
198    },
199}
200
201impl BenchmarkCommand {
202    /// Returns the number of transactions per block configured for this benchmark.
203    pub fn transactions_per_block(&self) -> usize {
204        match self {
205            Self::Single { options } => options.transactions_per_block,
206            Self::Multi { options, .. } => options.transactions_per_block,
207        }
208    }
209}
210
211use crate::util::{
212    DEFAULT_PAUSE_AFTER_GQL_MUTATIONS_SECS, DEFAULT_PAUSE_AFTER_LINERA_SERVICE_SECS,
213};
214
215/// The subcommands of the Linera client binary.
216#[derive(Clone, clap::Subcommand)]
217pub enum ClientCommand {
218    /// Transfer funds
219    Transfer {
220        /// Sending chain ID (must be one of our chains)
221        #[arg(long = "from")]
222        sender: Account,
223
224        /// Recipient account
225        #[arg(long = "to")]
226        recipient: Account,
227
228        /// Amount to transfer
229        amount: Amount,
230    },
231
232    /// Open (i.e. activate) a new chain deriving the UID from an existing one.
233    OpenChain {
234        /// Chain ID (must be one of our chains).
235        #[arg(long = "from")]
236        chain_id: Option<ChainId>,
237
238        /// The new owner (otherwise create a key pair and remember it)
239        #[arg(long = "owner")]
240        owner: Option<AccountOwner>,
241
242        /// The initial balance of the new chain. This is subtracted from the parent chain's
243        /// balance.
244        #[arg(long = "initial-balance", default_value = "0")]
245        balance: Amount,
246
247        /// Whether to create a super owner for the new chain.
248        #[arg(long)]
249        super_owner: bool,
250    },
251
252    /// Open (i.e. activate) a new multi-owner chain deriving the UID from an existing one.
253    OpenMultiOwnerChain {
254        /// Chain ID (must be one of our chains).
255        #[arg(long = "from")]
256        chain_id: Option<ChainId>,
257
258        /// Options configuring the new chain's ownership.
259        #[clap(flatten)]
260        ownership_config: ChainOwnershipConfig,
261
262        /// Options configuring the new chain's application permissions.
263        #[clap(flatten)]
264        application_permissions_config: ApplicationPermissionsConfig,
265
266        /// The initial balance of the new chain. This is subtracted from the parent chain's
267        /// balance.
268        #[arg(long = "initial-balance", default_value = "0")]
269        balance: Amount,
270    },
271
272    /// Display who owns the chain, and how the owners work together proposing blocks.
273    ShowOwnership {
274        /// The ID of the chain whose owners will be changed.
275        #[clap(long)]
276        chain_id: Option<ChainId>,
277    },
278
279    /// Change who owns the chain, and how the owners work together proposing blocks.
280    ///
281    /// Specify the complete set of new owners, by public key. Existing owners that are
282    /// not included will be removed.
283    ChangeOwnership {
284        /// The ID of the chain whose owners will be changed.
285        #[clap(long)]
286        chain_id: Option<ChainId>,
287
288        /// Options configuring the new chain's ownership.
289        #[clap(flatten)]
290        ownership_config: ChainOwnershipConfig,
291    },
292
293    /// Change the preferred owner of a chain.
294    SetPreferredOwner {
295        /// The ID of the chain whose preferred owner will be changed.
296        #[clap(long)]
297        chain_id: Option<ChainId>,
298
299        /// The new preferred owner.
300        #[arg(long)]
301        owner: AccountOwner,
302    },
303
304    /// Changes the application permissions configuration.
305    ChangeApplicationPermissions {
306        /// The ID of the chain to which the new permissions will be applied.
307        #[arg(long)]
308        chain_id: Option<ChainId>,
309
310        /// Options configuring the new chain's application permissions.
311        #[clap(flatten)]
312        application_permissions_config: ApplicationPermissionsConfig,
313    },
314
315    /// Close an existing chain.
316    ///
317    /// A closed chain cannot execute operations or accept messages anymore.
318    /// It can still reject incoming messages, so they bounce back to the sender.
319    CloseChain {
320        /// Chain ID (must be one of our chains)
321        chain_id: ChainId,
322    },
323
324    /// Print out the network description.
325    ShowNetworkDescription,
326
327    /// Read the current native-token balance of the given account directly from the local
328    /// state.
329    ///
330    /// NOTE: The local balance does not reflect messages that are waiting to be picked in
331    /// the local inbox, or that have not been synchronized from validators yet. Use
332    /// `linera sync` then either `linera query-balance` or `linera process-inbox &&
333    /// linera local-balance` for a consolidated balance.
334    LocalBalance {
335        /// The account to read, written as `OWNER@CHAIN-ID` or simply `CHAIN-ID` for the
336        /// chain balance. By default, we read the chain balance of the default chain in
337        /// the wallet.
338        account: Option<Account>,
339    },
340
341    /// Simulate the execution of one block made of pending messages from the local inbox,
342    /// then read the native-token balance of the account from the local state.
343    ///
344    /// NOTE: The balance does not reflect messages that have not been synchronized from
345    /// validators yet. Call `linera sync` first to do so.
346    QueryBalance {
347        /// The account to query, written as `OWNER@CHAIN-ID` or simply `CHAIN-ID` for the
348        /// chain balance. By default, we read the chain balance of the default chain in
349        /// the wallet.
350        account: Option<Account>,
351    },
352
353    /// (DEPRECATED) Synchronize the local state of the chain with a quorum validators, then query the
354    /// local balance.
355    ///
356    /// This command is deprecated. Use `linera sync && linera query-balance` instead.
357    SyncBalance {
358        /// The account to query, written as `OWNER@CHAIN-ID` or simply `CHAIN-ID` for the
359        /// chain balance. By default, we read the chain balance of the default chain in
360        /// the wallet.
361        account: Option<Account>,
362    },
363
364    /// Synchronize the local state of the chain with a quorum validators.
365    Sync {
366        /// The chain to synchronize with validators. If omitted, synchronizes the
367        /// default chain of the wallet.
368        chain_id: Option<ChainId>,
369
370        /// Stop synchronizing at this block height (exclusive). For instance,
371        /// `--next-height 0` downloads zero blocks, `--next-height 10` downloads
372        /// blocks 0 through 9.
373        #[arg(long)]
374        next_height: Option<BlockHeight>,
375
376        /// Stop synchronizing at the first block with a timestamp greater than this
377        /// value. The format is `YYYY-MM-DDTHH:MM:SS` or
378        /// `YYYY-MM-DD HH:MM:SS` in UTC.
379        #[arg(long)]
380        until_block_time: Option<Timestamp>,
381    },
382
383    /// Process all pending incoming messages from the inbox of the given chain by creating as many
384    /// blocks as needed to execute all (non-failing) messages. Failing messages will be
385    /// marked as rejected and may bounce to their sender depending on their configuration.
386    ProcessInbox {
387        /// The chain to process. If omitted, uses the default chain of the wallet.
388        chain_id: Option<ChainId>,
389    },
390
391    /// Deprecates all committees up to and including the specified one.
392    RevokeEpochs {
393        /// The highest epoch to deprecate.
394        epoch: Epoch,
395    },
396
397    /// View or update the resource control policy
398    ResourceControlPolicy {
399        /// Set the price per unit of Wasm fuel.
400        #[arg(long)]
401        wasm_fuel_unit: Option<Amount>,
402
403        /// Set the price per unit of EVM fuel.
404        #[arg(long)]
405        evm_fuel_unit: Option<Amount>,
406
407        /// Set the price per read operation.
408        #[arg(long)]
409        read_operation: Option<Amount>,
410
411        /// Set the price per write operation.
412        #[arg(long)]
413        write_operation: Option<Amount>,
414
415        /// Set the price per byte read from runtime.
416        #[arg(long)]
417        byte_runtime: Option<Amount>,
418
419        /// Set the price per byte read.
420        #[arg(long)]
421        byte_read: Option<Amount>,
422
423        /// Set the price per byte written.
424        #[arg(long)]
425        byte_written: Option<Amount>,
426
427        /// Set the base price to read a blob.
428        #[arg(long)]
429        blob_read: Option<Amount>,
430
431        /// Set the base price to publish a blob.
432        #[arg(long)]
433        blob_published: Option<Amount>,
434
435        /// Set the price to read a blob, per byte.
436        #[arg(long)]
437        blob_byte_read: Option<Amount>,
438
439        /// The price to publish a blob, per byte.
440        #[arg(long)]
441        blob_byte_published: Option<Amount>,
442
443        /// Set the price per byte stored.
444        #[arg(long)]
445        byte_stored: Option<Amount>,
446
447        /// Set the base price of sending an operation from a block..
448        #[arg(long)]
449        operation: Option<Amount>,
450
451        /// Set the additional price for each byte in the argument of a user operation.
452        #[arg(long)]
453        operation_byte: Option<Amount>,
454
455        /// Set the base price of sending a message from a block..
456        #[arg(long)]
457        message: Option<Amount>,
458
459        /// Set the additional price for each byte in the argument of a user message.
460        #[arg(long)]
461        message_byte: Option<Amount>,
462
463        /// Set the price per query to a service as an oracle.
464        #[arg(long)]
465        service_as_oracle_query: Option<Amount>,
466
467        /// Set the price for performing an HTTP request.
468        #[arg(long)]
469        http_request: Option<Amount>,
470
471        /// Set the maximum amount of Wasm fuel per block.
472        #[arg(long)]
473        maximum_wasm_fuel_per_block: Option<u64>,
474
475        /// Set the maximum amount of EVM fuel per block.
476        #[arg(long)]
477        maximum_evm_fuel_per_block: Option<u64>,
478
479        /// Set the maximum time in milliseconds that a block can spend executing services as oracles.
480        #[arg(long)]
481        maximum_service_oracle_execution_ms: Option<u64>,
482
483        /// Set the maximum size of a block, in bytes.
484        #[arg(long)]
485        maximum_block_size: Option<u64>,
486
487        /// Set the maximum size of data blobs, compressed bytecode and other binary blobs,
488        /// in bytes.
489        #[arg(long)]
490        maximum_blob_size: Option<u64>,
491
492        /// Set the maximum number of published blobs per block.
493        #[arg(long)]
494        maximum_published_blobs: Option<u64>,
495
496        /// Set the maximum size of decompressed contract or service bytecode, in bytes.
497        #[arg(long)]
498        maximum_bytecode_size: Option<u64>,
499
500        /// Set the maximum size of a block proposal, in bytes.
501        #[arg(long)]
502        maximum_block_proposal_size: Option<u64>,
503
504        /// Set the maximum read data per block.
505        #[arg(long)]
506        maximum_bytes_read_per_block: Option<u64>,
507
508        /// Set the maximum write data per block.
509        #[arg(long)]
510        maximum_bytes_written_per_block: Option<u64>,
511
512        /// Set the maximum size of oracle responses.
513        #[arg(long)]
514        maximum_oracle_response_bytes: Option<u64>,
515
516        /// Set the maximum size in bytes of a received HTTP response.
517        #[arg(long)]
518        maximum_http_response_bytes: Option<u64>,
519
520        /// Set the maximum amount of time allowed to wait for an HTTP response.
521        #[arg(long)]
522        http_request_timeout_ms: Option<u64>,
523
524        /// Set the list of hosts that contracts and services can send HTTP requests to.
525        ///
526        /// Besides hostnames, the following special flags are recognized:
527        ///
528        /// - `FLAG_ZERO_HASH.linera.network`: Skip hashing of the execution state
529        ///   (return all zeros instead).
530        /// - `FLAG_FREE_REJECT.linera.network`: Make bouncing messages free of charge.
531        /// - `FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE.linera.network`: Require
532        ///   accepted (not rejected) incoming messages to satisfy mandatory application
533        ///   checks.
534        /// - `FLAG_FREE_APPLICATION_ID_<APP_ID>.linera.network`: Waive all message-
535        ///   and event-related fees for the given application ID (see also
536        ///   `--free-application-ids`).
537        #[arg(long, value_delimiter = ',')]
538        http_request_allow_list: Option<Vec<String>>,
539
540        /// Set the list of application IDs for which message- and event-related fees are waived.
541        ///
542        /// This is a convenience flag that adds
543        /// `FLAG_FREE_APPLICATION_ID_<APP_ID>.linera.network` entries to the HTTP
544        /// request allow list.
545        #[arg(long, value_delimiter = ',')]
546        free_application_ids: Option<Vec<String>>,
547    },
548
549    /// Run benchmarks to test network performance.
550    #[command(subcommand)]
551    Benchmark(BenchmarkCommand),
552
553    /// Create genesis configuration for a Linera deployment.
554    /// Create initial user chains and print information to be used for initialization of validator setup.
555    /// This will also create an initial wallet for the owner of the initial "root" chains.
556    CreateGenesisConfig {
557        /// Sets the file describing the public configurations of all validators
558        #[arg(long = "committee")]
559        committee_config_path: PathBuf,
560
561        /// The output config path to be consumed by the server
562        #[arg(long = "genesis")]
563        genesis_config_path: PathBuf,
564
565        /// Known initial balance of the chain
566        #[arg(long, default_value = "0")]
567        initial_funding: Amount,
568
569        /// The start timestamp: no blocks can be created before this time.
570        #[arg(long)]
571        start_timestamp: Option<DateTime<Utc>>,
572
573        /// Number of initial (aka "root") chains to create in addition to the admin chain.
574        num_other_initial_chains: u32,
575
576        /// Configure the resource control policy (notably fees) according to pre-defined
577        /// settings.
578        #[arg(long, default_value = "no-fees")]
579        policy_config: ResourceControlPolicyConfig,
580
581        /// Set the price per unit of Wasm fuel.
582        /// (This will overwrite value from `--policy-config`)
583        #[arg(long)]
584        wasm_fuel_unit_price: Option<Amount>,
585
586        /// Set the price per unit of EVM fuel.
587        /// (This will overwrite value from `--policy-config`)
588        #[arg(long)]
589        evm_fuel_unit_price: Option<Amount>,
590
591        /// Set the price per read operation.
592        /// (This will overwrite value from `--policy-config`)
593        #[arg(long)]
594        read_operation_price: Option<Amount>,
595
596        /// Set the price per write operation.
597        /// (This will overwrite value from `--policy-config`)
598        #[arg(long)]
599        write_operation_price: Option<Amount>,
600
601        /// Set the price per byte read from runtime.
602        /// (This will overwrite value from `--policy-config`)
603        #[arg(long)]
604        byte_runtime_price: Option<Amount>,
605
606        /// Set the price per byte read.
607        /// (This will overwrite value from `--policy-config`)
608        #[arg(long)]
609        byte_read_price: Option<Amount>,
610
611        /// Set the price per byte written.
612        /// (This will overwrite value from `--policy-config`)
613        #[arg(long)]
614        byte_written_price: Option<Amount>,
615
616        /// Set the base price to read a blob.
617        /// (This will overwrite value from `--policy-config`)
618        #[arg(long)]
619        blob_read_price: Option<Amount>,
620
621        /// Set the base price to publish a blob.
622        /// (This will overwrite value from `--policy-config`)
623        #[arg(long)]
624        blob_published_price: Option<Amount>,
625
626        /// Set the price to read a blob, per byte.
627        /// (This will overwrite value from `--policy-config`)
628        #[arg(long)]
629        blob_byte_read_price: Option<Amount>,
630
631        /// Set the price to publish a blob, per byte.
632        /// (This will overwrite value from `--policy-config`)
633        #[arg(long)]
634        blob_byte_published_price: Option<Amount>,
635
636        /// Set the price per byte stored.
637        /// (This will overwrite value from `--policy-config`)
638        #[arg(long)]
639        byte_stored_price: Option<Amount>,
640
641        /// Set the base price of sending an operation from a block..
642        /// (This will overwrite value from `--policy-config`)
643        #[arg(long)]
644        operation_price: Option<Amount>,
645
646        /// Set the additional price for each byte in the argument of a user operation.
647        /// (This will overwrite value from `--policy-config`)
648        #[arg(long)]
649        operation_byte_price: Option<Amount>,
650
651        /// Set the base price of sending a message from a block..
652        /// (This will overwrite value from `--policy-config`)
653        #[arg(long)]
654        message_price: Option<Amount>,
655
656        /// Set the additional price for each byte in the argument of a user message.
657        /// (This will overwrite value from `--policy-config`)
658        #[arg(long)]
659        message_byte_price: Option<Amount>,
660
661        /// Set the price per query to a service as an oracle.
662        #[arg(long)]
663        service_as_oracle_query_price: Option<Amount>,
664
665        /// Set the price for performing an HTTP request.
666        #[arg(long)]
667        http_request_price: Option<Amount>,
668
669        /// Set the maximum amount of Wasm fuel per block.
670        /// (This will overwrite value from `--policy-config`)
671        #[arg(long)]
672        maximum_wasm_fuel_per_block: Option<u64>,
673
674        /// Set the maximum amount of EVM fuel per block.
675        /// (This will overwrite value from `--policy-config`)
676        #[arg(long)]
677        maximum_evm_fuel_per_block: Option<u64>,
678
679        /// Set the maximum time in milliseconds that a block can spend executing services as oracles.
680        #[arg(long)]
681        maximum_service_oracle_execution_ms: Option<u64>,
682
683        /// Set the maximum size of a block.
684        /// (This will overwrite value from `--policy-config`)
685        #[arg(long)]
686        maximum_block_size: Option<u64>,
687
688        /// Set the maximum size of decompressed contract or service bytecode, in bytes.
689        /// (This will overwrite value from `--policy-config`)
690        #[arg(long)]
691        maximum_bytecode_size: Option<u64>,
692
693        /// Set the maximum size of data blobs, compressed bytecode and other binary blobs,
694        /// in bytes.
695        /// (This will overwrite value from `--policy-config`)
696        #[arg(long)]
697        maximum_blob_size: Option<u64>,
698
699        /// Set the maximum number of published blobs per block.
700        /// (This will overwrite value from `--policy-config`)
701        #[arg(long)]
702        maximum_published_blobs: Option<u64>,
703
704        /// Set the maximum size of a block proposal, in bytes.
705        /// (This will overwrite value from `--policy-config`)
706        #[arg(long)]
707        maximum_block_proposal_size: Option<u64>,
708
709        /// Set the maximum read data per block.
710        /// (This will overwrite value from `--policy-config`)
711        #[arg(long)]
712        maximum_bytes_read_per_block: Option<u64>,
713
714        /// Set the maximum write data per block.
715        /// (This will overwrite value from `--policy-config`)
716        #[arg(long)]
717        maximum_bytes_written_per_block: Option<u64>,
718
719        /// Set the maximum size of oracle responses.
720        /// (This will overwrite value from `--policy-config`)
721        #[arg(long)]
722        maximum_oracle_response_bytes: Option<u64>,
723
724        /// Set the maximum size in bytes of a received HTTP response.
725        #[arg(long)]
726        maximum_http_response_bytes: Option<u64>,
727
728        /// Set the maximum amount of time allowed to wait for an HTTP response.
729        #[arg(long)]
730        http_request_timeout_ms: Option<u64>,
731
732        /// Set the list of hosts that contracts and services can send HTTP requests to.
733        ///
734        /// Besides hostnames, the following special flags are recognized:
735        ///
736        /// - `FLAG_ZERO_HASH.linera.network`: Skip hashing of the execution state
737        ///   (return all zeros instead).
738        /// - `FLAG_FREE_REJECT.linera.network`: Make bouncing messages free of charge.
739        /// - `FLAG_MANDATORY_APPS_NEED_ACCEPTED_MESSAGE.linera.network`: Require
740        ///   accepted (not rejected) incoming messages to satisfy mandatory application
741        ///   checks.
742        /// - `FLAG_FREE_APPLICATION_ID_<APP_ID>.linera.network`: Waive all message-
743        ///   and event-related fees for the given application ID (see also
744        ///   `--free-application-ids`).
745        #[arg(long, value_delimiter = ',')]
746        http_request_allow_list: Option<Vec<String>>,
747
748        /// Set the list of application IDs for which message- and event-related fees are waived.
749        ///
750        /// This is a convenience flag that adds
751        /// `FLAG_FREE_APPLICATION_ID_<APP_ID>.linera.network` entries to the HTTP
752        /// request allow list.
753        #[arg(long, value_delimiter = ',')]
754        free_application_ids: Option<Vec<String>>,
755
756        /// Force this wallet to generate keys using a PRNG and a given seed. USE FOR
757        /// TESTING ONLY.
758        #[arg(long)]
759        testing_prng_seed: Option<u64>,
760
761        /// A unique name to identify this network.
762        #[arg(long)]
763        network_name: Option<String>,
764    },
765
766    /// Watch the network for notifications.
767    Watch {
768        /// The chain ID to watch.
769        chain_id: Option<ChainId>,
770
771        /// Show all notifications from all validators.
772        #[arg(long)]
773        raw: bool,
774    },
775
776    /// Run a GraphQL service to explore and extend the chains of the wallet.
777    Service {
778        /// Configuration for the chain listener backing the service.
779        #[command(flatten)]
780        config: ChainListenerConfig,
781
782        /// The port on which to run the server
783        #[arg(long)]
784        port: NonZeroU16,
785
786        /// The port to expose metrics on.
787        #[cfg(with_metrics)]
788        #[arg(long)]
789        metrics_port: NonZeroU16,
790
791        /// Application IDs of operator applications to watch.
792        /// When specified, a task processor is started alongside the node service.
793        #[arg(long = "operator-application-ids")]
794        operator_application_ids: Vec<ApplicationId>,
795
796        /// A controller to execute a dynamic set of applications running on a dynamic set of
797        /// chains.
798        #[arg(long = "controller-id")]
799        controller_application_id: Option<ApplicationId>,
800
801        /// Supported operators and their binary paths.
802        /// Format: `name=path` or just `name` (uses name as path).
803        /// Example: `--operators my-operator=/path/to/binary`
804        #[arg(long = "operators", value_parser = parse_operator)]
805        operators: Vec<(String, PathBuf)>,
806
807        /// Delay in seconds before retrying a failed operator task batch.
808        /// Only relevant when operators are configured via `--operator-application-ids`
809        /// or `--controller-id`.
810        #[arg(long, default_value = "5")]
811        task_retry_delay_secs: u64,
812
813        /// Run in read-only mode: disallow mutations and prevent queries from scheduling
814        /// operations. Use this when exposing the service to untrusted clients.
815        #[arg(long)]
816        read_only: bool,
817
818        /// Enable the application query response cache with the given per-chain capacity.
819        /// Each entry stores a serialized GraphQL response keyed by
820        /// (application_id, request_bytes). Incompatible with `--long-lived-services`.
821        #[arg(long, env = "LINERA_QUERY_CACHE_SIZE")]
822        query_cache_size: Option<usize>,
823
824        /// Allow a named GraphQL subscription query.
825        /// The operation name is extracted from the query string.
826        /// Repeatable.
827        /// Example: `--allow-subscription 'query CounterValue { getCounter { value } }'`
828        #[arg(long = "allow-subscription")]
829        allowed_subscriptions: Vec<String>,
830
831        /// Set a minimum TTL (in seconds) for a subscription query's cached result.
832        /// When set, invalidations that arrive before the TTL expires are deferred
833        /// until the remaining time elapses. Format: `Name=Secs`.
834        /// Repeatable.
835        /// Example: `--subscription-ttl-secs CounterValue=30`
836        #[arg(long = "subscription-ttl-secs", value_parser = parse_subscription_ttl)]
837        subscription_ttls: Vec<(String, u64)>,
838
839        /// Start in paused mode: do not synchronize chains from the network.
840        /// The service will serve queries from local state only, without downloading
841        /// new blocks or processing incoming messages.
842        #[arg(long)]
843        pause: bool,
844    },
845
846    /// Run a GraphQL service that exposes a faucet where users can claim tokens.
847    /// This gives away the chain's tokens, and is mainly intended for testing.
848    Faucet {
849        /// The chain that gives away its tokens.
850        chain_id: Option<ChainId>,
851
852        /// The port on which to run the server
853        #[arg(long, default_value = "8080")]
854        port: u16,
855
856        /// The port for prometheus to scrape.
857        #[cfg(with_metrics)]
858        #[arg(long, default_value = "9090")]
859        metrics_port: u16,
860
861        /// The number of tokens to send to each new chain.
862        #[arg(long)]
863        amount: Amount,
864
865        /// The number of tokens to send per daily claim. Set to 0 to disable daily claims.
866        #[arg(long, default_value = "0")]
867        daily_claim_amount: Amount,
868
869        /// The end timestamp: The faucet will rate-limit the token supply so it runs out of money
870        /// no earlier than this.
871        #[arg(long)]
872        limit_rate_until: Option<DateTime<Utc>>,
873
874        /// Configuration for the faucet chain listener.
875        #[command(flatten)]
876        config: ChainListenerConfig,
877
878        /// Path to the persistent storage file for faucet mappings.
879        #[arg(long)]
880        storage_path: PathBuf,
881
882        /// Maximum number of operations to include in a single block (default: 100).
883        #[arg(long, default_value = "100")]
884        max_batch_size: usize,
885    },
886
887    /// Publish module.
888    PublishModule {
889        /// Path to the Wasm file for the application "contract" bytecode.
890        contract: PathBuf,
891
892        /// Path to the Wasm file for the application "service" bytecode.
893        service: PathBuf,
894
895        /// The virtual machine runtime to use.
896        #[arg(long, default_value = "wasm")]
897        vm_runtime: VmRuntime,
898
899        /// An optional chain ID to publish the module. The default chain of the wallet
900        /// is used otherwise.
901        publisher: Option<ChainId>,
902    },
903
904    /// Publish a module along with the JSON-encoded `Formats` description loaded
905    /// from an insta SNAP file. The publication and the formats-registry write
906    /// happen atomically in a single block.
907    PublishModuleWithFormats {
908        /// Path to the Wasm file for the application "contract" bytecode.
909        contract: PathBuf,
910
911        /// Path to the Wasm file for the application "service" bytecode.
912        service: PathBuf,
913
914        /// Path to the insta SNAP file containing the YAML serialization of the
915        /// application's `Formats`.
916        formats: PathBuf,
917
918        /// The application ID of the formats registry that will receive the
919        /// JSON-encoded formats.
920        registry_application_id: ApplicationId,
921
922        /// The virtual machine runtime to use.
923        #[arg(long, default_value = "wasm")]
924        vm_runtime: VmRuntime,
925
926        /// An optional chain ID to publish the module. The default chain of the wallet
927        /// is used otherwise.
928        publisher: Option<ChainId>,
929    },
930
931    /// Print events from a specific chain and stream from a specified index.
932    ListEventsFromIndex {
933        /// The chain to query. If omitted, query the default chain of the wallet.
934        chain_id: Option<ChainId>,
935
936        /// The stream being considered.
937        #[arg(long)]
938        stream_id: StreamId,
939
940        /// Index of the message to start with
941        #[arg(long, default_value = "0")]
942        start_index: u32,
943    },
944
945    /// Publish a data blob of binary data.
946    PublishDataBlob {
947        /// Path to data blob file to be published.
948        blob_path: PathBuf,
949        /// An optional chain ID to publish the blob. The default chain of the wallet
950        /// is used otherwise.
951        publisher: Option<ChainId>,
952    },
953
954    // TODO(#2490): Consider removing or renaming this.
955    /// Verify that a data blob is readable.
956    ReadDataBlob {
957        /// The hash of the content.
958        hash: CryptoHash,
959        /// An optional chain ID to verify the blob. The default chain of the wallet
960        /// is used otherwise.
961        reader: Option<ChainId>,
962    },
963
964    /// Describe an existing application: print its `ApplicationDescription` (module
965    /// ID, creator chain, parameters and required dependencies) as JSON. The
966    /// description is content-addressed and fetched from the validators, so the
967    /// application need not be registered on the wallet's default chain.
968    DescribeApplication {
969        /// The ID of the application to describe.
970        application_id: ApplicationId,
971    },
972
973    /// Create an application.
974    CreateApplication {
975        /// The module ID of the application to create.
976        module_id: ModuleId,
977
978        /// An optional chain ID to host the application. The default chain of the wallet
979        /// is used otherwise.
980        creator: Option<ChainId>,
981
982        /// The shared parameters as JSON string.
983        #[arg(long)]
984        json_parameters: Option<String>,
985
986        /// Path to a JSON file containing the shared parameters.
987        #[arg(long)]
988        json_parameters_path: Option<PathBuf>,
989
990        /// The instantiation argument as a JSON string.
991        #[arg(long)]
992        json_argument: Option<String>,
993
994        /// Path to a JSON file containing the instantiation argument.
995        #[arg(long)]
996        json_argument_path: Option<PathBuf>,
997
998        /// The list of required dependencies of application, if any.
999        #[arg(long, num_args(0..))]
1000        required_application_ids: Option<Vec<ApplicationId>>,
1001    },
1002
1003    /// Create an application, and publish the required module.
1004    PublishAndCreate {
1005        /// Path to the Wasm file for the application "contract" bytecode.
1006        contract: PathBuf,
1007
1008        /// Path to the Wasm file for the application "service" bytecode.
1009        service: PathBuf,
1010
1011        /// The virtual machine runtime to use.
1012        #[arg(long, default_value = "wasm")]
1013        vm_runtime: VmRuntime,
1014
1015        /// An optional chain ID to publish the module. The default chain of the wallet
1016        /// is used otherwise.
1017        publisher: Option<ChainId>,
1018
1019        /// The shared parameters as JSON string.
1020        #[arg(long)]
1021        json_parameters: Option<String>,
1022
1023        /// Path to a JSON file containing the shared parameters.
1024        #[arg(long)]
1025        json_parameters_path: Option<PathBuf>,
1026
1027        /// The instantiation argument as a JSON string.
1028        #[arg(long)]
1029        json_argument: Option<String>,
1030
1031        /// Path to a JSON file containing the instantiation argument.
1032        #[arg(long)]
1033        json_argument_path: Option<PathBuf>,
1034
1035        /// The list of required dependencies of application, if any.
1036        #[arg(long, num_args(0..))]
1037        required_application_ids: Option<Vec<ApplicationId>>,
1038    },
1039
1040    /// Create an unassigned key pair.
1041    Keygen,
1042
1043    /// Link the owner to the chain.
1044    /// Expects that the caller has a private key corresponding to the `public_key`,
1045    /// otherwise block proposals will fail when signing with it.
1046    Assign {
1047        /// The owner to assign.
1048        #[arg(long)]
1049        owner: AccountOwner,
1050
1051        /// The ID of the chain.
1052        #[arg(long)]
1053        chain_id: ChainId,
1054    },
1055
1056    /// Retry a block we unsuccessfully tried to propose earlier.
1057    ///
1058    /// As long as a block is pending most other commands will fail, since it is unsafe to propose
1059    /// multiple blocks at the same height.
1060    RetryPendingBlock {
1061        /// The chain with the pending block. If not specified, the wallet's default chain is used.
1062        chain_id: Option<ChainId>,
1063    },
1064
1065    /// Execute a raw user operation on an application.
1066    ///
1067    /// The operation bytes are provided as a hex string (BCS-encoded).
1068    ExecuteOperation {
1069        /// The application to send the operation to.
1070        #[arg(long)]
1071        application_id: ApplicationId,
1072
1073        /// BCS-encoded operation bytes as a hex string.
1074        #[arg(long)]
1075        operation: String,
1076
1077        /// Chain ID to submit the operation on. Defaults to the wallet's default chain.
1078        #[arg(long)]
1079        chain_id: Option<ChainId>,
1080    },
1081
1082    /// Show the contents of the wallet.
1083    #[command(subcommand)]
1084    Wallet(WalletCommand),
1085
1086    /// Show the information about a chain.
1087    #[command(subcommand)]
1088    Chain(ChainCommand),
1089
1090    /// Manage Linera projects.
1091    #[command(subcommand)]
1092    Project(ProjectCommand),
1093
1094    /// Manage a local Linera Network.
1095    #[command(subcommand)]
1096    Net(NetCommand),
1097
1098    /// Manage validators in the committee.
1099    #[command(subcommand)]
1100    Validator(validator::Command),
1101
1102    /// Operation on the storage.
1103    #[command(subcommand)]
1104    Storage(DatabaseToolCommand),
1105
1106    /// Print CLI help in Markdown format, and exit.
1107    #[command(hide = true)]
1108    HelpMarkdown,
1109
1110    /// Extract a Bash and GraphQL script embedded in a markdown file and print it on
1111    /// `stdout`.
1112    #[command(hide = true)]
1113    ExtractScriptFromMarkdown {
1114        /// The source file
1115        path: PathBuf,
1116
1117        /// Insert a pause of N seconds after calls to `linera service`.
1118        #[arg(long, default_value = DEFAULT_PAUSE_AFTER_LINERA_SERVICE_SECS, value_parser = util::parse_secs)]
1119        pause_after_linera_service: Duration,
1120
1121        /// Insert a pause of N seconds after GraphQL queries.
1122        #[arg(long, default_value = DEFAULT_PAUSE_AFTER_GQL_MUTATIONS_SECS, value_parser = util::parse_secs)]
1123        pause_after_gql_mutations: Duration,
1124    },
1125
1126    /// Generate shell completion scripts
1127    Completion {
1128        /// The shell to generate completions for
1129        #[arg(value_enum)]
1130        shell: clap_complete::Shell,
1131    },
1132}
1133
1134impl ClientCommand {
1135    /// Returns the log file name to use based on the [`ClientCommand`] that will run.
1136    pub fn log_file_name(&self) -> Cow<'static, str> {
1137        match self {
1138            ClientCommand::Transfer { .. }
1139            | ClientCommand::OpenChain { .. }
1140            | ClientCommand::OpenMultiOwnerChain { .. }
1141            | ClientCommand::ShowOwnership { .. }
1142            | ClientCommand::ChangeOwnership { .. }
1143            | ClientCommand::SetPreferredOwner { .. }
1144            | ClientCommand::ChangeApplicationPermissions { .. }
1145            | ClientCommand::CloseChain { .. }
1146            | ClientCommand::ShowNetworkDescription
1147            | ClientCommand::LocalBalance { .. }
1148            | ClientCommand::QueryBalance { .. }
1149            | ClientCommand::SyncBalance { .. }
1150            | ClientCommand::Sync { .. }
1151            | ClientCommand::ProcessInbox { .. }
1152            | ClientCommand::ResourceControlPolicy { .. }
1153            | ClientCommand::RevokeEpochs { .. }
1154            | ClientCommand::CreateGenesisConfig { .. }
1155            | ClientCommand::PublishModule { .. }
1156            | ClientCommand::PublishModuleWithFormats { .. }
1157            | ClientCommand::ListEventsFromIndex { .. }
1158            | ClientCommand::PublishDataBlob { .. }
1159            | ClientCommand::ReadDataBlob { .. }
1160            | ClientCommand::DescribeApplication { .. }
1161            | ClientCommand::CreateApplication { .. }
1162            | ClientCommand::PublishAndCreate { .. }
1163            | ClientCommand::Keygen
1164            | ClientCommand::Assign { .. }
1165            | ClientCommand::Wallet { .. }
1166            | ClientCommand::Chain { .. }
1167            | ClientCommand::Validator { .. }
1168            | ClientCommand::RetryPendingBlock { .. }
1169            | ClientCommand::ExecuteOperation { .. } => "client".into(),
1170            ClientCommand::Benchmark(BenchmarkCommand::Single { .. }) => "single-benchmark".into(),
1171            ClientCommand::Benchmark(BenchmarkCommand::Multi { .. }) => "multi-benchmark".into(),
1172            ClientCommand::Net { .. } => "net".into(),
1173            ClientCommand::Project { .. } => "project".into(),
1174            ClientCommand::Watch { .. } => "watch".into(),
1175            ClientCommand::Storage { .. } => "storage".into(),
1176            ClientCommand::Service { port, .. } => format!("service-{port}").into(),
1177            ClientCommand::Faucet { .. } => "faucet".into(),
1178            ClientCommand::HelpMarkdown
1179            | ClientCommand::ExtractScriptFromMarkdown { .. }
1180            | ClientCommand::Completion { .. } => "tool".into(),
1181        }
1182    }
1183}
1184
1185#[derive(Clone, clap::Parser)]
1186/// The subcommands for managing the storage database.
1187pub enum DatabaseToolCommand {
1188    /// Delete all the namespaces in the database
1189    DeleteAll,
1190
1191    /// Delete a single namespace from the database
1192    DeleteNamespace,
1193
1194    /// Check existence of a namespace in the database
1195    CheckExistence,
1196
1197    /// Initialize a namespace in the database
1198    Initialize {
1199        /// The path to the genesis configuration file.
1200        #[arg(long = "genesis")]
1201        genesis_config_path: PathBuf,
1202    },
1203
1204    /// List the namespaces in the database
1205    ListNamespaces,
1206
1207    /// List the blob IDs in the database
1208    ListBlobIds,
1209
1210    /// List the chain IDs in the database
1211    ListChainIds,
1212}
1213
1214#[expect(clippy::large_enum_variant)]
1215#[derive(Clone, clap::Parser)]
1216/// The subcommands for managing a local Linera network.
1217pub enum NetCommand {
1218    /// Start a Local Linera Network
1219    Up {
1220        /// The number of initial "root" chains created in the genesis config on top of
1221        /// the default "admin" chain. All initial chains belong to the first "admin"
1222        /// wallet. It is recommended to use at least one other initial chain for the
1223        /// faucet.
1224        #[arg(long, default_value = "2")]
1225        other_initial_chains: u32,
1226
1227        /// The initial amount of native tokens credited in the initial "root" chains,
1228        /// including the default "admin" chain.
1229        #[arg(long, default_value = "1000000")]
1230        initial_amount: u128,
1231
1232        /// The number of validators in the local test network.
1233        #[arg(long, default_value = "1")]
1234        validators: usize,
1235
1236        /// The number of shards per validator in the local test network.
1237        #[arg(long, default_value = "1")]
1238        shards: usize,
1239
1240        /// Configure the resource control policy (notably fees) according to pre-defined
1241        /// settings.
1242        #[arg(long, default_value = "no-fees")]
1243        policy_config: ResourceControlPolicyConfig,
1244
1245        /// The configuration for cross-chain messages.
1246        #[clap(flatten)]
1247        cross_chain_config: CrossChainConfig,
1248
1249        /// Force this wallet to generate keys using a PRNG and a given seed. USE FOR
1250        /// TESTING ONLY.
1251        #[arg(long)]
1252        testing_prng_seed: Option<u64>,
1253
1254        /// Run with a specific path where the wallet and validator input files are.
1255        /// If none, then a temporary directory is created.
1256        #[arg(long)]
1257        path: Option<String>,
1258
1259        /// External protocol used, either `grpc` or `grpcs`.
1260        #[arg(long, default_value = "grpc")]
1261        external_protocol: String,
1262
1263        /// If present, a faucet is started using the chain provided by --faucet-chain, or
1264        /// the first non-admin chain if not provided.
1265        #[arg(long, default_value = "false")]
1266        with_faucet: bool,
1267
1268        /// When using --with-faucet, this specifies the chain on which the faucet will be started.
1269        /// If this is `n`, the `n`-th non-admin chain (lexicographically) in the wallet is selected.
1270        #[arg(long)]
1271        faucet_chain: Option<u32>,
1272
1273        /// The port on which to run the faucet server
1274        #[arg(long, default_value = "8080")]
1275        faucet_port: NonZeroU16,
1276
1277        /// The number of tokens to send to each new chain created by the faucet.
1278        #[arg(long, default_value = "1000")]
1279        faucet_amount: Amount,
1280
1281        /// Whether to start a block exporter for each validator.
1282        #[arg(long, default_value = "false")]
1283        with_block_exporter: bool,
1284
1285        /// The number of block exporters to start.
1286        #[arg(long, default_value = "1")]
1287        num_block_exporters: usize,
1288
1289        /// The address of the block exporter.
1290        #[arg(long, default_value = "localhost")]
1291        exporter_address: String,
1292
1293        /// The port on which to run the block exporter.
1294        #[arg(long, default_value = "8081")]
1295        exporter_port: NonZeroU16,
1296
1297        /// Set the list of hosts that contracts and services can send HTTP requests to.
1298        #[arg(long, value_delimiter = ',')]
1299        http_request_allow_list: Option<Vec<String>>,
1300    },
1301
1302    /// Print a bash helper script to make `linera net up` easier to use. The script is
1303    /// meant to be installed in `~/.bash_profile` or sourced when needed.
1304    Helper,
1305}
1306
1307#[derive(Clone, clap::Subcommand)]
1308/// The subcommands for managing the wallet.
1309pub enum WalletCommand {
1310    /// Show the contents of the wallet.
1311    Show {
1312        /// The chain to show the metadata.
1313        chain_id: Option<ChainId>,
1314        /// Only print a non-formatted list of the wallet's chain IDs.
1315        #[arg(long)]
1316        short: bool,
1317        /// Print only the chains that we have a key pair for.
1318        #[arg(long)]
1319        owned: bool,
1320    },
1321
1322    /// Change the wallet default chain.
1323    SetDefault {
1324        /// The chain to set as the default.
1325        chain_id: ChainId,
1326    },
1327
1328    /// Initialize a wallet from the genesis configuration.
1329    Init {
1330        /// The path to the genesis configuration for a Linera deployment. Either this or `--faucet`
1331        /// must be specified.
1332        #[arg(long = "genesis")]
1333        genesis_config_path: Option<PathBuf>,
1334
1335        /// The address of a faucet.
1336        #[arg(long = "faucet")]
1337        faucet: Option<String>,
1338
1339        /// Force this wallet to generate keys using a PRNG and a given seed. USE FOR
1340        /// TESTING ONLY.
1341        #[arg(long)]
1342        testing_prng_seed: Option<u64>,
1343    },
1344
1345    /// Request a new chain from a faucet and add it to the wallet.
1346    RequestChain {
1347        /// The address of a faucet.
1348        #[arg(long)]
1349        faucet: String,
1350
1351        /// Whether this chain should become the default chain.
1352        #[arg(long)]
1353        set_default: bool,
1354    },
1355
1356    /// Export the genesis configuration to a JSON file.
1357    ///
1358    /// By default, exports the genesis config from the current wallet. Alternatively,
1359    /// use `--faucet` to retrieve the genesis config directly from a faucet URL.
1360    ExportGenesis {
1361        /// Path to save the genesis configuration JSON file.
1362        output: PathBuf,
1363
1364        /// The address of a faucet to retrieve the genesis config from.
1365        /// If not specified, the genesis config is read from the current wallet.
1366        #[arg(long)]
1367        faucet: Option<String>,
1368    },
1369
1370    /// Add a new followed chain (i.e. a chain without keypair) to the wallet.
1371    FollowChain {
1372        /// The chain ID.
1373        chain_id: ChainId,
1374        /// Synchronize the new chain and download all its blocks from the validators.
1375        #[arg(long)]
1376        sync: bool,
1377    },
1378
1379    /// Forgets the specified chain's keys. The chain will still be followed by the
1380    /// wallet.
1381    ForgetKeys {
1382        /// The chain whose keys will be forgotten.
1383        chain_id: ChainId,
1384    },
1385
1386    /// Forgets the specified chain, including the associated key pair.
1387    ForgetChain {
1388        /// The chain to forget.
1389        chain_id: ChainId,
1390    },
1391}
1392
1393#[derive(Clone, clap::Subcommand)]
1394/// The subcommands for inspecting chains.
1395pub enum ChainCommand {
1396    /// Show the contents of a block.
1397    ShowBlock {
1398        /// The height of the block.
1399        height: BlockHeight,
1400        /// The chain to show the block (if not specified, the default chain from the
1401        /// wallet is used).
1402        chain_id: Option<ChainId>,
1403    },
1404
1405    /// Show the chain description of a chain.
1406    ShowChainDescription {
1407        /// The chain ID to show (if not specified, the default chain from the wallet is
1408        /// used).
1409        chain_id: Option<ChainId>,
1410    },
1411}
1412
1413#[derive(Clone, clap::Parser)]
1414/// The subcommands for managing Linera projects.
1415pub enum ProjectCommand {
1416    /// Create a new Linera project.
1417    New {
1418        /// The project name. A directory of the same name will be created in the current directory.
1419        name: String,
1420
1421        /// Use the given clone of the Linera repository instead of remote crates.
1422        #[arg(long)]
1423        linera_root: Option<PathBuf>,
1424    },
1425
1426    /// Test a Linera project.
1427    ///
1428    /// Equivalent to running `cargo test` with the appropriate test runner.
1429    Test {
1430        /// The path of the root of the Linera project to test.
1431        path: Option<PathBuf>,
1432    },
1433
1434    /// Build and publish a Linera project.
1435    PublishAndCreate {
1436        /// The path of the root of the Linera project.
1437        /// Defaults to current working directory if unspecified.
1438        path: Option<PathBuf>,
1439
1440        /// Specify the name of the Linera project.
1441        /// This is used to locate the generated bytecode files. The generated bytecode files should
1442        /// be of the form `<name>_{contract,service}.wasm`.
1443        ///
1444        /// Defaults to the package name in Cargo.toml, with dashes replaced by
1445        /// underscores.
1446        name: Option<String>,
1447
1448        /// An optional chain ID to publish the module. The default chain of the wallet
1449        /// is used otherwise.
1450        publisher: Option<ChainId>,
1451
1452        /// The virtual machine runtime to use.
1453        #[arg(long, default_value = "wasm")]
1454        vm_runtime: VmRuntime,
1455
1456        /// The shared parameters as JSON string.
1457        #[arg(long)]
1458        json_parameters: Option<String>,
1459
1460        /// Path to a JSON file containing the shared parameters.
1461        #[arg(long)]
1462        json_parameters_path: Option<PathBuf>,
1463
1464        /// The instantiation argument as a JSON string.
1465        #[arg(long)]
1466        json_argument: Option<String>,
1467
1468        /// Path to a JSON file containing the instantiation argument.
1469        #[arg(long)]
1470        json_argument_path: Option<PathBuf>,
1471
1472        /// The list of required dependencies of application, if any.
1473        #[arg(long, num_args(0..))]
1474        required_application_ids: Option<Vec<ApplicationId>>,
1475    },
1476}