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#[derive(Serialize, Deserialize, Clone, Parser)]
49#[command(author, version, about, long_about = None)]
50pub struct CliArgs {
51 #[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 #[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 #[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}