linera_service/cli/
common_options.rs

1// Copyright (c) Zefchain Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! CLI options shared between the Linera CLI and other tools (e.g. pm-benchmark).
5
6use std::{env, path::PathBuf};
7
8use anyhow::{bail, Error};
9use linera_client::config::GenesisConfig;
10use linera_execution::WasmRuntime;
11
12use crate::{
13    storage::{CommonStorageOptions, StorageConfig},
14    Wallet,
15};
16
17/// Wallet, keystore, and storage configuration options common to all Linera client tools.
18#[derive(Clone, clap::Parser)]
19pub struct CommonCliOptions {
20    /// Sets the file storing the private state of user chains (an empty one will be created
21    /// if missing).
22    #[arg(long = "wallet")]
23    pub wallet_state_path: Option<PathBuf>,
24
25    /// Sets the file storing the keystore state.
26    #[arg(long = "keystore")]
27    pub keystore_path: Option<PathBuf>,
28
29    /// Given an ASCII alphanumeric parameter `X`, read the wallet state and the wallet
30    /// storage config from the environment variables `LINERA_WALLET_{X}` and
31    /// `LINERA_STORAGE_{X}` instead of `LINERA_WALLET` and
32    /// `LINERA_STORAGE`.
33    #[arg(long, short = 'w', value_parser = crate::util::parse_ascii_alphanumeric_string)]
34    pub with_wallet: Option<String>,
35
36    /// Storage configuration for the blockchain history.
37    #[arg(long = "storage", global = true)]
38    pub storage_config: Option<String>,
39
40    /// Common storage options.
41    #[command(flatten)]
42    pub common_storage_options: CommonStorageOptions,
43
44    /// The WebAssembly runtime to use.
45    #[arg(long)]
46    pub wasm_runtime: Option<WasmRuntime>,
47
48    /// Output log messages from contract execution.
49    #[arg(long = "with-application-logs", env = "LINERA_APPLICATION_LOGS")]
50    pub application_logs: bool,
51
52    /// The number of Tokio worker threads to use.
53    #[arg(long, env = "LINERA_CLIENT_TOKIO_THREADS")]
54    pub tokio_threads: Option<usize>,
55
56    /// The number of Tokio blocking threads to use.
57    #[arg(long, env = "LINERA_CLIENT_TOKIO_BLOCKING_THREADS")]
58    pub tokio_blocking_threads: Option<usize>,
59}
60
61impl CommonCliOptions {
62    pub fn suffix(&self) -> String {
63        self.with_wallet
64            .as_ref()
65            .map(|x| format!("_{x}"))
66            .unwrap_or_default()
67    }
68
69    pub fn storage_config(&self) -> Result<StorageConfig, Error> {
70        if let Some(config) = &self.storage_config {
71            return config.parse();
72        }
73        let suffix = self.suffix();
74        let storage_env_var = env::var(format!("LINERA_STORAGE{suffix}")).ok();
75        if let Some(config) = storage_env_var {
76            return config.parse();
77        }
78        cfg_if::cfg_if! {
79            if #[cfg(feature = "rocksdb")] {
80                let spawn_mode =
81                    linera_views::rocks_db::RocksDbSpawnMode::get_spawn_mode_from_runtime();
82                let inner_storage_config = crate::storage::InnerStorageConfig::RocksDb {
83                    path: linera_wallet_json::paths::config_dir()?.join("wallet.db"),
84                    spawn_mode,
85                };
86                let namespace = linera_storage::DEFAULT_NAMESPACE.to_string();
87                Ok(StorageConfig {
88                    inner_storage_config,
89                    namespace,
90                })
91            } else {
92                bail!("Cannot apply default storage because the feature 'rocksdb' was not selected");
93            }
94        }
95    }
96
97    pub fn wallet_path(&self) -> Result<PathBuf, Error> {
98        linera_wallet_json::paths::wallet_path(self.wallet_state_path.as_ref(), &self.suffix())
99    }
100
101    pub fn keystore_path(&self) -> Result<PathBuf, Error> {
102        linera_wallet_json::paths::keystore_path(self.keystore_path.as_ref(), &self.suffix())
103    }
104
105    pub fn wallet(&self) -> Result<Wallet, Error> {
106        Ok(Wallet::read(&self.wallet_path()?)?)
107    }
108
109    pub fn keystore(&self) -> Result<linera_wallet_json::Keystore, Error> {
110        Ok(linera_wallet_json::Keystore::read(&self.keystore_path()?)?)
111    }
112
113    pub fn create_wallet(&self, genesis_config: GenesisConfig) -> Result<Wallet, Error> {
114        let wallet_path = self.wallet_path()?;
115        if wallet_path.exists() {
116            bail!("Wallet already exists: {}", wallet_path.display());
117        }
118        let wallet = Wallet::create(&wallet_path, genesis_config)?;
119        wallet.save()?;
120        Ok(wallet)
121    }
122
123    pub fn create_keystore(
124        &self,
125        testing_prng_seed: Option<u64>,
126    ) -> Result<linera_wallet_json::Keystore, Error> {
127        let keystore_path = self.keystore_path()?;
128        if keystore_path.exists() {
129            bail!("Keystore already exists: {}", keystore_path.display());
130        }
131        Ok(linera_wallet_json::Keystore::create(
132            &keystore_path,
133            testing_prng_seed,
134        )?)
135    }
136}