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