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}