tests.rs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  1. /* This file is part of DarkFi (https://dark.fi)
  2. *
  3. * Copyright (C) 2020-2026 Dyne.org foundation
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU Affero General Public License as
  7. * published by the Free Software Foundation, either version 3 of the
  8. * License, or (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU Affero General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. */
  18. // cargo test --release --features=net --lib p2p -- --include-ignored
  19. use std::{
  20. collections::{HashMap, HashSet},
  21. net::TcpListener,
  22. panic,
  23. sync::Arc,
  24. };
  25. use darkfi_serial::{async_trait, SerialDecodable, SerialEncodable};
  26. use rand::{prelude::SliceRandom, rngs::ThreadRng, Rng};
  27. use smol::{channel, future, Executor};
  28. use tracing::{error, info, warn};
  29. use url::Url;
  30. use crate::{
  31. net::{
  32. hosts::HostColor,
  33. message::{GetAddrsMessage, Message},
  34. metering::{MeteringConfiguration, DEFAULT_METERING_CONFIGURATION},
  35. settings::NetworkProfile,
  36. P2p, Settings,
  37. },
  38. system::sleep,
  39. util::logger::{setup_test_logger, Level},
  40. };
  41. fn init_logger() {
  42. let ignored_targets = [
  43. "sled",
  44. "net::protocol_ping",
  45. "net::channel::subscribe_stop()",
  46. "net::hosts",
  47. "net::session",
  48. "net::outbound_session",
  49. "net::inbound_session",
  50. "net::message_publisher",
  51. "net::protocol_address",
  52. "net::protocol_version",
  53. "net::protocol_registry",
  54. "net::protocol_jobs_manager",
  55. "net::channel::send()",
  56. "net::channel::start()",
  57. "net::channel::handle_stop()",
  58. "net::channel::subscribe_msg()",
  59. "net::channel::main_receive_loop()",
  60. "net::tcp",
  61. ];
  62. // We check this error so we can execute same file tests in parallel,
  63. // otherwise second one fails to init logger here.
  64. if setup_test_logger(
  65. &ignored_targets,
  66. false,
  67. //Level::Info,
  68. Level::Verbose,
  69. //Level::Debug,
  70. //Level::Trace,
  71. )
  72. .is_err()
  73. {
  74. warn!(target: "test_harness", "Logger already initialized");
  75. }
  76. }
  77. fn get_random_available_port() -> usize {
  78. let listener = TcpListener::bind("127.0.0.1:0").unwrap();
  79. let port = listener.local_addr().unwrap().port();
  80. drop(listener);
  81. port.into()
  82. }
  83. fn get_unique_ports(n_nodes: usize) -> Vec<usize> {
  84. let mut ports = HashSet::new();
  85. while ports.len() < n_nodes {
  86. ports.insert(get_random_available_port());
  87. }
  88. ports.into_iter().collect()
  89. }
  90. async fn spawn_seed_session(
  91. seed_addr: Url,
  92. ex: Arc<Executor<'static>>,
  93. n_nodes: usize,
  94. ) -> Vec<Arc<P2p>> {
  95. info!("========================================================");
  96. info!("Initializing outbound nodes...");
  97. info!("========================================================");
  98. let mut outbound_instances = vec![];
  99. let ports = get_unique_ports(n_nodes);
  100. let mut profiles = HashMap::new();
  101. profiles.insert(
  102. "tcp".to_string(),
  103. NetworkProfile { outbound_connect_timeout: 2, ..Default::default() },
  104. );
  105. for port in ports {
  106. let settings = Settings {
  107. localnet: true,
  108. inbound_addrs: vec![Url::parse(&format!("tcp://127.0.0.1:{port}")).unwrap()],
  109. external_addrs: vec![Url::parse(&format!("tcp://127.0.0.1:{port}")).unwrap()],
  110. outbound_connections: 2,
  111. outbound_peer_discovery_cooloff_time: 2,
  112. inbound_connections: usize::MAX,
  113. greylist_refinery_interval: 15,
  114. peers: vec![],
  115. seeds: vec![seed_addr.clone()],
  116. node_id: (port).to_string(),
  117. active_profiles: vec!["tcp".to_string()],
  118. profiles: profiles.clone(),
  119. ..Default::default()
  120. };
  121. let p2p = P2p::new(settings, ex.clone()).await.unwrap();
  122. outbound_instances.push(p2p);
  123. }
  124. outbound_instances
  125. }
  126. async fn spawn_manual_session(
  127. ex: Arc<Executor<'static>>,
  128. n_nodes: usize,
  129. n_conns: usize,
  130. ) -> Vec<Arc<P2p>> {
  131. info!("========================================================");
  132. info!("Initializing manual nodes...");
  133. info!("========================================================");
  134. let mut manual_instances = vec![];
  135. let mut rng = rand::thread_rng();
  136. let ports = get_unique_ports(n_nodes);
  137. let mut profiles = HashMap::new();
  138. profiles.insert(
  139. "tcp".to_string(),
  140. NetworkProfile { outbound_connect_timeout: 2, ..Default::default() },
  141. );
  142. for i in 0..n_nodes {
  143. let mut peer_indexes_copy: Vec<usize> = (0..n_nodes).collect();
  144. peer_indexes_copy.remove(i);
  145. let peer_indexes_to_connect: Vec<_> =
  146. peer_indexes_copy.choose_multiple(&mut rng, n_conns).collect();
  147. let mut peers = vec![];
  148. for &peer_index in peer_indexes_to_connect {
  149. let port = ports[peer_index];
  150. peers.push(Url::parse(&format!("tcp://127.0.0.1:{port}")).unwrap());
  151. }
  152. let inbound_port = ports[i];
  153. let settings = Settings {
  154. localnet: true,
  155. inbound_addrs: vec![Url::parse(&format!("tcp://127.0.0.1:{inbound_port}")).unwrap()],
  156. external_addrs: vec![Url::parse(&format!("tcp://127.0.0.1:{inbound_port}")).unwrap()],
  157. outbound_connections: 2,
  158. outbound_peer_discovery_cooloff_time: 2,
  159. inbound_connections: usize::MAX,
  160. greylist_refinery_interval: 15,
  161. peers,
  162. seeds: vec![],
  163. node_id: inbound_port.to_string(),
  164. active_profiles: vec!["tcp".to_string()],
  165. profiles: profiles.clone(),
  166. ..Default::default()
  167. };
  168. let p2p = P2p::new(settings, ex.clone()).await.unwrap();
  169. manual_instances.push(p2p);
  170. }
  171. manual_instances
  172. }
  173. async fn get_random_gold_host(
  174. outbound_instances: &[Arc<P2p>],
  175. index: usize,
  176. ) -> ((Url, u64), usize) {
  177. let random_node = &outbound_instances[index];
  178. let hosts = random_node.hosts();
  179. let external_addr = random_node.settings().read().await.external_addrs[0].clone();
  180. info!("========================================================");
  181. info!("Getting gold addr from node={external_addr}");
  182. info!("========================================================");
  183. let list = hosts.container.hostlists[HostColor::Gold as usize].read().unwrap();
  184. assert!(!list.is_empty());
  185. let position = rand::thread_rng().gen_range(0..list.len());
  186. let entry = &list[position];
  187. (entry.clone(), position)
  188. }
  189. async fn _check_random_hostlist(outbound_instances: &[Arc<P2p>], rng: &mut ThreadRng) {
  190. let mut urls = HashSet::new();
  191. let random_node = outbound_instances.choose(rng).unwrap();
  192. let external_addr = random_node.settings().read().await.external_addrs[0].clone();
  193. info!("========================================================");
  194. info!("Checking node={external_addr}");
  195. info!("========================================================");
  196. let greylist = random_node.hosts().container.fetch_all(HostColor::Grey);
  197. let whitelist = random_node.hosts().container.fetch_all(HostColor::White);
  198. let goldlist = random_node.hosts().container.fetch_all(HostColor::Gold);
  199. for (url, _) in greylist {
  200. assert!(urls.insert(url));
  201. }
  202. for (url, _) in whitelist {
  203. assert!(urls.insert(url));
  204. }
  205. for (url, _) in goldlist {
  206. assert!(urls.insert(url));
  207. }
  208. assert!(!urls.is_empty());
  209. }
  210. async fn check_all_hostlist(outbound_instances: &Vec<Arc<P2p>>) {
  211. for node in outbound_instances {
  212. let external_addr = &node.settings().read().await.external_addrs[0].clone();
  213. info!("========================================================");
  214. info!("Checking node={external_addr}");
  215. info!("========================================================");
  216. let mut urls = HashSet::new();
  217. let greylist = node.hosts().container.fetch_all(HostColor::Grey);
  218. let whitelist = node.hosts().container.fetch_all(HostColor::White);
  219. let goldlist = node.hosts().container.fetch_all(HostColor::Gold);
  220. for (url, _) in greylist {
  221. assert!(urls.insert(url));
  222. }
  223. for (url, _) in whitelist {
  224. assert!(urls.insert(url));
  225. }
  226. for (url, _) in goldlist {
  227. assert!(urls.insert(url));
  228. }
  229. assert!(!urls.is_empty());
  230. }
  231. }
  232. async fn kill_node(outbound_instances: &Vec<Arc<P2p>>, node: Url) {
  233. for p2p in outbound_instances {
  234. if p2p.settings().read().await.external_addrs[0] == node {
  235. info!("========================================================");
  236. info!("Shutting down node: {}", p2p.settings().read().await.external_addrs[0]);
  237. info!("========================================================");
  238. p2p.stop().await;
  239. }
  240. }
  241. }
  242. macro_rules! test_body {
  243. ($real_call:ident, $threads:expr) => {
  244. init_logger();
  245. let ex = Arc::new(Executor::new());
  246. let ex_ = ex.clone();
  247. let (signal, shutdown) = channel::unbounded::<()>();
  248. panic::set_hook(Box::new(|panic_info| {
  249. error!("Panic occurred: {:?}", panic_info);
  250. }));
  251. // Run a thread for each node.
  252. easy_parallel::Parallel::new()
  253. .each(0..$threads, |_| {
  254. let result = std::panic::catch_unwind(|| {
  255. let res = future::block_on(ex.run(shutdown.recv()));
  256. res
  257. });
  258. if let Err(err) = result {
  259. error!("Thread panicked: {:?}", err);
  260. }
  261. })
  262. .finish(|| {
  263. future::block_on(async {
  264. $real_call(ex_).await;
  265. drop(signal);
  266. });
  267. });
  268. };
  269. }
  270. #[test]
  271. fn p2p_test() {
  272. test_body!(p2p_test_real, 5);
  273. }
  274. async fn p2p_test_real(ex: Arc<Executor<'static>>) {
  275. // Number of nodes to spawn and number of peers each node connects to
  276. const N_NODES: usize = 5;
  277. const N_CONNS: usize = 4;
  278. // ============================================================
  279. // 1. Create a new seed node.
  280. // ============================================================
  281. let seed_port = get_random_available_port();
  282. let seed_addr = Url::parse(&format!("tcp://127.0.0.1:{seed_port}")).unwrap();
  283. let settings = Settings {
  284. localnet: true,
  285. inbound_addrs: vec![seed_addr.clone()],
  286. outbound_connections: 0,
  287. inbound_connections: usize::MAX,
  288. seeds: vec![],
  289. peers: vec![],
  290. active_profiles: vec!["tcp".to_string()],
  291. greylist_refinery_interval: 12,
  292. node_id: "seed".to_string(),
  293. ..Default::default()
  294. };
  295. let seed = P2p::new(settings, ex.clone()).await.unwrap();
  296. info!("========================================================");
  297. info!("Starting seed node on {seed_addr}");
  298. info!("========================================================");
  299. seed.clone().start().await.unwrap();
  300. // ============================================================
  301. // 2. Spawn outbound nodes that will connect to the seed node.
  302. // ============================================================
  303. let outbound_instances = spawn_seed_session(seed_addr, ex.clone(), N_NODES).await;
  304. for p2p in &outbound_instances {
  305. info!("========================================================");
  306. info!("Starting node={}", p2p.settings().read().await.external_addrs[0]);
  307. info!("========================================================");
  308. p2p.clone().start().await.unwrap();
  309. }
  310. info!("========================================================");
  311. info!("Waiting 10s for all peers to reach the seed node");
  312. info!("========================================================");
  313. sleep(10).await;
  314. // ===========================================================
  315. // 3. Assert that all nodes have shared their external addr
  316. // with the seed node.
  317. // ===========================================================
  318. let greylist = seed.hosts().container.fetch_all(HostColor::Grey);
  319. assert!(greylist.len() == N_NODES);
  320. info!("========================================================");
  321. info!("Seedsync session successful!");
  322. info!("========================================================");
  323. info!("========================================================");
  324. info!("Waiting 5s for seed node refinery to kick in...");
  325. info!("========================================================");
  326. sleep(5).await;
  327. // ===========================================================
  328. // 4. Assert that seed node has at least one whitelist entry,
  329. // indicating that the refinery process is happening correctly.
  330. // ===========================================================
  331. assert!(!seed.hosts().container.is_empty(HostColor::White));
  332. info!("========================================================");
  333. info!("Checking seed={}", seed.settings().read().await.inbound_addrs[0]);
  334. info!("========================================================");
  335. let mut urls = HashSet::new();
  336. let greylist = seed.hosts().container.fetch_all(HostColor::Grey);
  337. let whitelist = seed.hosts().container.fetch_all(HostColor::White);
  338. let goldlist = seed.hosts().container.fetch_all(HostColor::Gold);
  339. for (url, _) in greylist {
  340. info!("Found grey url: {url}");
  341. assert!(urls.insert(url));
  342. }
  343. for (url, _) in whitelist {
  344. info!("Found white url: {url}");
  345. assert!(urls.insert(url));
  346. }
  347. for (url, _) in goldlist {
  348. info!("Found gold url: {url}");
  349. assert!(urls.insert(url));
  350. }
  351. assert!(!urls.is_empty());
  352. info!("========================================================");
  353. info!("Seed node refinery operating successfully!");
  354. info!("========================================================");
  355. info!("========================================================");
  356. info!("Waiting 10s for seed refinery...");
  357. info!("========================================================");
  358. sleep(10).await;
  359. let whitelist = seed.hosts().container.fetch_all(HostColor::White);
  360. assert!(whitelist.len() >= 2);
  361. // ===========================================================
  362. // 5. Select a random peer and ensure that its hostlist is not
  363. // empty. This ensures the seed node is sharing whitelisted
  364. // nodes around the network.
  365. // ===========================================================
  366. check_all_hostlist(&outbound_instances).await;
  367. info!("========================================================");
  368. info!("Peers successfully received addrs!");
  369. info!("========================================================");
  370. info!("========================================================");
  371. info!("Waiting 5s for outbound loop to connect...");
  372. info!("========================================================");
  373. sleep(5).await;
  374. // ===========================================================
  375. // 6. Select a random gold peer from one of the nodes and kill
  376. // it.
  377. // ===========================================================
  378. info!("========================================================");
  379. info!("Selecting a random gold entry...");
  380. info!("========================================================");
  381. let random_node_index = rand::thread_rng().gen_range(0..outbound_instances.len());
  382. let ((addr, _), _) = get_random_gold_host(&outbound_instances, random_node_index).await;
  383. kill_node(&outbound_instances, addr.clone()).await;
  384. info!("========================================================");
  385. info!("Waiting for greylist downgrade sequence to occur...");
  386. info!("========================================================");
  387. // ===========================================================
  388. // 7. Verify the peer has been removed from the Gold list.
  389. // ===========================================================
  390. outbound_instances[random_node_index]
  391. .hosts()
  392. .container
  393. .contains(HostColor::Grey as usize, &addr);
  394. info!("========================================================");
  395. info!("Greylist downgrade occured successfully!");
  396. info!("========================================================");
  397. info!("========================================================");
  398. info!("Seed session successful! Shutting down seed test...");
  399. info!("========================================================");
  400. // ===========================================================
  401. // 8. Stop the P2P network
  402. // ===========================================================
  403. for p2p in outbound_instances.iter() {
  404. p2p.clone().stop().await;
  405. }
  406. seed.clone().stop().await;
  407. info!("========================================================");
  408. info!("Seed test shutdown complete! Starting manual test...");
  409. info!("========================================================");
  410. let manual_instances = spawn_manual_session(ex.clone(), N_NODES, N_CONNS).await;
  411. for p2p in &manual_instances {
  412. info!("========================================================");
  413. info!("Starting node={}", p2p.settings().read().await.external_addrs[0]);
  414. info!("========================================================");
  415. p2p.clone().start().await.unwrap();
  416. }
  417. info!("========================================================");
  418. info!("Waiting 5s for all manual peers to connect");
  419. info!("========================================================");
  420. sleep(5).await;
  421. info!("========================================================");
  422. info!("Checking manual nodes connected successfully...");
  423. info!("========================================================");
  424. for p2p in manual_instances.clone() {
  425. // We should have (N_CONNS outbound + N_CONNS inbound)
  426. // connections at this point.
  427. info!("========================================================");
  428. info!("Checking manual node={}", p2p.settings().read().await.node_id);
  429. info!("========================================================");
  430. let peers = p2p.hosts().peers();
  431. assert!(peers.len() == N_CONNS * 2);
  432. }
  433. info!("========================================================");
  434. info!("Manual session successful! Shutting down manual test...");
  435. info!("========================================================");
  436. // ===========================================================
  437. // 8. Stop the P2P network
  438. // ===========================================================
  439. for p2p in manual_instances.clone() {
  440. p2p.clone().stop().await;
  441. }
  442. }
  443. #[test]
  444. fn p2p_channel_unsupported_message_type_gets_banned() {
  445. test_body!(p2p_channel_unsupported_message_type_gets_banned_real, 2);
  446. }
  447. async fn p2p_channel_unsupported_message_type_gets_banned_real(ex: Arc<Executor<'static>>) {
  448. // Test with two nodes directly connected to each other
  449. let manual_instances = spawn_manual_session(ex.clone(), 2, 1).await;
  450. for p2p in &manual_instances {
  451. p2p.clone().start().await.unwrap();
  452. }
  453. // Let's wait for the nodes to connect to each other
  454. sleep(5).await;
  455. let node1_p2p = manual_instances[0].clone();
  456. let node2_p2p = manual_instances[1].clone();
  457. let channel = node1_p2p.hosts().channels().first().unwrap().clone();
  458. // Create a new message type
  459. #[derive(SerialEncodable, SerialDecodable)]
  460. struct CustomMessage(u32);
  461. crate::impl_p2p_message!(
  462. CustomMessage,
  463. "UnsupportedMessage",
  464. 0,
  465. 0,
  466. DEFAULT_METERING_CONFIGURATION
  467. );
  468. let instance = CustomMessage(23);
  469. channel.send(&instance).await.unwrap();
  470. sleep(1).await;
  471. // Node1 should be banned by Node2
  472. assert_eq!(node2_p2p.hosts().container.fetch_all(HostColor::Black).len(), 1);
  473. node1_p2p.stop().await;
  474. node2_p2p.stop().await;
  475. }
  476. #[test]
  477. fn p2p_channel_invalid_command_length_gets_banned() {
  478. test_body!(p2p_channel_invalid_command_length_gets_banned_real, 2);
  479. }
  480. async fn p2p_channel_invalid_command_length_gets_banned_real(ex: Arc<Executor<'static>>) {
  481. // Test with two nodes directly connected to each other
  482. let manual_instances = spawn_manual_session(ex.clone(), 2, 1).await;
  483. for p2p in &manual_instances {
  484. p2p.clone().start().await.unwrap();
  485. }
  486. // Let's wait for the nodes to connect to each other
  487. sleep(5).await;
  488. let node1_p2p = manual_instances[0].clone();
  489. let node2_p2p = manual_instances[1].clone();
  490. let channel = node1_p2p.hosts().channels().first().unwrap().clone();
  491. // Create a custom message that has invalid length command name
  492. #[derive(SerialEncodable, SerialDecodable)]
  493. struct CustomMessage(u32);
  494. // The length of COMMAND_NAME is greater than message::MAX_COMMAND_LENGTH, this one is 256
  495. const COMMAND_NAME: &str =
  496. "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
  497. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\
  498. AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
  499. crate::impl_p2p_message!(CustomMessage, &COMMAND_NAME, 0, 0, DEFAULT_METERING_CONFIGURATION);
  500. let instance = CustomMessage(23);
  501. channel.send(&instance).await.unwrap();
  502. sleep(1).await;
  503. // Node1 should be banned by Node2
  504. assert_eq!(node2_p2p.hosts().container.fetch_all(HostColor::Black).len(), 1);
  505. node1_p2p.stop().await;
  506. node2_p2p.stop().await;
  507. }
  508. #[test]
  509. fn p2p_channel_invalid_message_length_gets_banned() {
  510. test_body!(p2p_channel_invalid_message_length_gets_banned_real, 2);
  511. }
  512. async fn p2p_channel_invalid_message_length_gets_banned_real(ex: Arc<Executor<'static>>) {
  513. // Test with two nodes directly connected to each other
  514. let manual_instances = spawn_manual_session(ex.clone(), 2, 1).await;
  515. for p2p in &manual_instances {
  516. p2p.clone().start().await.unwrap();
  517. }
  518. // Let's wait for the nodes to connect to each other
  519. sleep(5).await;
  520. let node1_p2p = manual_instances[0].clone();
  521. let node2_p2p = manual_instances[1].clone();
  522. let channel = node1_p2p.hosts().channels().first().unwrap().clone();
  523. // Let's create a GetAddrsMessage that will be over the GET_ADDRS_MAX_BYTES threshold
  524. let message = GetAddrsMessage { max: 20, transports: vec!["tor".to_string(); 256] };
  525. channel.send(&message).await.unwrap();
  526. sleep(1).await;
  527. // Node1 should be banned by Node2
  528. assert_eq!(node2_p2p.hosts().container.fetch_all(HostColor::Black).len(), 1);
  529. node1_p2p.stop().await;
  530. node2_p2p.stop().await;
  531. }