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}