1#![doc = include_str!("../doc/setup/NodeSetup.md")]
7use crate::core::gossipsub_cfg::Blacklist;
8pub use crate::prelude::*;
9pub use libp2p_identity::{rsa::Keypair as RsaKeypair, KeyType, Keypair, PeerId};
10
11use super::*;
13
14#[derive(Debug)]
16pub struct BootstrapConfig {
17 tcp_port: Port,
19 udp_port: Port,
21 keypair: Keypair,
23 boot_nodes: Nodes,
25 blacklist: Blacklist,
27}
28
29impl BootstrapConfig {
30 pub fn from_file(file_path: &str) -> Self {
36 util::read_ini_file(file_path).unwrap()
37 }
38
39 pub fn new() -> Self {
44 BootstrapConfig {
45 tcp_port: MIN_PORT,
47 udp_port: MAX_PORT,
49 keypair: Keypair::generate_ed25519(),
51 boot_nodes: Default::default(),
52 blacklist: Default::default(),
54 }
55 }
56
57 pub fn with_bootnodes(mut self, boot_nodes: Nodes) -> Self {
59 self.boot_nodes.extend(boot_nodes.into_iter());
61 self
62 }
63
64 pub fn with_blacklist(mut self, list: Vec<PeerId>) -> Self {
66 self.blacklist.list.extend(list.into_iter());
68 self
69 }
70
71 pub fn with_tcp(self, tcp_port: Port) -> Self {
75 if tcp_port > MIN_PORT && tcp_port < MAX_PORT {
76 BootstrapConfig { tcp_port, ..self }
77 } else {
78 self
79 }
80 }
81
82 pub fn with_udp(self, udp_port: Port) -> Self {
86 if udp_port > MIN_PORT && udp_port < MAX_PORT {
87 BootstrapConfig { udp_port, ..self }
88 } else {
89 self
90 }
91 }
92
93 pub fn generate_keypair(self, key_type: KeyType, rsa_pk8_filepath: Option<&str>) -> Self {
107 if rsa_pk8_filepath.is_none() && key_type == KeyType::RSA {
108 panic!("RSA keypair specified without a .pk8 file");
109 }
110
111 let keypair = match key_type {
112 KeyType::Ed25519 => Keypair::generate_ed25519(),
114 KeyType::RSA => {
115 let mut bytes = std::fs::read(rsa_pk8_filepath.unwrap()).unwrap_or_default();
116 Keypair::rsa_from_pkcs8(&mut bytes).unwrap()
118 },
119 KeyType::Secp256k1 => Keypair::generate_secp256k1(),
120 KeyType::Ecdsa => Keypair::generate_ecdsa(),
121 };
122
123 BootstrapConfig { keypair, ..self }
124 }
125
126 pub fn generate_keypair_from_protobuf(self, key_type_str: &str, bytes: &mut [u8]) -> Self {
138 if let Some(key_type) = <KeyType as CustomFrom>::from(key_type_str) {
140 let raw_keypair = Keypair::from_protobuf_encoding(bytes)
141 .expect("Invalid keypair: protobuf bytes not parsable into keypair");
142
143 let keypair = match key_type {
144 KeyType::Ed25519 => Keypair::try_into_ed25519(raw_keypair).unwrap().into(),
146 KeyType::RSA => Keypair::rsa_from_pkcs8(bytes).unwrap(),
148 KeyType::Secp256k1 => Keypair::try_into_secp256k1(raw_keypair).unwrap().into(),
150 KeyType::Ecdsa => Keypair::try_into_ecdsa(raw_keypair).unwrap().into(),
152 };
153
154 BootstrapConfig { keypair, ..self }
155 } else {
156 BootstrapConfig {
158 keypair: Keypair::generate_ed25519(),
159 ..self
160 }
161 }
162 }
163
164 pub fn keypair(&self) -> Keypair {
166 self.keypair.clone()
167 }
168
169 pub fn ports(&self) -> (Port, Port) {
171 (self.tcp_port, self.udp_port)
172 }
173
174 pub fn bootnodes(&self) -> Nodes {
176 self.boot_nodes.clone()
177 }
178
179 pub fn blacklist(&self) -> Blacklist {
181 self.blacklist.clone()
182 }
183}
184
185impl Default for BootstrapConfig {
187 fn default() -> Self {
188 Self::new()
189 }
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use std::collections::HashMap;
196 use std::fs;
197 use std::panic;
198 use std::process::Command;
199
200 fn generate_rsa_keypair_files() {
203 let genrsa_output = Command::new("openssl")
205 .args(&["genrsa", "-out", "private.pem", "2048"])
206 .output()
207 .expect("Failed to execute openssl genrsa command");
208
209 let pkcs8_output = Command::new("openssl")
211 .args(&[
212 "pkcs8",
213 "-in",
214 "private.pem",
215 "-inform",
216 "PEM",
217 "-topk8",
218 "-out",
219 "private.pk8",
220 "-outform",
221 "DER",
222 "-nocrypt",
223 ])
224 .output()
225 .expect("Failed to execute openssl pkcs8 command");
226
227 if genrsa_output.status.success() {
229 println!("RSA private key generated successfully");
230 } else {
231 eprintln!(
232 "Failed to generate RSA private key:\n{}",
233 String::from_utf8_lossy(&genrsa_output.stderr)
234 );
235 }
236
237 if pkcs8_output.status.success() {
238 println!("RSA private key converted to PKCS8 format successfully");
239 } else {
240 eprintln!(
241 "Failed to convert RSA private key to PKCS8 format:\n{}",
242 String::from_utf8_lossy(&pkcs8_output.stderr)
243 );
244 }
245 }
246
247 #[test]
248 fn file_read_should_panic() {
249 let result = panic::catch_unwind(|| {
250 BootstrapConfig::from_file("non_existent_file.ini");
251 });
252 assert!(result.is_err());
253 }
254
255 #[test]
256 fn default_config_works() {
257 let bootstrap_config = BootstrapConfig::default();
258
259 assert_eq!(bootstrap_config.tcp_port, MIN_PORT);
261 assert_eq!(bootstrap_config.udp_port, MAX_PORT);
262
263 let keypair = bootstrap_config.keypair;
265 assert_eq!(keypair.key_type(), KeyType::Ed25519);
266
267 assert_eq!(bootstrap_config.boot_nodes, HashMap::new());
269 }
270
271 #[test]
272 fn new_config_with_bootnodes_works() {
273 let mut bootnodes: Nodes = HashMap::new();
275 let key_1 = "12D3KooWBmwXN3rsVfnLsZKbXeBrSLfczHxZHwVjPrbKwpLfYm3t".to_string();
276 let val_1 = "/ip4/192.168.1.205/tcp/1509".to_string();
277 let key_2 = "12A0ZooWBmwXN3rsVfnLsZKbXeBrSLfczHxZHwVjPrbKwpLfYm3t".to_string();
278 let val_2 = "/ip4/192.168.1.205/tcp/1588".to_string();
279 bootnodes.insert(key_1.clone(), val_1.clone());
280 bootnodes.insert(key_2.clone(), val_2.clone());
281
282 let bootstrap_config = BootstrapConfig::new().with_bootnodes(bootnodes);
284 assert_eq!(bootstrap_config.bootnodes().len(), 2);
285
286 let bootnodes = bootstrap_config.bootnodes();
288 assert_eq!(bootnodes.get_key_value(&key_1), Some((&key_1, &val_1)));
289 assert_eq!(bootnodes.get_key_value(&key_2), Some((&key_2, &val_2)));
290 }
291
292 #[test]
293 fn new_config_with_tcp_port_works() {
294 let bootstrap_config = BootstrapConfig::default();
296 assert_eq!(bootstrap_config.ports().0, MIN_PORT);
297
298 let bootstrap_config_with_tcp = bootstrap_config.with_tcp(49666);
300 assert_eq!(bootstrap_config_with_tcp.ports().0, 49666);
301
302 let bootstrap_config_invalid_tcp_port = BootstrapConfig::new().with_tcp(MIN_PORT - 42);
305
306 assert_eq!(bootstrap_config_invalid_tcp_port.ports().0, MIN_PORT);
308 }
309
310 #[test]
311 fn new_config_with_udp_port_works() {
312 let bootstrap_config = BootstrapConfig::default();
314 assert_eq!(bootstrap_config.ports().1, MAX_PORT);
315
316 let bootstrap_config_with_udp = bootstrap_config.with_udp(55555);
318 assert_eq!(bootstrap_config_with_udp.ports().1, 55555);
319
320 let bootstrap_config_invalid_udp_port = BootstrapConfig::new().with_udp(MIN_PORT - 42);
322 assert_eq!(bootstrap_config_invalid_udp_port.ports().1, MAX_PORT);
323 }
324
325 #[test]
326 fn key_type_is_invalid() {
327 let invalid_keytype = "SomeMagicCryptoType";
329
330 let mut ed25519_serialized_keypair =
332 Keypair::generate_ed25519().to_protobuf_encoding().unwrap();
333
334 let result = panic::catch_unwind(move || {
336 let bootstrap_config = BootstrapConfig::default()
337 .generate_keypair_from_protobuf(invalid_keytype, &mut ed25519_serialized_keypair);
338
339 assert_eq!(bootstrap_config.keypair().key_type(), KeyType::Ed25519);
340 });
341
342 assert!(result.is_ok());
343 }
344
345 #[test]
346 #[should_panic(expected = "Invalid keypair: protobuf bytes not parsable into keypair")]
347 fn key_pair_is_invalid() {
348 let valid_key_types = ["Ed25519", "RSA", "Secp256k1", "Ecdsa"];
349 let mut invalid_keypair: [u8; 2] = [0; 2];
350
351 let _ = BootstrapConfig::default()
353 .generate_keypair_from_protobuf(valid_key_types[0], &mut invalid_keypair);
354 let _ = BootstrapConfig::default()
355 .generate_keypair_from_protobuf(valid_key_types[1], &mut invalid_keypair);
356 let _ = BootstrapConfig::default()
357 .generate_keypair_from_protobuf(valid_key_types[2], &mut invalid_keypair);
358 let _ = BootstrapConfig::default()
359 .generate_keypair_from_protobuf(valid_key_types[3], &mut invalid_keypair);
360 }
361
362 #[test]
363 #[should_panic(expected = "RSA keypair specified without a .pk8 file")]
364 fn rsa_specified_without_filepath_panics() {
365 let bootstrap_config = BootstrapConfig::default();
366 let _ = bootstrap_config.generate_keypair(KeyType::RSA, None);
367 }
368
369 #[test]
370 #[should_panic]
371 fn rsa_specified_with_nonexistant_file() {
372 let bootstrap_config = BootstrapConfig::default();
373 let _ = bootstrap_config.generate_keypair(KeyType::RSA, Some("invalid_rsa_file.pk8"));
374 }
375
376 #[test]
377 fn rsa_with_invalid_contents_should_panic() {
378 let file_path = "invalid_rsa_keypair_temp_file.pk8";
380 let invalid_keypair: [u8; 64] = [0; 64];
381 std::fs::write(file_path, invalid_keypair).unwrap();
382
383 let result = panic::catch_unwind(|| {
384 let _ = BootstrapConfig::default().generate_keypair(KeyType::RSA, Some(file_path));
386 });
387
388 assert!(result.is_err());
390
391 fs::remove_file(file_path).unwrap_or_default();
393 }
394
395 #[test]
396 fn rsa_from_valid_file_works() {
397 generate_rsa_keypair_files();
399
400 let bootstrap_config =
401 BootstrapConfig::new().generate_keypair(KeyType::RSA, Some("private.pk8"));
402
403 assert_eq!(bootstrap_config.keypair().key_type(), KeyType::RSA);
404
405 fs::remove_file("private.pk8").unwrap_or_default();
407 fs::remove_file("private.pem").unwrap_or_default();
408 }
409
410 #[test]
411 fn generate_keypair_from_protobuf_ed25519_works() {
412 let key_type_str = "Ed25519";
414 let mut ed25519_serialized_keypair =
415 Keypair::generate_ed25519().to_protobuf_encoding().unwrap();
416
417 let bootstrap_config = BootstrapConfig::new()
419 .generate_keypair_from_protobuf(key_type_str, &mut ed25519_serialized_keypair);
420
421 assert_eq!(bootstrap_config.keypair().key_type(), KeyType::Ed25519);
422 }
423
424 #[test]
425 fn generate_keypair_from_protobuf_ecdsa_works() {
426 let key_type_str = "Ecdsa";
428 let mut ecdsa_serialized_keypair =
429 Keypair::generate_ecdsa().to_protobuf_encoding().unwrap();
430
431 let bootstrap_config = BootstrapConfig::new()
433 .generate_keypair_from_protobuf(key_type_str, &mut ecdsa_serialized_keypair);
434
435 assert_eq!(bootstrap_config.keypair().key_type(), KeyType::Ecdsa);
436 }
437
438 #[test]
439 fn generate_keypair_from_protobuf_secp256k1_works() {
440 let key_type_str = "Secp256k1";
442 let mut secp256k1_serialized_keypair = Keypair::generate_secp256k1()
443 .to_protobuf_encoding()
444 .unwrap();
445
446 let bootstrap_config = BootstrapConfig::new()
448 .generate_keypair_from_protobuf(key_type_str, &mut secp256k1_serialized_keypair);
449
450 assert_eq!(bootstrap_config.keypair().key_type(), KeyType::Secp256k1);
451 }
452}