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