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