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