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