hoprd/
cli.rs

1use std::str::FromStr;
2
3use clap::{ArgAction, Parser, builder::ValueParser};
4use hopr_lib::{HostConfig, looks_like_domain};
5use serde::{Deserialize, Serialize};
6
7pub const DEFAULT_API_HOST: &str = "localhost";
8pub const DEFAULT_API_PORT: u16 = 3001;
9
10pub const MINIMAL_API_TOKEN_LENGTH: usize = 8;
11
12fn parse_host(s: &str) -> Result<HostConfig, String> {
13    let host = s.split_once(':').map_or(s, |(h, _)| h);
14    if !(validator::ValidateIp::validate_ipv4(&host) || looks_like_domain(host)) {
15        return Err(format!(
16            "Given string {s} is not a valid host, should have a format: <ip>:<port> or <domain>(:<port>)"
17        ));
18    }
19
20    HostConfig::from_str(s)
21}
22
23fn parse_api_token(mut s: &str) -> Result<String, String> {
24    if s.len() < MINIMAL_API_TOKEN_LENGTH {
25        return Err(format!(
26            "Length of API token is too short, minimally required {MINIMAL_API_TOKEN_LENGTH} but given {}",
27            s.len()
28        ));
29    }
30
31    match (s.starts_with('\''), s.ends_with('\'')) {
32        (true, true) => {
33            s = s.strip_prefix('\'').ok_or("failed to parse strip prefix part")?;
34            s = s.strip_suffix('\'').ok_or("failed to parse strip suffix part")?;
35
36            Ok(s.into())
37        }
38        (true, false) => Err("Found leading quote but no trailing quote".into()),
39        (false, true) => Err("Found trailing quote but no leading quote".into()),
40        (false, false) => Ok(s.into()),
41    }
42}
43
44/// Takes all CLI arguments whose structure is known at compile-time.
45/// Arguments whose structure, e.g. their default values depend on
46/// file contents need be specified using `clap`s builder API
47#[derive(Serialize, Deserialize, Clone, Parser)]
48#[command(author, version, about, long_about = None)]
49pub struct CliArgs {
50    /// Network the node will operate in
51    #[arg(
52        long,
53        env = "HOPRD_NETWORK",
54        help = "ID of the network the node will attempt to connect to",
55        required = false
56    )]
57    pub network: Option<String>,
58
59    // Identity details
60    #[arg(
61        long,
62        env = "HOPRD_IDENTITY",
63        help = "The path to the identity file",
64        required = false
65    )]
66    pub identity: Option<String>,
67
68    // Identity details
69    #[arg(
70        long,
71        env = "HOPRD_DATA",
72        help = "Specifies the directory to hold all the data",
73        required = false
74    )]
75    pub data: Option<String>,
76
77    #[arg(
78        long,
79        env = "HOPRD_HOST",
80        help = "Host to listen on for P2P connections",
81        value_parser = ValueParser::new(parse_host),
82    )]
83    pub host: Option<HostConfig>,
84
85    #[arg(
86        long,
87        env = "HOPRD_ANNOUNCE",
88        help = "Announce the node on chain with a public address",
89        action = ArgAction::Count
90    )]
91    pub announce: u8,
92
93    #[arg(
94        long,
95        env = "HOPRD_API",
96        help = format!("Expose the API on {}:{}", DEFAULT_API_HOST, DEFAULT_API_PORT),
97        action = ArgAction::Count
98    )]
99    pub api: u8,
100
101    #[arg(
102        long = "apiHost",
103        value_name = "HOST",
104        help = "Set host IP to which the API server will bind",
105        env = "HOPRD_API_HOST"
106    )]
107    pub api_host: Option<String>,
108
109    #[arg(
110        long = "apiPort",
111        value_parser = clap::value_parser ! (u16),
112        value_name = "PORT",
113        help = "Set port to which the API server will bind",
114        env = "HOPRD_API_PORT"
115    )]
116    pub api_port: Option<u16>,
117
118    #[arg(
119        long = "defaultSessionListenHost",
120        env = "HOPRD_DEFAULT_SESSION_LISTEN_HOST",
121        help = "Default Session listening host for Session IP forwarding",
122        value_parser = ValueParser::new(parse_host),
123    )]
124    pub default_session_listen_host: Option<HostConfig>,
125
126    #[arg(
127        long = "disableApiAuthentication",
128        help = "Completely disables the token authentication for the API, overrides any apiToken if set",
129        env = "HOPRD_DISABLE_API_AUTHENTICATION",
130        hide = true,
131        action = ArgAction::Count
132    )]
133    pub disable_api_authentication: u8,
134
135    #[arg(
136        long = "apiToken",
137        alias = "api-token",
138        help = "A REST API token and for user authentication",
139        value_name = "TOKEN",
140        value_parser = ValueParser::new(parse_api_token),
141        env = "HOPRD_API_TOKEN"
142    )]
143    pub api_token: Option<String>,
144
145    #[arg(
146        long,
147        env = "HOPRD_PASSWORD",
148        help = "A password to encrypt your keys",
149        value_name = "PASSWORD"
150    )]
151    pub password: Option<String>,
152
153    #[arg(
154        long = "noKeepLogs",
155        env = "HOPRD_INDEXER_DISABLE_KEEP_LOGS",
156        help = "Disables keeping RPC logs in the logs database after they were processed.",
157        action = ArgAction::Count
158    )]
159    pub no_keep_logs: u8,
160
161    #[arg(
162        long = "noFastSync",
163        env = "HOPRD_INDEXER_DISABLE_FAST_SYNC",
164        help = "Disables using fast sync at node start.",
165        action = ArgAction::Count
166    )]
167    pub no_fast_sync: u8,
168
169    #[arg(
170        long = "maxBlockRange",
171        help = "Maximum number of blocks that can be fetched in a batch request from the RPC provider.",
172        env = "HOPRD_MAX_BLOCK_RANGE",
173        value_name = "MAX_BLOCK_RANGE",
174        value_parser = clap::value_parser ! (u64)
175    )]
176    pub max_block_range: Option<u64>,
177
178    #[arg(
179        long = "maxRequestsPerSec",
180        help = "Maximum number of RPC requests that can be performed per second.",
181        env = "HOPRD_MAX_RPC_REQUESTS_PER_SEC",
182        value_name = "MAX_RPC_REQUESTS_PER_SEC",
183        value_parser = clap::value_parser ! (u32)
184    )]
185    pub max_rpc_requests_per_sec: Option<u32>,
186
187    #[arg(
188        long,
189        help = "A custom RPC provider to be used for the node to connect to blockchain",
190        env = "HOPRD_PROVIDER",
191        value_name = "PROVIDER"
192    )]
193    pub provider: Option<String>,
194
195    #[arg(
196        long,
197        help = "initialize a database if it doesn't already exist",
198        env = "HOPRD_INIT",
199        action = ArgAction::Count
200    )]
201    pub init: u8,
202
203    #[arg(
204        long = "forceInit",
205        help = "initialize a database, even if it already exists",
206        env = "HOPRD_FORCE_INIT",
207        action = ArgAction::Count
208    )]
209    pub force_init: u8,
210
211    #[arg(
212        long = "privateKey",
213        hide = true,
214        help = "A private key to be used for the node",
215        env = "HOPRD_PRIVATE_KEY",
216        value_name = "PRIVATE_KEY"
217    )]
218    pub private_key: Option<String>,
219
220    #[arg(
221        long = "testAnnounceLocalAddresses",
222        env = "HOPRD_TEST_ANNOUNCE_LOCAL_ADDRESSES",
223        help = "For testing local testnets. Announce local addresses",
224        hide = true,
225        action = ArgAction::Count
226    )]
227    pub test_announce_local_addresses: u8,
228
229    #[arg(
230        long = "testPreferLocalAddresses",
231        env = "HOPRD_TEST_PREFER_LOCAL_ADDRESSES",
232        help = "For testing local testnets. Prefer local peers to remote",
233        hide = true,
234        action = ArgAction::Count
235    )]
236    pub test_prefer_local_addresses: u8,
237
238    #[arg(
239        long = "probeRecheckThreshold",
240        help = "Timeframe in seconds after which it is reasonable to recheck the nearest neighbor",
241        value_name = "SECONDS",
242        value_parser = clap::value_parser ! (u64),
243        env = "HOPRD_PROBE_RECHECK_THRESHOLD",
244    )]
245    pub probe_recheck_threshold: Option<u64>,
246
247    #[arg(
248        long = "networkQualityThreshold",
249        help = "Minimum quality of a peer connection to be considered usable",
250        value_name = "THRESHOLD",
251        value_parser = clap::value_parser ! (f64),
252        env = "HOPRD_NETWORK_QUALITY_THRESHOLD"
253    )]
254    pub network_quality_threshold: Option<f64>,
255
256    #[arg(
257        long = "configurationFilePath",
258        required = false,
259        help = "Path to a file containing the entire HOPRd configuration",
260        value_name = "CONFIG_FILE_PATH",
261        value_parser = clap::value_parser ! (String),
262        env = "HOPRD_CONFIGURATION_FILE_PATH"
263    )]
264    pub configuration_file_path: Option<String>,
265
266    #[arg(
267        long = "safeTransactionServiceProvider",
268        value_name = "HOPRD_SAFE_TX_SERVICE_PROVIDER",
269        help = "Base URL for safe transaction service",
270        env = "HOPRD_SAFE_TRANSACTION_SERVICE_PROVIDER"
271    )]
272    pub safe_transaction_service_provider: Option<String>,
273
274    #[arg(
275        long = "safeAddress",
276        value_name = "HOPRD_SAFE_ADDR",
277        help = "Address of Safe that safeguards tokens",
278        env = "HOPRD_SAFE_ADDRESS"
279    )]
280    pub safe_address: Option<String>,
281
282    #[arg(
283        long = "moduleAddress",
284        value_name = "HOPRD_MODULE_ADDR",
285        help = "Address of the node management module",
286        env = "HOPRD_MODULE_ADDRESS"
287    )]
288    pub module_address: Option<String>,
289
290    #[arg(
291        long = "protocolConfig",
292        value_name = "HOPRD_PROTOCOL_CONFIG_PATH",
293        help = "Path to the protocol-config.json file",
294        env = "HOPRD_PROTOCOL_CONFIG_PATH"
295    )]
296    pub protocol_config_path: Option<String>,
297}