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