hoprd/
cli.rs

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