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 = "enableLogsSnapshot",
171        env = "HOPRD_ENABLE_LOGS_SNAPSHOT",
172        help = "Enables downloading logs snapshot at node start. If this is set to true, the node will attempt to \
173                download logs snapshot from the configured `logsSnapshotUrl`.",
174        value_name = "ENABLE_LOGS_SNAPSHOT",
175        action = ArgAction::Count
176    )]
177    pub enable_logs_snapshot: u8,
178
179    #[arg(
180        long = "logsSnapshotUrl",
181        env = "HOPRD_LOGS_SNAPSHOT_URL",
182        help = "URL to download logs snapshot from. If none is provided or configured in the configuration file, the \
183                node will not attempt to download any logs snapshot.",
184        value_name = "LOGS_SNAPSHOT_URL"
185    )]
186    pub logs_snapshot_url: Option<String>,
187
188    #[arg(
189        long = "maxBlockRange",
190        help = "Maximum number of blocks that can be fetched in a batch request from the RPC provider.",
191        env = "HOPRD_MAX_BLOCK_RANGE",
192        value_name = "MAX_BLOCK_RANGE",
193        value_parser = clap::value_parser ! (u64)
194    )]
195    pub max_block_range: Option<u64>,
196
197    #[arg(
198        long = "maxRequestsPerSec",
199        help = "Maximum number of RPC requests that can be performed per second.",
200        env = "HOPRD_MAX_RPC_REQUESTS_PER_SEC",
201        value_name = "MAX_RPC_REQUESTS_PER_SEC",
202        value_parser = clap::value_parser ! (u32)
203    )]
204    pub max_rpc_requests_per_sec: Option<u32>,
205
206    #[arg(
207        long,
208        help = "A custom RPC provider to be used for the node to connect to blockchain",
209        env = "HOPRD_PROVIDER",
210        value_name = "PROVIDER"
211    )]
212    pub provider: Option<String>,
213
214    #[arg(
215        long,
216        help = "initialize a database if it doesn't already exist",
217        env = "HOPRD_INIT",
218        action = ArgAction::Count
219    )]
220    pub init: u8,
221
222    #[arg(
223        long = "forceInit",
224        help = "initialize a database, even if it already exists",
225        env = "HOPRD_FORCE_INIT",
226        action = ArgAction::Count
227    )]
228    pub force_init: u8,
229
230    #[arg(
231        long = "privateKey",
232        hide = true,
233        help = "A private key to be used for the node",
234        env = "HOPRD_PRIVATE_KEY",
235        value_name = "PRIVATE_KEY"
236    )]
237    pub private_key: Option<String>,
238
239    #[arg(
240        long = "testAnnounceLocalAddresses",
241        env = "HOPRD_TEST_ANNOUNCE_LOCAL_ADDRESSES",
242        help = "For testing local testnets. Announce local addresses",
243        hide = true,
244        action = ArgAction::Count
245    )]
246    pub test_announce_local_addresses: u8,
247
248    #[arg(
249        long = "testPreferLocalAddresses",
250        env = "HOPRD_TEST_PREFER_LOCAL_ADDRESSES",
251        help = "For testing local testnets. Prefer local peers to remote",
252        hide = true,
253        action = ArgAction::Count
254    )]
255    pub test_prefer_local_addresses: u8,
256
257    #[arg(
258        long = "probeRecheckThreshold",
259        help = "Timeframe in seconds after which it is reasonable to recheck the nearest neighbor",
260        value_name = "SECONDS",
261        value_parser = clap::value_parser ! (u64),
262        env = "HOPRD_PROBE_RECHECK_THRESHOLD",
263    )]
264    pub probe_recheck_threshold: Option<u64>,
265
266    #[arg(
267        long = "networkQualityThreshold",
268        help = "Minimum quality of a peer connection to be considered usable",
269        value_name = "THRESHOLD",
270        value_parser = clap::value_parser ! (f64),
271        env = "HOPRD_NETWORK_QUALITY_THRESHOLD"
272    )]
273    pub network_quality_threshold: Option<f64>,
274
275    #[arg(
276        long = "configurationFilePath",
277        required = false,
278        help = "Path to a file containing the entire HOPRd configuration",
279        value_name = "CONFIG_FILE_PATH",
280        value_parser = clap::value_parser ! (String),
281        env = "HOPRD_CONFIGURATION_FILE_PATH"
282    )]
283    pub configuration_file_path: Option<String>,
284
285    #[arg(
286        long = "safeTransactionServiceProvider",
287        value_name = "HOPRD_SAFE_TX_SERVICE_PROVIDER",
288        help = "Base URL for safe transaction service",
289        env = "HOPRD_SAFE_TRANSACTION_SERVICE_PROVIDER"
290    )]
291    pub safe_transaction_service_provider: Option<String>,
292
293    #[arg(
294        long = "safeAddress",
295        value_name = "HOPRD_SAFE_ADDR",
296        help = "Address of Safe that safeguards tokens",
297        env = "HOPRD_SAFE_ADDRESS"
298    )]
299    pub safe_address: Option<String>,
300
301    #[arg(
302        long = "moduleAddress",
303        value_name = "HOPRD_MODULE_ADDR",
304        help = "Address of the node management module",
305        env = "HOPRD_MODULE_ADDRESS"
306    )]
307    pub module_address: Option<String>,
308
309    #[arg(
310        long = "protocolConfig",
311        value_name = "HOPRD_PROTOCOL_CONFIG_PATH",
312        help = "Path to the protocol-config.json file",
313        env = "HOPRD_PROTOCOL_CONFIG_PATH"
314    )]
315    pub protocol_config_path: Option<String>,
316}