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