diff --git a/ci/bump_version.sh b/ci/bump_version.sh index 1204b8173..de32eea3c 100755 --- a/ci/bump_version.sh +++ b/ci/bump_version.sh @@ -31,7 +31,7 @@ bump_version() { find_pattern='^version\s*=.*[^,]\s*$' replace_pattern='s/'$find_pattern'/version = "'"$version"'"/' - grep -q "$find_pattern" "$file" && sed -i "$replace_pattern" "$file" + grep -q "$find_pattern" "$file" && sed -i "" "$replace_pattern" "$file" exit_code="$?" if [[ "$exit_code" != "0" ]]; then final_exit_code=1 diff --git a/masq/src/commands/connection_status_command.rs b/masq/src/commands/connection_status_command.rs index b22ced228..4d6f1b8d8 100644 --- a/masq/src/commands/connection_status_command.rs +++ b/masq/src/commands/connection_status_command.rs @@ -25,8 +25,7 @@ const CONNECTION_STATUS_ABOUT: &str = const NOT_CONNECTED_MSG: &str = "NotConnected: No external neighbor is connected to us."; const CONNECTED_TO_NEIGHBOR_MSG: &str = "ConnectedToNeighbor: External neighbor(s) are connected to us."; -const THREE_HOPS_ROUTE_FOUND_MSG: &str = - "ThreeHopsRouteFound: You can relay data over the network."; +const ROUTE_FOUND_MSG: &str = "RouteFound: You can relay data over the network."; pub fn connection_status_subcommand() -> App<'static, 'static> { SubCommand::with_name("connection-status").about(CONNECTION_STATUS_ABOUT) @@ -42,7 +41,7 @@ impl Command for ConnectionStatusCommand { let stdout_msg = match response.stage { UiConnectionStage::NotConnected => NOT_CONNECTED_MSG, UiConnectionStage::ConnectedToNeighbor => CONNECTED_TO_NEIGHBOR_MSG, - UiConnectionStage::ThreeHopsRouteFound => THREE_HOPS_ROUTE_FOUND_MSG, + UiConnectionStage::RouteFound => ROUTE_FOUND_MSG, }; short_writeln!(context.stdout(), "\n{}\n", stdout_msg); Ok(()) @@ -109,8 +108,8 @@ mod tests { "ConnectedToNeighbor: External neighbor(s) are connected to us." ); assert_eq!( - THREE_HOPS_ROUTE_FOUND_MSG, - "ThreeHopsRouteFound: You can relay data over the network." + ROUTE_FOUND_MSG, + "RouteFound: You can relay data over the network." ) } @@ -164,11 +163,8 @@ mod tests { #[test] fn connection_status_command_happy_path_for_three_hops_route_found() { assert_on_connection_status_response( - UiConnectionStage::ThreeHopsRouteFound, - ( - "\nThreeHopsRouteFound: You can relay data over the network.\n\n", - "", - ), + UiConnectionStage::RouteFound, + ("\nRouteFound: You can relay data over the network.\n\n", ""), ); } diff --git a/masq/src/notifications/connection_change_notification.rs b/masq/src/notifications/connection_change_notification.rs index 7377282b9..7f987b4c8 100644 --- a/masq/src/notifications/connection_change_notification.rs +++ b/masq/src/notifications/connection_change_notification.rs @@ -21,7 +21,7 @@ impl ConnectionChangeNotification { UiConnectionStage::ConnectedToNeighbor => { "\nConnectedToNeighbor: Established neighborship with an external node.\n" } - UiConnectionStage::ThreeHopsRouteFound => { + UiConnectionStage::RouteFound => { "\nThreeHopsRouteFound: You can now relay data over the network.\n" } }; @@ -63,7 +63,7 @@ mod tests { let mut stdout = ByteArrayWriter::new(); let stderr = ByteArrayWriter::new(); let msg = UiConnectionChangeBroadcast { - stage: UiConnectionStage::ThreeHopsRouteFound, + stage: UiConnectionStage::RouteFound, }; let term_interface = TerminalWrapper::new(Arc::new(TerminalPassiveMock::new())); diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index 412076e0d..b0d6ae15f 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -550,7 +550,7 @@ pub struct UiPaymentThresholds { pub enum UiConnectionStage { NotConnected, ConnectedToNeighbor, - ThreeHopsRouteFound, + RouteFound, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] diff --git a/masq_lib/src/multi_config.rs b/masq_lib/src/multi_config.rs index 99062d9e6..596929090 100644 --- a/masq_lib/src/multi_config.rs +++ b/masq_lib/src/multi_config.rs @@ -88,6 +88,13 @@ impl<'a> MultiConfig<'a> { let message = format!("Invalid value: {}", &captures[2]); return ConfiguratorError::required(name, &message); } + let another_invalid_value_regex = + Regex::new("error: (.*) isn't a valid value for '--(.*?) <.*>'").expect("Bad regex"); // TODO: is this good enough? + if let Some(captures) = another_invalid_value_regex.captures(&e.message) { + let name = &captures[2]; + let message = format!("Invalid value: {}", &captures[1]); + return ConfiguratorError::required(name, &message); + } if e.message .contains("The following required arguments were not provided:") { diff --git a/masq_lib/src/shared_schema.rs b/masq_lib/src/shared_schema.rs index 2d77ea687..4bc795d98 100644 --- a/masq_lib/src/shared_schema.rs +++ b/masq_lib/src/shared_schema.rs @@ -105,6 +105,22 @@ pub const MAPPING_PROTOCOL_HELP: &str = public IP address with the --ip parameter. If the Node communicates successfully with your router, \ it will remember the protocol it used, and on its next run it will try that protocol first, unless \ you specify a different protocol on the command line."; +pub const MIN_HOPS_HELP: &str = + "The Node is a system that routes data through multiple Nodes to enhance security and privacy. \ + However, the level of anonymity and security provided depends on the number of hops specified \ + by the user. By default, the system allows the user to customize the number of hops within a \ + range of 1 to 6.\n\n\ + It's important to note that if the user selects less than 3 hops, the anonymity of their data \ + cannot be guaranteed. Here's a breakdown of the different hop counts and their implications:\n\n\ + 1. A 1-hop route allows Exit Nodes to see your requests.\n\ + 2. A 2-hop route makes it harder to associate your requests with your IP address, but it's \ + not a foolproof guarantee.\n\ + 3. The minimum number of hops required to guarantee anonymity is 3.\n\ + 4. Increasing the number of hops to 4, 5, or 6 can enhance security, but it will also \ + increase the cost and latency of the route.\n\ + If you want to specify a minimum hops count, you can do so by entering a number after the \ + '--min-hops' command. For example, '--min-hops 4' would require at least 4 hops. If you fail \ + to provide this argument, the system will default to a minimum hops count of 3."; pub const REAL_USER_HELP: &str = "The user whose identity Node will assume when dropping privileges after bootstrapping. Since Node refuses to \ run with root privilege after bootstrapping, you might want to use this if you start the Node as root, or if \ @@ -407,6 +423,17 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> { .case_insensitive(true) .help(MAPPING_PROTOCOL_HELP), ) + .arg( + Arg::with_name("min-hops") + .long("min-hops") + .value_name("MIN_HOPS") + .default_value("3") + .required(false) + .min_values(0) + .max_values(1) + .possible_values(&["1", "2", "3", "4", "5", "6"]) + .help(MIN_HOPS_HELP), + ) .arg( Arg::with_name("neighborhood-mode") .long("neighborhood-mode") @@ -764,6 +791,24 @@ mod tests { it will remember the protocol it used, and on its next run it will try that protocol first, unless \ you specify a different protocol on the command line." ); + assert_eq!( + MIN_HOPS_HELP, + "The Node is a system that routes data through multiple Nodes to enhance security and privacy. \ + However, the level of anonymity and security provided depends on the number of hops specified \ + by the user. By default, the system allows the user to customize the number of hops within a \ + range of 1 to 6.\n\n\ + It's important to note that if the user selects less than 3 hops, the anonymity of their data \ + cannot be guaranteed. Here's a breakdown of the different hop counts and their implications:\n\n\ + 1. A 1-hop route allows Exit Nodes to see your requests.\n\ + 2. A 2-hop route makes it harder to associate your requests with your IP address, but it's \ + not a foolproof guarantee.\n\ + 3. The minimum number of hops required to guarantee anonymity is 3.\n\ + 4. Increasing the number of hops to 4, 5, or 6 can enhance security, but it will also \ + increase the cost and latency of the route.\n\ + If you want to specify a minimum hops count, you can do so by entering a number after the \ + '--min-hops' command. For example, '--min-hops 4' would require at least 4 hops. If you fail \ + to provide this argument, the system will default to a minimum hops count of 3." + ); assert_eq!( REAL_USER_HELP, "The user whose identity Node will assume when dropping privileges after bootstrapping. Since Node refuses to \ diff --git a/multinode_integration_tests/src/masq_cores_client.rs b/multinode_integration_tests/src/masq_cores_client.rs index 2722b9e24..0c89953cf 100644 --- a/multinode_integration_tests/src/masq_cores_client.rs +++ b/multinode_integration_tests/src/masq_cores_client.rs @@ -18,7 +18,7 @@ impl<'a> MASQCoresClient<'a> { pub fn new(socket_addr: SocketAddr, cryptde: &'a dyn CryptDE) -> MASQCoresClient<'a> { MASQCoresClient { cryptde, - delegate: MASQNodeClient::new(socket_addr), + delegate: MASQNodeClient::new(socket_addr, 1000), } } diff --git a/multinode_integration_tests/src/masq_node_client.rs b/multinode_integration_tests/src/masq_node_client.rs index 44445dd84..6df65b68a 100644 --- a/multinode_integration_tests/src/masq_node_client.rs +++ b/multinode_integration_tests/src/masq_node_client.rs @@ -11,7 +11,7 @@ pub struct MASQNodeClient { } impl MASQNodeClient { - pub fn new(socket_addr: SocketAddr) -> MASQNodeClient { + pub fn new(socket_addr: SocketAddr, timeout_millis: u64) -> MASQNodeClient { let stream = TcpStream::connect(&socket_addr) .unwrap_or_else(|_| panic!("Connecting to {}", socket_addr)); stream @@ -20,7 +20,7 @@ impl MASQNodeClient { MASQNodeClient { stream, - timeout: Duration::from_secs(1), + timeout: Duration::from_millis(timeout_millis), } } diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 0bdeb75bd..1027c329b 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -15,12 +15,13 @@ use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::localhost; use masq_lib::utils::{DEFAULT_CONSUMING_DERIVATION_PATH, DEFAULT_EARNING_DERIVATION_PATH}; use node_lib::blockchain::bip32::Bip32ECKeyProvider; +use node_lib::neighborhood::DEFAULT_MIN_HOPS_COUNT; use node_lib::sub_lib::accountant::{ PaymentThresholds, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, }; use node_lib::sub_lib::cryptde::{CryptDE, PublicKey}; use node_lib::sub_lib::cryptde_null::CryptDENull; -use node_lib::sub_lib::neighborhood::{RatePack, DEFAULT_RATE_PACK, ZERO_RATE_PACK}; +use node_lib::sub_lib::neighborhood::{Hops, RatePack, DEFAULT_RATE_PACK, ZERO_RATE_PACK}; use node_lib::sub_lib::node_addr::NodeAddr; use node_lib::sub_lib::wallet::Wallet; use regex::Regex; @@ -36,6 +37,7 @@ use std::thread; use std::time::Duration; pub const DATA_DIRECTORY: &str = "/node_root/home"; +pub const STANDARD_CLIENT_TIMEOUT_MILLIS: u64 = 1000; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Firewall { @@ -112,6 +114,7 @@ pub fn make_consuming_wallet_info(token: &str) -> ConsumingWalletInfo { #[derive(PartialEq, Eq, Clone, Debug)] pub struct NodeStartupConfig { pub neighborhood_mode: String, + pub min_hops_count: Hops, pub ip_info: LocalIpInfo, pub dns_servers_opt: Option>, pub neighbors: Vec, @@ -143,6 +146,7 @@ impl NodeStartupConfig { pub fn new() -> NodeStartupConfig { NodeStartupConfig { neighborhood_mode: "standard".to_string(), + min_hops_count: DEFAULT_MIN_HOPS_COUNT, ip_info: LocalIpInfo::ZeroHop, dns_servers_opt: None, neighbors: Vec::new(), @@ -174,6 +178,8 @@ impl NodeStartupConfig { let mut args = vec![]; args.push("--neighborhood-mode".to_string()); args.push(self.neighborhood_mode.clone()); + args.push("--min-hops".to_string()); + args.push(format!("{}", self.min_hops_count as usize)); if let LocalIpInfo::DistributedKnown(ip_addr) = self.ip_info { args.push("--ip".to_string()); args.push(ip_addr.to_string()); @@ -403,6 +409,7 @@ impl NodeStartupConfig { pub struct NodeStartupConfigBuilder { neighborhood_mode: String, + min_hops_count: Hops, ip_info: LocalIpInfo, dns_servers_opt: Option>, neighbors: Vec, @@ -458,6 +465,7 @@ impl NodeStartupConfigBuilder { pub fn standard() -> Self { Self { neighborhood_mode: "standard".to_string(), + min_hops_count: DEFAULT_MIN_HOPS_COUNT, ip_info: LocalIpInfo::DistributedUnknown, dns_servers_opt: None, neighbors: vec![], @@ -483,6 +491,7 @@ impl NodeStartupConfigBuilder { pub fn copy(config: &NodeStartupConfig) -> Self { Self { neighborhood_mode: config.neighborhood_mode.clone(), + min_hops_count: config.min_hops_count, ip_info: config.ip_info, dns_servers_opt: config.dns_servers_opt.clone(), neighbors: config.neighbors.clone(), @@ -521,6 +530,11 @@ impl NodeStartupConfigBuilder { } } + pub fn min_hops_count(mut self, value: Hops) -> Self { + self.min_hops_count = value; + self + } + pub fn ip(mut self, value: IpAddr) -> Self { self.ip_info = LocalIpInfo::DistributedKnown(value); self @@ -634,6 +648,7 @@ impl NodeStartupConfigBuilder { pub fn build(self) -> NodeStartupConfig { NodeStartupConfig { neighborhood_mode: self.neighborhood_mode, + min_hops_count: self.min_hops_count, ip_info: self.ip_info, dns_servers_opt: self.dns_servers_opt, neighbors: self.neighbors, @@ -926,9 +941,9 @@ impl MASQRealNode { } } - pub fn make_client(&self, port: u16) -> MASQNodeClient { + pub fn make_client(&self, port: u16, timeout_millis: u64) -> MASQNodeClient { let socket_addr = SocketAddr::new(self.ip_address(), port); - MASQNodeClient::new(socket_addr) + MASQNodeClient::new(socket_addr, timeout_millis) } pub fn make_server(&self, port: u16) -> MASQNodeServer { @@ -1226,6 +1241,7 @@ mod tests { use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::localhost; + use node_lib::sub_lib::neighborhood::Hops::TwoHops; #[test] fn node_startup_config_builder_zero_hop() { @@ -1289,6 +1305,7 @@ mod tests { #[test] fn node_startup_config_builder_settings() { + let min_hops_count = Hops::SixHops; let ip_addr = IpAddr::from_str("1.2.3.4").unwrap(); let one_neighbor_key = PublicKey::new(&[1, 2, 3, 4]); let one_neighbor_ip_addr = IpAddr::from_str("4.5.6.7").unwrap(); @@ -1317,6 +1334,7 @@ mod tests { let dns_target = IpAddr::from_str("8.9.10.11").unwrap(); let result = NodeStartupConfigBuilder::standard() + .min_hops_count(min_hops_count) .ip(ip_addr) .dns_servers(dns_servers.clone()) .neighbor(neighbors[0].clone()) @@ -1325,6 +1343,7 @@ mod tests { .dns_port(35) .build(); + assert_eq!(result.min_hops_count, min_hops_count); assert_eq!(result.ip_info, LocalIpInfo::DistributedKnown(ip_addr)); assert_eq!(result.dns_servers_opt, Some(dns_servers)); assert_eq!(result.neighbors, neighbors); @@ -1337,6 +1356,7 @@ mod tests { fn node_startup_config_builder_copy() { let original = NodeStartupConfig { neighborhood_mode: "consume-only".to_string(), + min_hops_count: TwoHops, ip_info: LocalIpInfo::DistributedUnknown, dns_servers_opt: Some(vec![IpAddr::from_str("255.255.255.255").unwrap()]), neighbors: vec![NodeReference::new( @@ -1415,6 +1435,7 @@ mod tests { .build(); assert_eq!(result.neighborhood_mode, neighborhood_mode); + assert_eq!(result.min_hops_count, Hops::TwoHops); assert_eq!(result.ip_info, LocalIpInfo::DistributedKnown(ip_addr)); assert_eq!(result.dns_servers_opt, Some(dns_servers)); assert_eq!(result.neighbors, neighbors); @@ -1481,6 +1502,7 @@ mod tests { let subject = NodeStartupConfigBuilder::standard() .neighborhood_mode("consume-only") + .min_hops_count(Hops::SixHops) .ip(IpAddr::from_str("1.3.5.7").unwrap()) .neighbor(one_neighbor.clone()) .neighbor(another_neighbor.clone()) @@ -1496,6 +1518,8 @@ mod tests { Command::strings(vec!( "--neighborhood-mode", "consume-only", + "--min-hops", + "6", "--ip", "1.3.5.7", "--neighbors", diff --git a/multinode_integration_tests/tests/connection_progress_test.rs b/multinode_integration_tests/tests/connection_progress_test.rs index 41172ba59..023ad4c88 100644 --- a/multinode_integration_tests/tests/connection_progress_test.rs +++ b/multinode_integration_tests/tests/connection_progress_test.rs @@ -56,9 +56,9 @@ fn connection_progress_is_properly_broadcast() { let message_body = ui_client.wait_for_specific_broadcast(vec!["connectionChange"], Duration::from_secs(5)); let (ccb, _) = UiConnectionChangeBroadcast::fmb(message_body).unwrap(); - assert_eq!(ccb.stage, UiConnectionStage::ThreeHopsRouteFound); + assert_eq!(ccb.stage, UiConnectionStage::RouteFound); } else { - assert_eq!(ccb.stage, UiConnectionStage::ThreeHopsRouteFound); + assert_eq!(ccb.stage, UiConnectionStage::RouteFound); } } diff --git a/multinode_integration_tests/tests/data_routing_test.rs b/multinode_integration_tests/tests/data_routing_test.rs index 6be340d4e..9b0e5a979 100644 --- a/multinode_integration_tests/tests/data_routing_test.rs +++ b/multinode_integration_tests/tests/data_routing_test.rs @@ -6,13 +6,14 @@ use multinode_integration_tests_lib::masq_node::MASQNode; use multinode_integration_tests_lib::masq_node_cluster::MASQNodeCluster; use multinode_integration_tests_lib::masq_real_node::{ default_consuming_wallet_info, make_consuming_wallet_info, MASQRealNode, - NodeStartupConfigBuilder, + NodeStartupConfigBuilder, STANDARD_CLIENT_TIMEOUT_MILLIS, }; use native_tls::HandshakeError; use native_tls::TlsConnector; use native_tls::TlsStream; use node_lib::proxy_server::protocol_pack::ServerImpersonator; use node_lib::proxy_server::server_impersonator_http::ServerImpersonatorHttp; +use node_lib::sub_lib::neighborhood::Hops; use node_lib::test_utils::{handle_connection_error, read_until_timeout}; use std::io::Write; use std::net::{IpAddr, SocketAddr, TcpStream}; @@ -57,7 +58,7 @@ fn http_end_to_end_routing_test() { thread::sleep(Duration::from_millis(500)); - let mut client = last_node.make_client(8080); + let mut client = last_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); let response = client.wait_for_chunk(); @@ -69,6 +70,50 @@ fn http_end_to_end_routing_test() { ); } +fn assert_http_end_to_end_routing_test(min_hops_count: Hops) { + let mut cluster = MASQNodeCluster::start().unwrap(); + let config = NodeStartupConfigBuilder::standard() + .min_hops_count(min_hops_count) + .chain(cluster.chain) + .consuming_wallet_info(make_consuming_wallet_info("first_node")) + .build(); + let first_node = cluster.start_real_node(config); + + let nodes_count = 2 * (min_hops_count as usize) + 1; + let nodes = (0..nodes_count) + .map(|_| { + cluster.start_real_node( + NodeStartupConfigBuilder::standard() + .neighbor(first_node.node_reference()) + .chain(cluster.chain) + .build(), + ) + }) + .collect::>(); + + thread::sleep(Duration::from_millis(500 * (nodes.len() as u64))); + + let mut client = first_node.make_client(8080, 5000); + client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); + let response = client.wait_for_chunk(); + + assert_eq!( + index_of(&response, &b"

Example Domain

"[..]).is_some(), + true, + "Actual response:\n{}", + String::from_utf8(response).unwrap() + ); +} + +#[test] +fn http_end_to_end_routing_test_with_different_min_hops_count() { + // This test fails sometimes due to a timeout: "Couldn't read chunk: Kind(TimedOut)" + // You may fix it by increasing the timeout for the client. + assert_http_end_to_end_routing_test(Hops::OneHop); + assert_http_end_to_end_routing_test(Hops::TwoHops); + assert_http_end_to_end_routing_test(Hops::SixHops); +} + #[test] fn http_end_to_end_routing_test_with_consume_and_originate_only_nodes() { let mut cluster = MASQNodeCluster::start().unwrap(); @@ -103,7 +148,7 @@ fn http_end_to_end_routing_test_with_consume_and_originate_only_nodes() { thread::sleep(Duration::from_millis(1000)); - let mut client = originating_node.make_client(8080); + let mut client = originating_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); let response = client.wait_for_chunk(); @@ -233,7 +278,7 @@ fn http_routing_failure_produces_internal_error_response() { ); thread::sleep(Duration::from_millis(1000)); - let mut client = originating_node.make_client(8080); + let mut client = originating_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); let response = client.wait_for_chunk(); @@ -265,7 +310,7 @@ fn tls_routing_failure_produces_internal_error_response() { .chain(cluster.chain) .build(), ); - let mut client = originating_node.make_client(8443); + let mut client = originating_node.make_client(8443, STANDARD_CLIENT_TIMEOUT_MILLIS); let client_hello = vec![ 0x16, // content_type: Handshake 0x03, 0x03, // TLS 1.2 @@ -312,8 +357,8 @@ fn multiple_stream_zero_hop_test() { .chain(cluster.chain) .build(), ); - let mut one_client = zero_hop_node.make_client(8080); - let mut another_client = zero_hop_node.make_client(8080); + let mut one_client = zero_hop_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); + let mut another_client = zero_hop_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS); one_client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); another_client.send_chunk(b"GET /online/ HTTP/1.1\r\nHost: whatever.neverssl.com\r\n\r\n"); diff --git a/node/src/actor_system_factory.rs b/node/src/actor_system_factory.rs index f03f57df2..537a0b3a5 100644 --- a/node/src/actor_system_factory.rs +++ b/node/src/actor_system_factory.rs @@ -630,6 +630,7 @@ mod tests { use crate::sub_lib::ui_gateway::UiGatewayConfig; use crate::test_utils::automap_mocks::{AutomapControlFactoryMock, AutomapControlMock}; use crate::test_utils::make_wallet; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_COUNT_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::{ make_accountant_subs_from_recorder, make_blockchain_bridge_subs_from, @@ -1070,6 +1071,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, payment_thresholds_opt: Some(PaymentThresholds::default()), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, @@ -1143,6 +1145,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, payment_thresholds_opt: Default::default(), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC @@ -1286,6 +1289,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let make_params_arc = Arc::new(Mutex::new(vec![])); let mut subject = make_subject_with_null_setter(); @@ -1439,6 +1443,7 @@ mod tests { real_user: RealUser::null(), neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![]), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, payment_thresholds_opt: Default::default(), when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC @@ -1627,6 +1632,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, node_descriptor: Default::default(), payment_thresholds_opt: Default::default(), diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 2103cdf04..31ff25f1c 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -15,6 +15,7 @@ use crate::json_discriminator_factory::JsonDiscriminatorFactory; use crate::listener_handler::ListenerHandler; use crate::listener_handler::ListenerHandlerFactory; use crate::listener_handler::ListenerHandlerFactoryReal; +use crate::neighborhood::DEFAULT_MIN_HOPS_COUNT; use crate::node_configurator::node_configurator_standard::{ NodeConfiguratorStandardPrivileged, NodeConfiguratorStandardUnprivileged, }; @@ -395,6 +396,7 @@ impl BootstrapperConfig { consuming_wallet_opt: None, neighborhood_config: NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: DEFAULT_MIN_HOPS_COUNT, }, when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC, } @@ -647,13 +649,11 @@ impl Bootstrapper { ) .expect("Failed to bind ListenerHandler to clandestine port"); self.listener_handlers.push(listener_handler); - self.config.neighborhood_config = NeighborhoodConfig { - mode: NeighborhoodMode::Standard( - NodeAddr::new(&node_addr.ip_addr(), &[clandestine_port]), - neighbor_configs.clone(), - *rate_pack, - ), - }; + self.config.neighborhood_config.mode = NeighborhoodMode::Standard( + NodeAddr::new(&node_addr.ip_addr(), &[clandestine_port]), + neighbor_configs.clone(), + *rate_pack, + ); Some(clandestine_port) } else { None @@ -732,6 +732,7 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::socket_server::ConfiguredByPrivilege; use crate::sub_lib::stream_connector::ConnectionInfo; + use crate::test_utils::neighborhood_test_utils::MIN_HOPS_COUNT_FOR_TEST; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::RecordAwaiter; @@ -1222,6 +1223,7 @@ mod tests { let clandestine_port_opt = Some(44444); let neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::OriginateOnly(vec![], rate_pack(9)), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let earning_wallet = make_wallet("earning wallet"); let consuming_wallet_opt = Some(make_wallet("consuming wallet")); @@ -1822,6 +1824,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; config.data_directory = data_dir.clone(); config.clandestine_port_opt = Some(port); @@ -1891,6 +1894,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; config.data_directory = data_dir.clone(); config.clandestine_port_opt = None; @@ -1939,6 +1943,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() @@ -1975,6 +1980,7 @@ mod tests { Chain::EthRopsten, cryptde, ))]), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() @@ -2004,6 +2010,7 @@ mod tests { config.clandestine_port_opt = None; config.neighborhood_config = NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let listener_handler = ListenerHandlerNull::new(vec![]); let mut subject = BootstrapperBuilder::new() diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index c1ee05c65..8a753bf05 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -814,6 +814,13 @@ impl ValueRetriever for MappingProtocol { } } +struct MinHops {} +impl ValueRetriever for MinHops { + fn value_name(&self) -> &'static str { + "min-hops" + } +} + struct NeighborhoodMode {} impl ValueRetriever for NeighborhoodMode { fn value_name(&self) -> &'static str { @@ -1040,6 +1047,7 @@ fn value_retrievers(dirs_wrapper: &dyn DirsWrapper) -> Vec, gossip_source: SocketAddr, - connection_progress_peers: &[IpAddr], - cpm_recipient: &Recipient, + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult; } @@ -133,8 +130,7 @@ impl GossipHandler for DebutHandler { database: &mut NeighborhoodDatabase, mut agrs: Vec, gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], - _cpm_recipient: &Recipient, + _neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let source_agr = { let mut agr = agrs.remove(0); // empty Gossip shouldn't get here @@ -510,8 +506,7 @@ impl GossipHandler for PassHandler { database: &mut NeighborhoodDatabase, agrs: Vec, _gossip_source: SocketAddr, - connection_progress_peers: &[IpAddr], - cpm_recipient: &Recipient, + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let pass_agr = &agrs[0]; // empty Gossip shouldn't get here let pass_target_node_addr: NodeAddr = pass_agr @@ -524,7 +519,8 @@ impl GossipHandler for PassHandler { peer_addr: _gossip_source.ip(), event, }; - cpm_recipient + neighborhood_metadata + .cpm_recipient .try_send(connection_progress_message) .expect("System is dead."); }; @@ -541,7 +537,10 @@ impl GossipHandler for PassHandler { let mut hash_map = self.previous_pass_targets.borrow_mut(); let gossip_acceptance_result = match hash_map.get_mut(&pass_target_ip_addr) { - None => match connection_progress_peers.contains(&pass_target_ip_addr) { + None => match neighborhood_metadata + .connection_progress_peers + .contains(&pass_target_ip_addr) + { true => { send_cpm(ConnectionProgressEvent::PassLoopFound); GossipAcceptanceResult::Ignored @@ -628,8 +627,7 @@ impl GossipHandler for IntroductionHandler { database: &mut NeighborhoodDatabase, agrs: Vec, gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], - cpm_recipient: &Recipient, + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { if database.root().full_neighbor_keys(database).len() >= MAX_DEGREE { GossipAcceptanceResult::Ignored @@ -660,7 +658,8 @@ impl GossipHandler for IntroductionHandler { peer_addr: introducer_ip_addr, event: ConnectionProgressEvent::IntroductionGossipReceived(introducee_ip_addr), }; - cpm_recipient + neighborhood_metadata + .cpm_recipient .try_send(connection_progess_message) .expect("Neighborhood is dead"); let (debut, target_key, target_node_addr) = @@ -933,13 +932,16 @@ impl GossipHandler for StandardGossipHandler { database: &mut NeighborhoodDatabase, agrs: Vec, gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], - cpm_recipient: &Recipient, + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); - let patch = self.compute_patch(&agrs, database.root()); + let patch = self.compute_patch( + &agrs, + database.root(), + neighborhood_metadata.min_hops_count as u8, + ); let filtered_agrs = self.filter_agrs_by_patch(agrs, patch); let mut db_changed = self.identify_and_add_non_introductory_new_nodes( @@ -962,7 +964,8 @@ impl GossipHandler for StandardGossipHandler { peer_addr: gossip_source.ip(), event: ConnectionProgressEvent::StandardGossipReceived, }; - cpm_recipient + neighborhood_metadata + .cpm_recipient .try_send(cpm) .unwrap_or_else(|e| panic!("Neighborhood is dead: {}", e)); } @@ -986,6 +989,7 @@ impl StandardGossipHandler { &self, agrs: &[AccessibleGossipRecord], root_node: &NodeRecord, + min_hops_count: u8, ) -> HashSet { let agrs_by_key = agrs .iter() @@ -997,7 +1001,7 @@ impl StandardGossipHandler { &mut patch, root_node.public_key(), &agrs_by_key, - DEFAULT_MINIMUM_HOP_COUNT, + min_hops_count, root_node, ); @@ -1009,7 +1013,7 @@ impl StandardGossipHandler { patch: &mut HashSet, current_node_key: &PublicKey, agrs: &HashMap<&PublicKey, &AccessibleGossipRecord>, - hops_remaining: usize, + hops_remaining: u8, root_node: &NodeRecord, ) { patch.insert(current_node_key.clone()); @@ -1209,8 +1213,7 @@ impl GossipHandler for RejectHandler { _database: &mut NeighborhoodDatabase, _agrs: Vec, _gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], - _cpm_recipient: &Recipient, + _neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { panic!("Should never be called") } @@ -1228,7 +1231,7 @@ pub trait GossipAcceptor: Send /* Send because lazily-written tests require it * database: &mut NeighborhoodDatabase, agrs: Vec, gossip_source: SocketAddr, - connection_progress_peers: &[IpAddr], + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult; } @@ -1236,7 +1239,6 @@ pub struct GossipAcceptorReal<'a> { cryptde: &'a dyn CryptDE, gossip_handlers: Vec>, logger: Logger, - cpm_recipient: Recipient, } impl<'a> GossipAcceptor for GossipAcceptorReal<'a> { @@ -1245,7 +1247,7 @@ impl<'a> GossipAcceptor for GossipAcceptorReal<'a> { database: &mut NeighborhoodDatabase, agrs: Vec, gossip_source: SocketAddr, - connection_progress_peers: &[IpAddr], + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let (qualification, handler_ref) = self .gossip_handlers @@ -1265,8 +1267,7 @@ impl<'a> GossipAcceptor for GossipAcceptorReal<'a> { database, agrs, gossip_source, - connection_progress_peers, - &self.cpm_recipient, + neighborhood_metadata, ) } Qualification::Unmatched => { @@ -1278,10 +1279,7 @@ impl<'a> GossipAcceptor for GossipAcceptorReal<'a> { } impl<'a> GossipAcceptorReal<'a> { - pub fn new( - cryptde: &'a dyn CryptDE, - cpm_recipient: Recipient, - ) -> GossipAcceptorReal { + pub fn new(cryptde: &'a dyn CryptDE) -> GossipAcceptorReal { let logger = Logger::new("GossipAcceptor"); GossipAcceptorReal { gossip_handlers: vec![ @@ -1293,7 +1291,6 @@ impl<'a> GossipAcceptorReal<'a> { ], cryptde, logger, - cpm_recipient, } } @@ -1337,15 +1334,16 @@ mod tests { use crate::neighborhood::gossip_producer::GossipProducerReal; use crate::neighborhood::node_record::NodeRecord; use crate::sub_lib::cryptde_null::CryptDENull; - use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage}; + use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, Hops}; use crate::sub_lib::utils::time_t_timestamp; use crate::test_utils::neighborhood_test_utils::{ - db_from_node, make_meaningless_db, make_node_record, make_node_record_f, + db_from_node, gossip_about_nodes_from_database, linearly_connect_nodes, + make_meaningless_db, make_node_record, make_node_record_f, make_node_records, + public_keys_from_node_records, MIN_HOPS_COUNT_FOR_TEST, }; - use crate::test_utils::recorder::make_recorder; use crate::test_utils::unshared_test_utils::make_cpm_recipient; use crate::test_utils::{assert_contains, main_cryptde, vec_to_set}; - use actix::{Actor, System}; + use actix::System; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use std::convert::TryInto; @@ -1367,6 +1365,14 @@ mod tests { // ZeroHop is decentralized and should never appear in GossipAcceptor tests } + fn make_default_neighborhood_metadata() -> NeighborhoodMetadata { + NeighborhoodMetadata { + connection_progress_peers: vec![], + cpm_recipient: make_cpm_recipient().0, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, + } + } + #[test] fn proper_debut_of_accepting_node_with_populated_database_is_identified_and_handled() { let (gossip, new_node, gossip_source_opt) = make_debut(2345, Mode::Standard); @@ -1376,7 +1382,6 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_key); let cryptde = CryptDENull::from(db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = DebutHandler::new(Logger::new("test")); let qualifies_result = @@ -1386,8 +1391,7 @@ mod tests { &mut db, agrs_vec, gossip_source_opt, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -1414,7 +1418,6 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_key); let cryptde = CryptDENull::from(db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = DebutHandler::new(Logger::new("test")); let qualifies_result = subject.qualifies(&db, agrs_vec.as_slice(), gossip_source.clone()); @@ -1423,8 +1426,7 @@ mod tests { &mut db, agrs_vec, gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -1470,7 +1472,6 @@ mod tests { .node(src_db.root().public_key(), true) .build(); let agrs_vec: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = DebutHandler::new(Logger::new("test")); let result = subject.handle( @@ -1478,8 +1479,7 @@ mod tests { &mut dest_db, agrs_vec, src_root.node_addr_opt().unwrap().into(), - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(result, GossipAcceptanceResult::Accepted); @@ -1504,7 +1504,6 @@ mod tests { .node(src_db.root().public_key(), true) .build(); let agrs_vec: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = DebutHandler::new(Logger::new("test")); let result = subject.handle( @@ -1512,8 +1511,7 @@ mod tests { &mut dest_db, agrs_vec, src_root.node_addr_opt().unwrap().into(), - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!( @@ -1618,7 +1616,6 @@ mod tests { .build() .try_into() .unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = DebutHandler::new(Logger::new("test")); let result = subject.handle( @@ -1626,8 +1623,7 @@ mod tests { &mut dest_db, agrs_vec, dest_root.node_addr_opt().clone().unwrap().into(), - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(result, GossipAcceptanceResult::Accepted); @@ -1640,7 +1636,6 @@ mod tests { let mut dest_db = make_meaningless_db(); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let qualifies_result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); let handle_result = subject.handle( @@ -1648,8 +1643,7 @@ mod tests { &mut dest_db, agrs_vec, gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -1897,7 +1891,6 @@ mod tests { &SocketAddr::from_str("4.5.6.7:4567").unwrap(), )); dest_db.resign_node(introducer_key); - let (cpm_recipient, _) = make_cpm_recipient(); let introducer_before_gossip = dest_db.node_by_key(introducer_key).unwrap().clone(); let before = time_t_timestamp(); @@ -1907,8 +1900,7 @@ mod tests { &mut dest_db, agrs.clone(), gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -1946,7 +1938,6 @@ mod tests { let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let subject = IntroductionHandler::new(Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let qualifies_result = subject.qualifies(&dest_db, &agrs, gossip_source); let handle_result = subject.handle( @@ -1954,8 +1945,7 @@ mod tests { &mut dest_db, agrs.clone(), gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -1997,15 +1987,13 @@ mod tests { let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let subject = IntroductionHandler::new(Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); - let (cpm_recipient, _) = make_cpm_recipient(); let handle_result = subject.handle( &cryptde, &mut dest_db, agrs.clone(), gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(handle_result, GossipAcceptanceResult::Ignored); @@ -2026,7 +2014,6 @@ mod tests { .unwrap() .set_version(0); dest_db.resign_node(&agrs[0].inner.public_key); - let (cpm_recipient, _) = make_cpm_recipient(); let qualifies_result = subject.qualifies(&dest_db, &agrs, gossip_source); let handle_result = subject.handle( @@ -2034,8 +2021,7 @@ mod tests { &mut dest_db, agrs.clone(), gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -2083,7 +2069,6 @@ mod tests { let agrs: Vec = gossip.try_into().unwrap(); dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), &agrs[0].inner.public_key); - let (cpm_recipient, _) = make_cpm_recipient(); let qualifies_result = subject.qualifies(&dest_db, &agrs, gossip_source); let handle_result = subject.handle( @@ -2091,8 +2076,7 @@ mod tests { &mut dest_db, agrs.clone(), gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(Qualification::Matched, qualifies_result); @@ -2335,6 +2319,8 @@ mod tests { let agrs_vec: Vec = gossip.try_into().unwrap(); let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let system = System::new("test"); let qualifies_result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2343,8 +2329,7 @@ mod tests { &mut dest_db, agrs_vec, gossip_source, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); assert_eq!(Qualification::Matched, qualifies_result); @@ -2400,7 +2385,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let result = subject.compute_patch(&agrs, node_a_db.root()); + let result = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2452,7 +2437,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let patch = subject.compute_patch(&agrs, node_a_db.root()); + let patch = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2501,7 +2486,7 @@ mod tests { .build(); let agrs: Vec = gossip.try_into().unwrap(); - let patch = subject.compute_patch(&agrs, node_a_db.root()); + let patch = subject.compute_patch(&agrs, node_a_db.root(), MIN_HOPS_COUNT_FOR_TEST as u8); let expected_hashset = vec![ node_a.public_key().clone(), @@ -2535,7 +2520,6 @@ mod tests { */ let cryptde = main_cryptde(); - let (cpm_recipient, _) = make_cpm_recipient(); let subject = StandardGossipHandler::new(Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); @@ -2578,13 +2562,40 @@ mod tests { &mut node_a_db, agrs, gossip_source, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); assert_eq!(result, GossipAcceptanceResult::Ignored); } + fn assert_compute_patch(min_hops_count: Hops) { + let subject = StandardGossipHandler::new(Logger::new("assert_compute_patch")); + // one node to finish hops and another node that's outside the patch + let nodes_count = min_hops_count as usize + 2; + let nodes = make_node_records(nodes_count as u16); + let db = linearly_connect_nodes(&nodes); + // gossip is intended for the first node (also root), thereby it's excluded + let gossip = gossip_about_nodes_from_database(&db, &nodes[1..]); + let agrs: Vec = gossip.try_into().unwrap(); + + let result = subject.compute_patch(&agrs, db.root(), min_hops_count as u8); + + // last node is excluded because it is outside the patch + let expected_nodes = &nodes[0..nodes_count - 1]; + let expected_patch = public_keys_from_node_records(&expected_nodes); + assert_eq!(result, expected_patch); + } + + #[test] + fn patch_can_be_calculated_for_different_hops() { + assert_compute_patch(Hops::OneHop); + assert_compute_patch(Hops::TwoHops); + assert_compute_patch(Hops::ThreeHops); + assert_compute_patch(Hops::FourHops); + assert_compute_patch(Hops::FiveHops); + assert_compute_patch(Hops::SixHops); + } + #[test] fn no_cpm_is_sent_in_case_full_neighborship_doesn_t_exist_and_cannot_be_created() { // Received gossip from a node we couldn't make a neighbor {Degree too high or malefactor banned node} (false, false) @@ -2604,6 +2615,8 @@ mod tests { .build(); let agrs = gossip.try_into().unwrap(); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let subject = StandardGossipHandler::new(Logger::new("test")); let system = System::new("test"); @@ -2612,8 +2625,7 @@ mod tests { &mut root_db, agrs, src_node_socket_addr, - &vec![], - &cpm_recipient, + make_default_neighborhood_metadata(), ); System::current().stop(); @@ -2643,6 +2655,8 @@ mod tests { .build(); let agrs = gossip.try_into().unwrap(); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let subject = StandardGossipHandler::new(Logger::new("test")); let system = System::new("test"); @@ -2651,8 +2665,7 @@ mod tests { &mut root_db, agrs, src_node_socket_addr, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); System::current().stop(); @@ -2689,6 +2702,8 @@ mod tests { .build(); let agrs = gossip.try_into().unwrap(); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let subject = StandardGossipHandler::new(Logger::new("test")); let system = System::new("test"); @@ -2697,8 +2712,7 @@ mod tests { &mut root_db, agrs, src_node_socket_addr, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); System::current().stop(); @@ -2729,6 +2743,8 @@ mod tests { .build(); let agrs = gossip.try_into().unwrap(); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let subject = StandardGossipHandler::new(Logger::new("test")); let system = System::new("test"); @@ -2737,8 +2753,7 @@ mod tests { &mut root_db, agrs, src_node_socket_addr, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); System::current().stop(); @@ -2781,7 +2796,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), src_root.node_addr_opt().clone().unwrap().into(), - &vec![], + make_default_neighborhood_metadata(), ); assert_eq!(result, GossipAcceptanceResult::Ignored); @@ -2865,7 +2880,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), src_node.node_addr_opt().unwrap().into(), - &vec![], + make_default_neighborhood_metadata(), ); assert_eq!(GossipAcceptanceResult::Ignored, result); @@ -2884,7 +2899,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -2923,7 +2938,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -2994,7 +3009,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -3090,7 +3105,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); let expected_acceptance_gossip_2 = GossipBuilder::new(&dest_db) @@ -3179,7 +3194,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); let expected_acceptance_gossip = GossipBuilder::new(&dest_db) @@ -3234,7 +3249,7 @@ mod tests { &mut dest_db, debut.try_into().unwrap(), gossip_source, - &vec![], + make_default_neighborhood_metadata(), ); assert_eq!(GossipAcceptanceResult::Ignored, result); @@ -3269,7 +3284,12 @@ mod tests { let subject = make_subject(main_cryptde()); let begin_at = time_t_timestamp(); - let result = subject.handle(&mut dest_db, debut_agrs, gossip_source, &vec![]); + let result = subject.handle( + &mut dest_db, + debut_agrs, + gossip_source, + make_default_neighborhood_metadata(), + ); let end_at = time_t_timestamp(); assert_eq!(GossipAcceptanceResult::Accepted, result); @@ -3297,7 +3317,12 @@ mod tests { let gossip_source = src_node.node_addr_opt().unwrap().into(); let subject = make_subject(main_cryptde()); - let result = subject.handle(&mut dest_db, debut_agrs, gossip_source, &vec![]); + let result = subject.handle( + &mut dest_db, + debut_agrs, + gossip_source, + make_default_neighborhood_metadata(), + ); assert_eq!(result, GossipAcceptanceResult::Accepted); assert_eq!( @@ -3327,7 +3352,12 @@ mod tests { let gossip_source = src_node.node_addr_opt().unwrap().into(); let subject = make_subject(main_cryptde()); - let result = subject.handle(&mut dest_db, debut_agrs, gossip_source, &vec![]); + let result = subject.handle( + &mut dest_db, + debut_agrs, + gossip_source, + make_default_neighborhood_metadata(), + ); assert_eq!(result, GossipAcceptanceResult::Accepted); assert_eq!( @@ -3347,6 +3377,8 @@ mod tests { let subject = IntroductionHandler::new(Logger::new("test")); let (gossip, gossip_source) = make_introduction(0, 1); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let agrs: Vec = gossip.try_into().unwrap(); let (_introducer, introducee) = IntroductionHandler::identify_players(agrs.clone(), gossip_source).unwrap(); @@ -3354,14 +3386,7 @@ mod tests { let system = System::new("introduction_gossip_handler_sends_cpm_for_neighborship_established"); - subject.handle( - cryptde, - &mut db, - agrs, - gossip_source, - &vec![], - &cpm_recipient, - ); + subject.handle(cryptde, &mut db, agrs, gossip_source, neighborhood_metadata); System::current().stop(); assert_eq!(system.run(), 0); @@ -3396,7 +3421,12 @@ mod tests { let (gossip, pass_target, gossip_source) = make_pass(2345); let subject = make_subject(main_cryptde()); - let result = subject.handle(&mut db, gossip.try_into().unwrap(), gossip_source, &vec![]); + let result = subject.handle( + &mut db, + gossip.try_into().unwrap(), + gossip_source, + make_default_neighborhood_metadata(), + ); let expected_relay_gossip = GossipBuilder::new(&db) .node(root_node.public_key(), true) @@ -3421,6 +3451,8 @@ mod tests { let (gossip, pass_target, gossip_source) = make_pass(2345); let system = System::new("handles_a_new_pass_target"); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let initial_timestamp = SystemTime::now(); let result = subject.handle( @@ -3428,8 +3460,7 @@ mod tests { &mut db, gossip.try_into().unwrap(), gossip_source, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); let final_timestamp = SystemTime::now(); @@ -3474,6 +3505,8 @@ mod tests { ); let system = System::new("handles_pass_target_that_is_not_yet_expired"); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let initial_timestamp = SystemTime::now(); let result = subject.handle( @@ -3481,8 +3514,7 @@ mod tests { &mut db, gossip.try_into().unwrap(), gossip_source, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); let final_timestamp = SystemTime::now(); @@ -3515,14 +3547,16 @@ mod tests { let system = System::new("handles_pass_target_that_is_a_part_of_a_different_connection_progress"); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; + neighborhood_metadata.connection_progress_peers = vec![pass_target_ip_addr]; let result = subject.handle( cryptde, &mut db, gossip.try_into().unwrap(), gossip_source, - &vec![pass_target_ip_addr], - &cpm_recipient, + neighborhood_metadata, ); System::current().stop(); @@ -3547,6 +3581,8 @@ mod tests { let subject = PassHandler::new(); let (gossip, pass_target, gossip_source) = make_pass(2345); let (cpm_recipient, recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; let pass_target_ip_addr = pass_target.node_addr_opt().unwrap().ip_addr(); let expired_time = PASS_GOSSIP_EXPIRED_TIME.add(Duration::from_secs(1)); subject @@ -3561,8 +3597,7 @@ mod tests { &mut db, gossip.try_into().unwrap(), gossip_source, - &vec![], - &cpm_recipient, + neighborhood_metadata, ); let final_timestamp = SystemTime::now(); @@ -3667,7 +3702,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), node_a.node_addr_opt().unwrap().into(), - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -3783,7 +3818,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), src_node.node_addr_opt().unwrap().into(), - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -3870,7 +3905,7 @@ mod tests { &mut dest_db, gossip.try_into().unwrap(), src_root.node_addr_opt().unwrap().into(), - &vec![], + make_default_neighborhood_metadata(), ); let after = time_t_timestamp(); @@ -4228,10 +4263,7 @@ mod tests { } fn make_subject(crypt_de: &dyn CryptDE) -> GossipAcceptorReal { - let (neighborhood, _, _) = make_recorder(); - let addr = neighborhood.start(); - let recipient = addr.recipient::(); - GossipAcceptorReal::new(crypt_de, recipient) + GossipAcceptorReal::new(crypt_de) } fn assert_node_records_eq( diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 82b4f7a31..f0e7fbc28 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -13,19 +13,19 @@ use std::convert::TryFrom; use std::net::{IpAddr, SocketAddr}; use std::path::PathBuf; -use actix::Addr; use actix::Context; use actix::Handler; use actix::MessageResult; use actix::Recipient; use actix::{Actor, System}; +use actix::{Addr, AsyncContext}; use itertools::Itertools; use masq_lib::messages::{ FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, }; use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest}; use masq_lib::ui_gateway::{MessageTarget, NodeFromUiMessage, NodeToUiMessage}; -use masq_lib::utils::{exit_process, ExpectValue}; +use masq_lib::utils::{exit_process, ExpectValue, NeighborhoodModeLight}; use crate::bootstrapper::BootstrapperConfig; use crate::database::db_initializer::DbInitializationConfig; @@ -46,7 +46,6 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData}; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::NodeQueryResponseMetadata; use crate::sub_lib::neighborhood::NodeRecordMetadataMessage; use crate::sub_lib::neighborhood::RemoveNeighborMessage; use crate::sub_lib::neighborhood::RouteQueryMessage; @@ -55,11 +54,11 @@ use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor}; use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService}; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; +use crate::sub_lib::neighborhood::{Hops, NeighborhoodMetadata, NodeQueryResponseMetadata}; use crate::sub_lib::neighborhood::{NRMetadataChange, NodeQueryMessage}; use crate::sub_lib::neighborhood::{NeighborhoodSubs, NeighborhoodTools}; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; -use crate::sub_lib::proxy_server::DEFAULT_MINIMUM_HOP_COUNT; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; @@ -78,6 +77,7 @@ use neighborhood_database::NeighborhoodDatabase; use node_record::NodeRecord; pub const CRASH_KEY: &str = "NEIGHBORHOOD"; +pub const DEFAULT_MIN_HOPS_COUNT: Hops = Hops::ThreeHops; pub const UNREACHABLE_HOST_PENALTY: i64 = 100_000_000; pub const RESPONSE_UNDESIRABILITY_FACTOR: usize = 1_000; // assumed response length is request * this @@ -87,10 +87,12 @@ pub struct Neighborhood { hopper_no_lookup_opt: Option>, connected_signal_opt: Option>, node_to_ui_recipient_opt: Option>, - gossip_acceptor_opt: Option>, - gossip_producer_opt: Option>, + gossip_acceptor: Box, + gossip_producer: Box, neighborhood_database: NeighborhoodDatabase, consuming_wallet_opt: Option, + mode: NeighborhoodModeLight, + min_hops_count: Hops, next_return_route_id: u32, overall_connection_status: OverallConnectionStatus, chain: Chain, @@ -114,11 +116,6 @@ impl Handler for Neighborhood { self.hopper_opt = Some(msg.peer_actors.hopper.from_hopper_client); self.hopper_no_lookup_opt = Some(msg.peer_actors.hopper.from_hopper_client_no_lookup); self.connected_signal_opt = Some(msg.peer_actors.accountant.start); - self.gossip_acceptor_opt = Some(Box::new(GossipAcceptorReal::new( - self.cryptde, - msg.peer_actors.neighborhood.connection_progress_sub, - ))); - self.gossip_producer_opt = Some(Box::new(GossipProducerReal::new())); self.node_to_ui_recipient_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); } } @@ -223,11 +220,12 @@ impl Handler> for Neighborhood { fn handle( &mut self, msg: ExpiredCoresPackage, - _ctx: &mut Self::Context, + ctx: &mut Self::Context, ) -> Self::Result { let incoming_gossip = msg.payload; + let cpm_recipient = ctx.address().recipient::(); self.log_incoming_gossip(&incoming_gossip, msg.immediate_neighbor); - self.handle_gossip(incoming_gossip, msg.immediate_neighbor); + self.handle_gossip(incoming_gossip, msg.immediate_neighbor, cpm_recipient); } } @@ -433,23 +431,23 @@ enum RouteDirection { impl Neighborhood { pub fn new(cryptde: &'static dyn CryptDE, config: &BootstrapperConfig) -> Self { let neighborhood_config = &config.neighborhood_config; - if neighborhood_config.mode.is_zero_hop() - && !neighborhood_config.mode.neighbor_configs().is_empty() - { + let min_hops_count = neighborhood_config.min_hops_count; + let neighborhood_mode = &neighborhood_config.mode; + let mode: NeighborhoodModeLight = neighborhood_mode.into(); + let neighborhood_configs = neighborhood_mode.neighbor_configs(); + if mode == NeighborhoodModeLight::ZeroHop && !neighborhood_configs.is_empty() { panic!( "A zero-hop MASQ Node is not decentralized and cannot have a --neighbors setting" ) } let neighborhood_database = NeighborhoodDatabase::new( cryptde.public_key(), - neighborhood_config.mode.clone(), + neighborhood_mode.clone(), config.earning_wallet.clone(), cryptde, ); let is_mainnet = config.blockchain_bridge_config.chain.is_mainnet(); - let initial_neighbors: Vec = neighborhood_config - .mode - .neighbor_configs() + let initial_neighbors: Vec = neighborhood_configs .iter() .map(|nc| { let mainnet_nc = nc.blockchain.is_mainnet(); @@ -472,10 +470,12 @@ impl Neighborhood { hopper_no_lookup_opt: None, connected_signal_opt: None, node_to_ui_recipient_opt: None, - gossip_acceptor_opt: None, - gossip_producer_opt: None, + gossip_acceptor: Box::new(GossipAcceptorReal::new(cryptde)), + gossip_producer: Box::new(GossipProducerReal::new()), neighborhood_database, consuming_wallet_opt: config.consuming_wallet_opt.clone(), + mode, + min_hops_count, next_return_route_id: 0, overall_connection_status, chain: config.blockchain_bridge_config.chain, @@ -533,7 +533,7 @@ impl Neighborhood { fn handle_route_query_message(&mut self, msg: RouteQueryMessage) -> Option { let debug_msg_opt = self.logger.debug_enabled().then(|| format!("{:?}", msg)); - let route_result = if msg.minimum_hop_count == 0 { + let route_result = if self.mode == NeighborhoodModeLight::ZeroHop { Ok(self.zero_hop_route_response()) } else { self.make_round_trip_route(msg) @@ -576,9 +576,7 @@ impl Neighborhood { } let gossip = self - .gossip_producer_opt - .as_ref() - .expect("Gossip Producer uninitialized") + .gossip_producer .produce_debut(&self.neighborhood_database); self.overall_connection_status .iter_initial_node_descriptors() @@ -627,7 +625,12 @@ impl Neighborhood { ); } - fn handle_gossip(&mut self, incoming_gossip: Gossip_0v1, gossip_source: SocketAddr) { + fn handle_gossip( + &mut self, + incoming_gossip: Gossip_0v1, + gossip_source: SocketAddr, + cpm_recipient: Recipient, + ) { let record_count = incoming_gossip.node_records.len(); info!( self.logger, @@ -666,7 +669,7 @@ impl Neighborhood { return; } - self.handle_gossip_agrs(agrs, gossip_source); + self.handle_gossip_agrs(agrs, gossip_source, cpm_recipient); self.announce_gossip_handling_completion(record_count); } @@ -715,9 +718,14 @@ impl Neighborhood { .collect() } - fn handle_gossip_agrs(&mut self, agrs: Vec, gossip_source: SocketAddr) { + fn handle_gossip_agrs( + &mut self, + agrs: Vec, + gossip_source: SocketAddr, + cpm_recipient: Recipient, + ) { let neighbor_keys_before = self.neighbor_keys(); - self.handle_agrs(agrs, gossip_source); + self.handle_agrs(agrs, gossip_source, cpm_recipient); let neighbor_keys_after = self.neighbor_keys(); self.handle_database_changes(neighbor_keys_before, neighbor_keys_after); } @@ -731,20 +739,25 @@ impl Neighborhood { .collect() } - fn handle_agrs(&mut self, agrs: Vec, gossip_source: SocketAddr) { + fn handle_agrs( + &mut self, + agrs: Vec, + gossip_source: SocketAddr, + cpm_recipient: Recipient, + ) { let ignored_node_name = self.gossip_source_name(&agrs, gossip_source); let gossip_record_count = agrs.len(); - let connection_progress_peers = self.overall_connection_status.get_peer_addrs(); - let acceptance_result = self - .gossip_acceptor_opt - .as_ref() - .expect("Gossip Acceptor wasn't created.") - .handle( - &mut self.neighborhood_database, - agrs, - gossip_source, - &connection_progress_peers, - ); + let neighborhood_metadata = NeighborhoodMetadata { + connection_progress_peers: self.overall_connection_status.get_peer_addrs(), + cpm_recipient, + min_hops_count: self.min_hops_count, + }; + let acceptance_result = self.gossip_acceptor.handle( + &mut self.neighborhood_database, + agrs, + gossip_source, + neighborhood_metadata, + ); match acceptance_result { GossipAcceptanceResult::Accepted => self.gossip_to_neighbors(), GossipAcceptanceResult::Reply(next_debut, target_key, target_node_addr) => { @@ -823,7 +836,6 @@ impl Neighborhood { let msg = RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count: DEFAULT_MINIMUM_HOP_COUNT, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, hostname_opt: None, @@ -831,11 +843,11 @@ impl Neighborhood { if self.handle_route_query_message(msg).is_some() { debug!( &self.logger, - "The connectivity check has found a 3 hops route." + "The connectivity check has found a {}-hop(s) route.", self.min_hops_count as usize ); self.overall_connection_status .update_ocs_stage_and_send_message_to_ui( - OverallConnectionStage::ThreeHopsRouteFound, + OverallConnectionStage::RouteFound, self.node_to_ui_recipient_opt .as_ref() .expect("UI was not bound."), @@ -874,9 +886,7 @@ impl Neighborhood { .collect_vec(); neighbors.iter().for_each(|neighbor| { if let Some(gossip) = self - .gossip_producer_opt - .as_ref() - .expect("Gossip Producer uninitialized") + .gossip_producer .produce(&mut self.neighborhood_database, neighbor) { self.gossip_to_neighbor(neighbor, gossip) @@ -959,7 +969,7 @@ impl Neighborhood { let over = self.make_route_segment( self.cryptde.public_key(), request_msg.target_key_opt.as_ref(), - request_msg.minimum_hop_count, + self.min_hops_count as usize, request_msg.target_component, request_msg.payload_size, RouteDirection::Over, @@ -974,7 +984,7 @@ impl Neighborhood { let back = self.make_route_segment( over.keys.last().expect("Empty segment"), Some(self.cryptde.public_key()), - request_msg.minimum_hop_count, + self.min_hops_count as usize, request_msg .return_component_opt .expect("No return component"), @@ -1610,11 +1620,11 @@ mod tests { use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::neighborhood::RatePack; use crate::sub_lib::neighborhood::{ AskAboutDebutGossipMessage, ExpectedServices, NeighborhoodMode, }; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; + use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; use crate::sub_lib::peer_actors::PeerActors; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::versioned_data::VersionedData; @@ -1622,8 +1632,10 @@ mod tests { use crate::test_utils::make_meaningless_route; use crate::test_utils::make_wallet; use crate::test_utils::neighborhood_test_utils::{ - db_from_node, make_global_cryptde_node_record, make_ip, make_node, make_node_descriptor, - make_node_record, make_node_record_f, neighborhood_from_nodes, + cryptdes_from_node_records, db_from_node, linearly_connect_nodes, + make_global_cryptde_node_record, make_ip, make_node, make_node_descriptor, + make_node_record, make_node_record_f, make_node_records, neighborhood_from_nodes, + MIN_HOPS_COUNT_FOR_TEST, }; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::rate_pack; @@ -1632,7 +1644,7 @@ mod tests { use crate::test_utils::recorder::Recorder; use crate::test_utils::recorder::Recording; use crate::test_utils::unshared_test_utils::{ - make_node_to_ui_recipient, make_recipient_and_recording_arc, + make_cpm_recipient, make_node_to_ui_recipient, make_recipient_and_recording_arc, prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, }; use crate::test_utils::vec_to_set; @@ -1663,6 +1675,33 @@ mod tests { #[test] fn constants_have_correct_values() { assert_eq!(CRASH_KEY, "NEIGHBORHOOD"); + assert_eq!(DEFAULT_MIN_HOPS_COUNT, Hops::ThreeHops); + } + + #[test] + fn min_hops_count_is_set_inside_neighborhood() { + let min_hops_count = Hops::SixHops; + let mode = NeighborhoodMode::Standard( + NodeAddr::new(&make_ip(1), &[1234, 2345]), + vec![make_node_descriptor(make_ip(2))], + rate_pack(100), + ); + let neighborhood_config = NeighborhoodConfig { + mode, + min_hops_count, + }; + + let subject = Neighborhood::new( + main_cryptde(), + &bc_from_nc_plus( + neighborhood_config, + make_wallet("earning"), + None, + "min_hops_count_is_set_inside_neighborhood", + ), + ); + + assert_eq!(subject.min_hops_count, Hops::SixHops); } #[test] @@ -1679,6 +1718,7 @@ mod tests { "masq://eth-ropsten:AQIDBA@1.2.3.4:1234", )) .unwrap()]), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), None, @@ -1703,6 +1743,7 @@ mod tests { "masq://eth-mainnet:AQIDBA@1.2.3.4:1234", )) .unwrap()]), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), None, @@ -1723,6 +1764,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), None, @@ -1751,6 +1793,7 @@ mod tests { vec![neighbor.node_descriptor(TEST_DEFAULT_CHAIN, cryptde)], DEFAULT_RATE_PACK.clone(), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), None, @@ -1767,24 +1810,6 @@ mod tests { assert_eq!(root_node_record_ref.half_neighbor_keys().len(), 0); } - #[test] - fn gossip_acceptor_and_gossip_producer_are_properly_initialized_through_bind_message() { - let subject = make_standard_subject(); - let addr = subject.start(); - let peer_actors = peer_actors_builder().build(); - let system = System::new("test"); - let assertions = Box::new(move |actor: &mut Neighborhood| { - assert!(actor.gossip_acceptor_opt.is_some()); - assert!(actor.gossip_producer_opt.is_some()); - }); - - addr.try_send(BindMessage { peer_actors }).unwrap(); - - addr.try_send(AssertionsMessage { assertions }).unwrap(); - System::current().stop(); - assert_eq!(system.run(), 0); - } - #[test] fn node_with_zero_hop_config_ignores_start_message() { init_test_logging(); @@ -1807,6 +1832,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -1855,6 +1881,7 @@ mod tests { ], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -1934,6 +1961,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); @@ -2402,6 +2430,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let mut subject = Neighborhood::new( main_cryptde(), @@ -2478,6 +2507,7 @@ mod tests { ], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), None, @@ -2549,6 +2579,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -2584,6 +2615,7 @@ mod tests { vec![node_record_to_neighbor_config(&one_neighbor)], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -2636,6 +2668,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -2676,6 +2709,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, node_record.earning_wallet(), None, @@ -2714,9 +2748,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 5, 400, - )); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 400)); System::current().stop_with_code(0); system.run(); @@ -2732,9 +2764,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 430, - )); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 430)); System::current().stop_with_code(0); system.run(); @@ -2750,6 +2780,7 @@ mod tests { "route_query_succeeds_when_asked_for_one_hop_round_trip_route_without_consuming_wallet", ); let mut subject = make_standard_subject(); + subject.min_hops_count = Hops::OneHop; subject .neighborhood_database .root_mut() @@ -2774,7 +2805,7 @@ mod tests { } let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = RouteQueryMessage::data_indefinite_route_request(None, 1, 54000); + let msg = RouteQueryMessage::data_indefinite_route_request(None, 54000); let future = sub.send(msg); @@ -2831,6 +2862,7 @@ mod tests { ) { let system = System::new("route_query_responds_with_none_when_asked_for_one_hop_round_trip_route_without_consuming_wallet_when_back_route_needs_two_hops"); let mut subject = make_standard_subject(); + subject.min_hops_count = Hops::OneHop; let a = &make_node_record(1234, true); let b = &subject.neighborhood_database.root().clone(); let c = &make_node_record(3456, true); @@ -2847,7 +2879,7 @@ mod tests { } let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = RouteQueryMessage::data_indefinite_route_request(None, 1, 10000); + let msg = RouteQueryMessage::data_indefinite_route_request(None, 10000); let future = sub.send(msg); @@ -2861,10 +2893,11 @@ mod tests { fn route_query_responds_with_none_when_asked_for_two_hop_one_way_route_without_consuming_wallet( ) { let system = System::new("route_query_responds_with_none_when_asked_for_two_hop_one_way_route_without_consuming_wallet"); - let subject = make_standard_subject(); + let mut subject = make_standard_subject(); + subject.min_hops_count = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = RouteQueryMessage::data_indefinite_route_request(None, 2, 20000); + let msg = RouteQueryMessage::data_indefinite_route_request(None, 20000); let future = sub.send(msg); @@ -2878,12 +2911,13 @@ mod tests { fn route_query_responds_with_standard_zero_hop_route_when_requested() { let cryptde = main_cryptde(); let system = System::new("responds_with_standard_zero_hop_route_when_requested"); - let subject = make_standard_subject(); + let mut subject = make_standard_subject(); + subject.mode = NeighborhoodModeLight::ZeroHop; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 0, 12345, + None, 12345, )); System::current().stop_with_code(0); @@ -2950,6 +2984,7 @@ mod tests { let earning_wallet = make_wallet("earning"); let system = System::new("route_query_messages"); let mut subject = make_standard_subject(); + subject.min_hops_count = Hops::TwoHops; subject .neighborhood_database .root_mut() @@ -2978,9 +3013,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 5000, - )); + let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 5000)); System::current().stop_with_code(0); system.run(); @@ -3070,21 +3103,16 @@ mod tests { fn return_route_ids_increase() { let cryptde = main_cryptde(); let system = System::new("return_route_ids_increase"); - let (_, _, _, subject) = make_o_r_e_subject(); - + let (_, _, _, mut subject) = make_o_r_e_subject(); + subject.min_hops_count = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 2000, - )); - let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 3000, - )); + let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000)); + let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 3000)); System::current().stop_with_code(0); system.run(); - let result_0 = data_route_0.wait().unwrap().unwrap(); let result_1 = data_route_1.wait().unwrap().unwrap(); let juicy_parts = |result: RouteQueryResponse| { @@ -3106,7 +3134,8 @@ mod tests { fn can_update_consuming_wallet() { let cryptde = main_cryptde(); let system = System::new("can_update_consuming_wallet"); - let (o, r, e, subject) = make_o_r_e_subject(); + let (o, r, e, mut subject) = make_o_r_e_subject(); + subject.min_hops_count = Hops::TwoHops; let addr: Addr = subject.start(); let set_wallet_sub = addr.clone().recipient::(); let route_sub = addr.recipient::(); @@ -3130,15 +3159,13 @@ mod tests { ) .unwrap(); - let route_request_1 = route_sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 1000, - )); + let route_request_1 = + route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 1000)); let _ = set_wallet_sub.try_send(SetConsumingWalletMessage { wallet: expected_new_wallet, }); - let route_request_2 = route_sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 2, 2000, - )); + let route_request_2 = + route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000)); System::current().stop(); system.run(); @@ -3550,6 +3577,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -3661,7 +3689,7 @@ mod tests { let subject_node = make_global_cryptde_node_record(1234, true); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor)); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let gossip = GossipBuilder::new(&subject.neighborhood_database) .node(subject_node.public_key(), true) .build(); @@ -3681,7 +3709,7 @@ mod tests { System::current().stop(); system.run(); let mut handle_params = handle_params_arc.lock().unwrap(); - let (call_database, call_agrs, call_gossip_source, connection_progress_peers) = + let (call_database, call_agrs, call_gossip_source, neighborhood_metadata) = handle_params.remove(0); assert!(handle_params.is_empty()); assert_eq!(&subject_node, call_database.root()); @@ -3691,7 +3719,10 @@ mod tests { let actual_gossip_source: SocketAddr = subject_node.node_addr_opt().unwrap().into(); assert_eq!(actual_gossip_source, call_gossip_source); let neighbor_ip = neighbor.node_addr_opt().unwrap().ip_addr(); - assert_eq!(connection_progress_peers, vec![neighbor_ip]); + assert_eq!( + neighborhood_metadata.connection_progress_peers, + vec![neighbor_ip] + ); } #[test] @@ -3718,7 +3749,7 @@ mod tests { introduction_target_node.public_key().clone(), introduction_target_node.node_addr_opt().unwrap(), )); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); let system = System::new(""); @@ -3727,6 +3758,7 @@ mod tests { subject.handle_gossip( Gossip_0v1::new(vec![]), SocketAddr::from_str("1.1.1.1:1111").unwrap(), + make_cpm_recipient().0, ); System::current().stop(); @@ -3762,9 +3794,13 @@ mod tests { let system = System::new("neighborhood_transmits_gossip_failure_properly"); let peer_actors = peer_actors_builder().hopper(hopper).build(); subject.hopper_no_lookup_opt = Some(peer_actors.hopper.from_hopper_client_no_lookup); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -3796,7 +3832,7 @@ mod tests { database: &mut NeighborhoodDatabase, _agrs: Vec, _gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], + _neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let non_root_database_keys = database .keys() @@ -3850,15 +3886,19 @@ mod tests { replacement_database.add_node(neighbor.clone()).unwrap(); replacement_database .add_arbitrary_half_neighbor(subject_node.public_key(), neighbor.public_key()); - subject.gossip_acceptor_opt = Some(Box::new(DatabaseReplacementGossipAcceptor { + subject.gossip_acceptor = Box::new(DatabaseReplacementGossipAcceptor { replacement_database, - })); + }); let (accountant, _, accountant_recording_arc) = make_recorder(); let system = System::new("neighborhood_does_not_start_accountant_if_no_route_can_be_made"); let peer_actors = peer_actors_builder().accountant(accountant).build(); bind_subject(&mut subject, peer_actors); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -3873,15 +3913,19 @@ mod tests { let neighbor = make_node_record(1111, true); let mut subject: Neighborhood = neighborhood_from_nodes(&subject_node, Some(&neighbor)); let replacement_database = subject.neighborhood_database.clone(); - subject.gossip_acceptor_opt = Some(Box::new(DatabaseReplacementGossipAcceptor { + subject.gossip_acceptor = Box::new(DatabaseReplacementGossipAcceptor { replacement_database, - })); + }); let (accountant, _, accountant_recording_arc) = make_recorder(); let system = System::new("neighborhood_does_not_start_accountant_if_already_connected"); let peer_actors = peer_actors_builder().accountant(accountant).build(); bind_subject(&mut subject, peer_actors); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -3893,13 +3937,17 @@ mod tests { fn neighborhood_starts_accountant_when_first_route_can_be_made() { let (accountant, _, accountant_recording_arc) = make_recorder(); let (ui_gateway, _, _) = make_recorder(); - let mut subject = make_neighborhood_linearly_connected_with_nodes(3); + let mut subject = make_neighborhood_with_linearly_connected_nodes(4); subject.node_to_ui_recipient_opt = Some(ui_gateway.start().recipient()); let peer_actors = peer_actors_builder().accountant(accountant).build(); bind_subject(&mut subject, peer_actors); let system = System::new("neighborhood_does_not_start_accountant_if_no_route_can_be_made"); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -3928,12 +3976,13 @@ mod tests { initial_node_descriptors, rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); let mut subject = Neighborhood::new(main_cryptde(), &bootstrap_config); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let mut peer_2_db = db_from_node(&peer_2); peer_2_db.add_node(peer_1.clone()).unwrap(); peer_2_db.add_arbitrary_full_neighbor(peer_2.public_key(), peer_1.public_key()); @@ -3943,27 +3992,33 @@ mod tests { .build(); let agrs: Vec = pass_gossip.try_into().unwrap(); - subject.handle_agrs(agrs, peer_2_socket_addr); + subject.handle_agrs(agrs, peer_2_socket_addr, make_cpm_recipient().0); TestLogHandler::new() .exists_log_containing(&format!("Gossip from {} ignored", peer_2_socket_addr)); } - #[test] - fn neighborhood_updates_ocs_stage_and_sends_message_to_the_ui_when_first_route_can_be_made() { + fn assert_connectivity_check(hops: Hops) { init_test_logging(); - let test_name = "neighborhood_updates_ocs_stage_and_sends_message_to_the_ui_when_first_route_can_be_made"; - let mut subject: Neighborhood = make_neighborhood_linearly_connected_with_nodes(3); + let test_name = &format!("connectivity_check_for_{}_hops", hops as usize); + let nodes_count = hops as u16 + 1; + let mut subject: Neighborhood = + make_neighborhood_with_linearly_connected_nodes(nodes_count); let (ui_gateway, _, ui_gateway_arc) = make_recorder(); let (accountant, _, _) = make_recorder(); let node_to_ui_recipient = ui_gateway.start().recipient::(); let connected_signal = accountant.start().recipient(); + subject.min_hops_count = hops; subject.logger = Logger::new(test_name); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); subject.connected_signal_opt = Some(connected_signal); let system = System::new(test_name); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -3973,29 +4028,39 @@ mod tests { assert_eq!(subject.overall_connection_status.can_make_routes(), true); assert_eq!( subject.overall_connection_status.stage(), - OverallConnectionStage::ThreeHopsRouteFound + OverallConnectionStage::RouteFound ); assert_eq!( node_to_ui_message, &NodeToUiMessage { target: MessageTarget::AllClients, body: UiConnectionChangeBroadcast { - stage: UiConnectionStage::ThreeHopsRouteFound + stage: UiConnectionStage::RouteFound } .tmb(0), } ); TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: {}: The connectivity check has found a 3 hops route.", - test_name + "DEBUG: {}: The connectivity check has found a {}-hop(s) route.", + test_name, hops as usize )); } + #[test] + fn connectivity_check_for_different_hops() { + assert_connectivity_check(Hops::OneHop); + assert_connectivity_check(Hops::TwoHops); + assert_connectivity_check(Hops::ThreeHops); + assert_connectivity_check(Hops::FourHops); + assert_connectivity_check(Hops::FiveHops); + assert_connectivity_check(Hops::SixHops); + } + #[test] fn neighborhood_logs_when_three_hops_route_can_not_be_made() { init_test_logging(); let test_name = "neighborhood_logs_when_three_hops_route_can_not_be_made"; - let mut subject: Neighborhood = make_neighborhood_linearly_connected_with_nodes(2); + let mut subject: Neighborhood = make_neighborhood_with_linearly_connected_nodes(3); let (ui_gateway, _, ui_gateway_arc) = make_recorder(); let (accountant, _, _) = make_recorder(); let node_to_ui_recipient = ui_gateway.start().recipient::(); @@ -4005,7 +4070,11 @@ mod tests { subject.connected_signal_opt = Some(connected_signal); let system = System::new(test_name); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); System::current().stop(); system.run(); @@ -4028,7 +4097,7 @@ mod tests { database: &mut NeighborhoodDatabase, _agrs: Vec, _gossip_source: SocketAddr, - _connection_progress_peers: &[IpAddr], + _neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { let half_neighbor_keys = database .root() @@ -4069,10 +4138,14 @@ mod tests { let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc) .set_past_neighbors_result(Ok(())); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); let mut set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); let (neighbors_opt, db_password) = set_past_neighbors_params.remove(0); @@ -4108,10 +4181,14 @@ mod tests { let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc) .set_past_neighbors_result(Ok(())); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); let mut set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); let (neighbors_opt, db_password) = set_past_neighbors_params.remove(0); @@ -4139,10 +4216,14 @@ mod tests { let set_past_neighbors_params_arc = Arc::new(Mutex::new(vec![])); let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); let set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); assert!(set_past_neighbors_params.is_empty()); @@ -4168,11 +4249,15 @@ mod tests { let set_past_neighbors_params_arc = Arc::new(Mutex::new(vec![])); let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); subject.db_password_opt = None; - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); let set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); assert!(set_past_neighbors_params.is_empty()); @@ -4198,10 +4283,14 @@ mod tests { let persistent_config = PersistentConfigurationMock::new().set_past_neighbors_result(Err( PersistentConfigError::DatabaseError("database is locked".to_string()), )); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); TestLogHandler::new().exists_log_containing("WARN: Neighborhood: Could not persist immediate-neighbor changes: database locked - skipping"); } @@ -4226,10 +4315,14 @@ mod tests { let persistent_config = PersistentConfigurationMock::new().set_past_neighbors_result(Err( PersistentConfigError::DatabaseError("Booga".to_string()), )); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject.handle_gossip_agrs(vec![], SocketAddr::from_str("1.2.3.4:1234").unwrap()); + subject.handle_gossip_agrs( + vec![], + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + make_cpm_recipient().0, + ); TestLogHandler::new().exists_log_containing("ERROR: Neighborhood: Could not persist immediate-neighbor changes: DatabaseError(\"Booga\")"); } @@ -4283,14 +4376,14 @@ mod tests { .add_arbitrary_half_neighbor(subject_node.public_key(), half_neighbor.public_key()); let gossip_acceptor = GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Accepted); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let gossip = Gossip_0v1::new(vec![]); let produce_params_arc = Arc::new(Mutex::new(vec![])); let gossip_producer = GossipProducerMock::new() .produce_params(&produce_params_arc) .produce_result(Some(gossip.clone())) .produce_result(Some(gossip.clone())); - subject.gossip_producer_opt = Some(Box::new(gossip_producer)); + subject.gossip_producer = Box::new(gossip_producer); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -4300,6 +4393,7 @@ mod tests { subject.handle_gossip( Gossip_0v1::new(vec![]), SocketAddr::from_str("1.1.1.1:1111").unwrap(), + make_cpm_recipient().0, ); System::current().stop(); @@ -4375,12 +4469,12 @@ mod tests { .add_arbitrary_full_neighbor(subject_node.public_key(), ungossippable.public_key()); let gossip_acceptor = GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Accepted); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let produce_params_arc = Arc::new(Mutex::new(vec![])); let gossip_producer = GossipProducerMock::new() .produce_params(&produce_params_arc) .produce_result(None); - subject.gossip_producer_opt = Some(Box::new(gossip_producer)); + subject.gossip_producer = Box::new(gossip_producer); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -4390,6 +4484,7 @@ mod tests { subject.handle_gossip( Gossip_0v1::new(vec![]), SocketAddr::from_str("1.1.1.1:1111").unwrap(), + make_cpm_recipient().0, ); System::current().stop(); @@ -4414,7 +4509,7 @@ mod tests { debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), )); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); let system = System::new(""); @@ -4425,6 +4520,7 @@ mod tests { // In real life this would be Relay Gossip from gossip_source to debut_node. Gossip_0v1::new(vec![]), gossip_source, + make_cpm_recipient().0, ); System::current().stop(); @@ -4456,7 +4552,7 @@ mod tests { let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor)); let gossip_acceptor = GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Ignored); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -4466,6 +4562,7 @@ mod tests { subject.handle_gossip( Gossip_0v1::new(vec![]), subject_node.node_addr_opt().unwrap().into(), + make_cpm_recipient().0, ); System::current().stop(); @@ -4482,7 +4579,7 @@ mod tests { let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor)); let gossip_acceptor = GossipAcceptorMock::new() .handle_result(GossipAcceptanceResult::Ban("Bad guy".to_string())); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); + subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -4492,6 +4589,7 @@ mod tests { subject.handle_gossip( Gossip_0v1::new(vec![]), subject_node.node_addr_opt().unwrap().into(), + make_cpm_recipient().0, ); System::current().stop(); @@ -4506,8 +4604,6 @@ mod tests { fn neighborhood_does_not_accept_gossip_if_a_record_is_non_deserializable() { init_test_logging(); let mut subject = make_standard_subject(); - let gossip_acceptor = GossipAcceptorMock::new(); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); let db = &mut subject.neighborhood_database; let one_node_key = &db.add_node(make_node_record(2222, true)).unwrap(); let another_node_key = &db.add_node(make_node_record(3333, true)).unwrap(); @@ -4518,7 +4614,7 @@ mod tests { gossip.node_records[1].signed_data = PlainData::new(&[1, 2, 3, 4]); // corrupt second record let gossip_source = SocketAddr::from_str("1.2.3.4:1234").unwrap(); - subject.handle_gossip(gossip, gossip_source); + subject.handle_gossip(gossip, gossip_source, make_cpm_recipient().0); // No panic means that subject didn't try to invoke the GossipAcceptorMock: test passes! TestLogHandler::new().exists_log_containing(&format!( @@ -4531,8 +4627,6 @@ mod tests { fn neighborhood_does_not_accept_gossip_if_a_record_signature_is_invalid() { init_test_logging(); let mut subject = make_standard_subject(); - let gossip_acceptor = GossipAcceptorMock::new(); - subject.gossip_acceptor_opt = Some(Box::new(gossip_acceptor)); let db = &mut subject.neighborhood_database; let one_node_key = &db.add_node(make_node_record(2222, true)).unwrap(); let another_node_key = &db.add_node(make_node_record(3333, true)).unwrap(); @@ -4543,7 +4637,7 @@ mod tests { gossip.node_records[1].signature = CryptData::new(&[1, 2, 3, 4]); // corrupt second record let gossip_source = SocketAddr::from_str("1.2.3.4:1234").unwrap(); - subject.handle_gossip(gossip, gossip_source); + subject.handle_gossip(gossip, gossip_source, make_cpm_recipient().0); // No panic means that subject didn't try to invoke the GossipAcceptorMock: test passes! TestLogHandler::new().exists_log_containing(&format!( @@ -4605,6 +4699,7 @@ mod tests { vec![], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, this_node_inside.earning_wallet(), None, @@ -4667,6 +4762,7 @@ mod tests { vec![debut_target.clone()], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, NodeRecord::earning_wallet_from_key(&cryptde.public_key()), NodeRecord::consuming_wallet_from_key(&cryptde.public_key()), @@ -4764,7 +4860,6 @@ mod tests { let three_hop_route_request = RouteQueryMessage { target_key_opt: Some(c.public_key().clone()), target_component: Component::ProxyClient, - minimum_hop_count: 3, return_component_opt: None, payload_size: 10000, hostname_opt: None, @@ -4858,6 +4953,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -4919,6 +5015,7 @@ mod tests { vec![node_record_to_neighbor_config(&one_neighbor)], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -4985,6 +5082,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, earning_wallet.clone(), consuming_wallet.clone(), @@ -5048,6 +5146,7 @@ mod tests { ))], rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, node_record.earning_wallet(), None, @@ -5089,10 +5188,12 @@ mod tests { #[test] fn make_round_trip_route_returns_error_when_no_non_next_door_neighbor_found() { // Make a triangle of Nodes + let min_hops_count = Hops::TwoHops; let one_next_door_neighbor = make_node_record(3333, true); let another_next_door_neighbor = make_node_record(4444, true); let subject_node = make_global_cryptde_node_record(5555, true); // 9e7p7un06eHs6frl5A let mut subject = neighborhood_from_nodes(&subject_node, Some(&one_next_door_neighbor)); + subject.min_hops_count = min_hops_count; subject .neighborhood_database @@ -5116,12 +5217,9 @@ mod tests { another_next_door_neighbor.public_key(), ); - let minimum_hop_count = 2; - let result = subject.make_round_trip_route(RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, hostname_opt: None, @@ -5130,7 +5228,7 @@ mod tests { assert_eq!( Err(format!( "Couldn't find any routes: at least {}-hop from {} to ProxyClient at Unknown", - minimum_hop_count, + min_hops_count as usize, main_cryptde().public_key() )), result @@ -5144,6 +5242,7 @@ mod tests { let subject_node = make_global_cryptde_node_record(666, true); // 9e7p7un06eHs6frl5A let mut subject = neighborhood_from_nodes(&subject_node, Some(&next_door_neighbor)); + subject.min_hops_count = Hops::TwoHops; subject .neighborhood_database @@ -5163,12 +5262,9 @@ mod tests { .neighborhood_database .add_arbitrary_full_neighbor(next_door_neighbor.public_key(), exit_node.public_key()); - let minimum_hop_count = 2; - let result = subject.make_round_trip_route(RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, hostname_opt: None, @@ -5209,6 +5305,54 @@ mod tests { assert_eq!(expected_public_keys, actual_keys); } + fn assert_route_query_message(min_hops_count: Hops) { + let hops = min_hops_count as usize; + let nodes_count = hops + 1; + let root_node = make_global_cryptde_node_record(4242, true); + let mut nodes = make_node_records(nodes_count as u16); + nodes[0] = root_node; + let db = linearly_connect_nodes(&nodes); + let mut subject = neighborhood_from_nodes(db.root(), nodes.get(1)); + subject.min_hops_count = min_hops_count; + subject.neighborhood_database = db; + + let result = subject.make_round_trip_route(RouteQueryMessage { + target_key_opt: None, + target_component: Component::ProxyClient, + return_component_opt: Some(Component::ProxyServer), + payload_size: 10000, + hostname_opt: None, + }); + + let assert_hops = |cryptdes: Vec, route: &[CryptData]| { + assert_eq!(cryptdes.len(), route.len()); + for (cryptde, data) in cryptdes.into_iter().zip(route) { + decodex::(&cryptde, data).unwrap(); + } + }; + let mut route = result.clone().unwrap().route.hops; + let route_length = route.len(); + let _accounting = route.pop(); + let over_route = &route[..hops]; + let back_route = &route[hops..]; + let over_cryptdes = cryptdes_from_node_records(&nodes[..nodes_count - 1]); + let mut back_cryptdes = cryptdes_from_node_records(&nodes); + back_cryptdes.reverse(); + assert_eq!(route_length, 2 * nodes_count); + assert_hops(over_cryptdes, over_route); + assert_hops(back_cryptdes, back_route); + } + + #[test] + fn routes_can_be_calculated_for_different_hops() { + assert_route_query_message(Hops::OneHop); + assert_route_query_message(Hops::TwoHops); + assert_route_query_message(Hops::ThreeHops); + assert_route_query_message(Hops::FourHops); + assert_route_query_message(Hops::FiveHops); + assert_route_query_message(Hops::SixHops); + } + /* For the next two tests, the database looks like this: @@ -5237,6 +5381,7 @@ mod tests { fn check_fee_preference(payload_size: usize, a_not_b: bool) { let mut subject = make_standard_subject(); + subject.min_hops_count = Hops::TwoHops; let db = &mut subject.neighborhood_database; let o = &db.root().public_key().clone(); let a = &db.add_node(make_node_record(2345, true)).unwrap(); @@ -5265,7 +5410,6 @@ mod tests { .handle_route_query_message(RouteQueryMessage { target_key_opt: Some(x.clone()), target_component: Component::ProxyClient, - minimum_hop_count: 2, return_component_opt: Some(Component::ProxyServer), payload_size, hostname_opt: None, @@ -5467,7 +5611,6 @@ mod tests { ); let peer_actors = peer_actors_builder().hopper(hopper).build(); subject.hopper_opt = Some(peer_actors.hopper.from_hopper_client); - subject.gossip_producer_opt = Some(Box::new(GossipProducerReal::new())); subject.handle_stream_shutdown_msg(StreamShutdownMsg { peer_addr: shutdown_neighbor_node_socket_addr, @@ -5505,6 +5648,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, make_wallet("earning"), None, @@ -5587,7 +5731,7 @@ mod tests { #[test] fn connection_status_message_is_handled_properly_for_three_hops_route_found() { - let stage = OverallConnectionStage::ThreeHopsRouteFound; + let stage = OverallConnectionStage::RouteFound; let client_id = 1236; let context_id = 4323; @@ -5693,7 +5837,6 @@ mod tests { let mut subject = neighborhood_from_nodes(&root_node, Some(&neighbor_node)); let persistent_config = PersistentConfigurationMock::new(); subject.persistent_config_opt = Some(Box::new(persistent_config)); - assert!(subject.gossip_acceptor_opt.is_none()); subject } @@ -5729,7 +5872,7 @@ mod tests { NeighborhoodDatabase, Vec, SocketAddr, - Vec, + NeighborhoodMetadata, )>, >, >, @@ -5742,13 +5885,13 @@ mod tests { database: &mut NeighborhoodDatabase, agrs: Vec, gossip_source: SocketAddr, - connection_progress_peers: &[IpAddr], + neighborhood_metadata: NeighborhoodMetadata, ) -> GossipAcceptanceResult { self.handle_params.lock().unwrap().push(( database.clone(), agrs, gossip_source, - connection_progress_peers.to_vec(), + neighborhood_metadata, )); self.handle_results.borrow_mut().remove(0) } @@ -5770,7 +5913,7 @@ mod tests { NeighborhoodDatabase, Vec, SocketAddr, - Vec, + NeighborhoodMetadata, )>, >, >, @@ -5855,6 +5998,7 @@ mod tests { initial_node_descriptors, rate_pack(100), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, test_name); @@ -5878,6 +6022,7 @@ mod tests { &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![make_node_descriptor(make_ip(1))]), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, make_wallet("earning"), None, @@ -5911,47 +6056,13 @@ mod tests { message_opt } - fn make_neighborhood_linearly_connected_with_nodes(hops: u16) -> Neighborhood { - let subject_node = make_global_cryptde_node_record(1234, true); - let relay1 = make_node_record(1111, true); - let mut nodes = vec![ - subject_node.public_key().clone(), - relay1.public_key().clone(), - ]; - let mut neighborhood: Neighborhood = neighborhood_from_nodes(&subject_node, Some(&relay1)); - let mut replacement_database = neighborhood.neighborhood_database.clone(); - - fn nonce(x: u16) -> u16 { - x + (10 * x) + (100 * x) + (1000 * x) - } - - for i in 1..=hops { - match i { - 1 => { - replacement_database.add_node(relay1.clone()).unwrap(); - } - i if i < hops => { - let relay_node = make_node_record(nonce(i), false); - replacement_database.add_node(relay_node.clone()).unwrap(); - nodes.push(relay_node.public_key().clone()); - } - i if i == hops => { - let exit_node = make_node_record(nonce(i), false); - replacement_database.add_node(exit_node.clone()).unwrap(); - nodes.push(exit_node.public_key().clone()); - } - _ => panic!("The match statement should be exhaustive."), - } - replacement_database - .add_arbitrary_full_neighbor(&nodes[i as usize - 1], &nodes[i as usize]); - } - - neighborhood.gossip_acceptor_opt = Some(Box::new(DatabaseReplacementGossipAcceptor { - replacement_database, - })); - neighborhood.persistent_config_opt = Some(Box::new( - PersistentConfigurationMock::new().set_past_neighbors_result(Ok(())), - )); + fn make_neighborhood_with_linearly_connected_nodes(nodes_count: u16) -> Neighborhood { + let root_node = make_global_cryptde_node_record(4242, true); + let mut nodes = make_node_records(nodes_count); + nodes[0] = root_node; + let db = linearly_connect_nodes(&nodes); + let mut neighborhood = neighborhood_from_nodes(db.root(), nodes.get(1)); + neighborhood.neighborhood_database = db; neighborhood } diff --git a/node/src/neighborhood/overall_connection_status.rs b/node/src/neighborhood/overall_connection_status.rs index 3a7e2cf2e..04358f076 100644 --- a/node/src/neighborhood/overall_connection_status.rs +++ b/node/src/neighborhood/overall_connection_status.rs @@ -118,7 +118,7 @@ impl ConnectionProgress { pub enum OverallConnectionStage { NotConnected = 0, ConnectedToNeighbor = 1, // When an Introduction or Standard Gossip (acceptance) is received - ThreeHopsRouteFound = 2, // Data can be relayed once this stage is reached + RouteFound = 2, // Data can be relayed once this stage is reached } impl From for UiConnectionStage { @@ -126,7 +126,7 @@ impl From for UiConnectionStage { match stage { OverallConnectionStage::NotConnected => UiConnectionStage::NotConnected, OverallConnectionStage::ConnectedToNeighbor => UiConnectionStage::ConnectedToNeighbor, - OverallConnectionStage::ThreeHopsRouteFound => UiConnectionStage::ThreeHopsRouteFound, + OverallConnectionStage::RouteFound => UiConnectionStage::RouteFound, } } } @@ -313,7 +313,7 @@ impl OverallConnectionStatus { } pub fn can_make_routes(&self) -> bool { - self.stage() == OverallConnectionStage::ThreeHopsRouteFound + self.stage() == OverallConnectionStage::RouteFound } pub fn stage(&self) -> OverallConnectionStage { @@ -408,7 +408,7 @@ mod tests { > OverallConnectionStage::NotConnected as usize ); assert!( - OverallConnectionStage::ThreeHopsRouteFound as usize + OverallConnectionStage::RouteFound as usize > OverallConnectionStage::ConnectedToNeighbor as usize ); } @@ -823,14 +823,11 @@ mod tests { #[test] fn converts_three_hops_route_found_stage_into_ui_connection_change_stage() { - let three_hops_route_found = OverallConnectionStage::ThreeHopsRouteFound; + let route_found = OverallConnectionStage::RouteFound; - let three_hops_route_found_converted: UiConnectionStage = three_hops_route_found.into(); + let route_found_converted: UiConnectionStage = route_found.into(); - assert_eq!( - three_hops_route_found_converted, - UiConnectionStage::ThreeHopsRouteFound - ); + assert_eq!(route_found_converted, UiConnectionStage::RouteFound); } #[test] @@ -848,7 +845,7 @@ mod tests { let mut subject = OverallConnectionStatus::new(vec![node_descriptor]); let initial_flag = subject.can_make_routes(); - subject.stage = OverallConnectionStage::ThreeHopsRouteFound; + subject.stage = OverallConnectionStage::RouteFound; let final_flag = subject.can_make_routes(); assert_eq!(initial_flag, false); @@ -860,7 +857,7 @@ mod tests { init_test_logging(); let test_name = "updates_the_ocs_stage_to_three_hops_route_found"; let initial_stage = OverallConnectionStage::NotConnected; - let new_stage = OverallConnectionStage::ThreeHopsRouteFound; + let new_stage = OverallConnectionStage::RouteFound; let (stage, message_opt) = assert_stage_and_node_to_ui_message(initial_stage, new_stage, test_name); @@ -935,7 +932,7 @@ mod tests { ) { init_test_logging(); let test_name = "doesn_t_send_a_message_to_ui_in_case_connection_drops_from_three_hops_to_connected_to_neighbor"; - let initial_stage = OverallConnectionStage::ThreeHopsRouteFound; + let initial_stage = OverallConnectionStage::RouteFound; let new_stage = OverallConnectionStage::ConnectedToNeighbor; let (stage, message_opt) = diff --git a/node/src/node_configurator/node_configurator_standard.rs b/node/src/node_configurator/node_configurator_standard.rs index b5700144b..82bbe40d3 100644 --- a/node/src/node_configurator/node_configurator_standard.rs +++ b/node/src/node_configurator/node_configurator_standard.rs @@ -252,7 +252,7 @@ fn configure_database( return Err(pce.into_configurator_error("clandestine-port")); } } - let neighborhood_mode_light = config.neighborhood_config.mode.make_light(); + let neighborhood_mode_light: NeighborhoodModeLight = (&config.neighborhood_config.mode).into(); if let Err(pce) = persistent_config.set_neighborhood_mode(neighborhood_mode_light) { return Err(pce.into_configurator_error("neighborhood-mode")); } @@ -284,7 +284,9 @@ mod tests { use crate::node_test_utils::DirsWrapperMock; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::neighborhood::NeighborhoodMode::ZeroHop; - use crate::sub_lib::neighborhood::{NeighborhoodConfig, NeighborhoodMode, NodeDescriptor}; + use crate::sub_lib::neighborhood::{ + Hops, NeighborhoodConfig, NeighborhoodMode, NodeDescriptor, + }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::unshared_test_utils::{ @@ -574,7 +576,8 @@ mod tests { assert_eq!( config.neighborhood_config, NeighborhoodConfig { - mode: NeighborhoodMode::ZeroHop // not populated on the privileged side + mode: NeighborhoodMode::ZeroHop, // not populated on the privileged side + min_hops_count: Hops::ThreeHops, } ); assert_eq!( diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 45d771c40..c4c640199 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -9,7 +9,7 @@ use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::neighborhood::{ - NeighborhoodConfig, NeighborhoodMode, NodeDescriptor, RatePack, + Hops, NeighborhoodConfig, NeighborhoodMode, NodeDescriptor, RatePack, }; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::wallet::Wallet; @@ -213,8 +213,15 @@ pub fn make_neighborhood_config( .get_past_neighbors(persistent_config, unprivileged_config)?, } }; + + let min_hops_count = value_m!(multi_config, "min-hops", Hops) + .expect("Clap schema didn't specified the default value."); + match make_neighborhood_mode(multi_config, neighbor_configs, persistent_config) { - Ok(mode) => Ok(NeighborhoodConfig { mode }), + Ok(mode) => Ok(NeighborhoodConfig { + mode, + min_hops_count, + }), Err(e) => Err(e), } } @@ -622,7 +629,7 @@ mod tests { use crate::db_config::persistent_configuration::PersistentConfigurationReal; use crate::sub_lib::accountant::DEFAULT_PAYMENT_THRESHOLDS; use crate::sub_lib::cryptde::{PlainData, PublicKey}; - use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; + use crate::sub_lib::neighborhood::{Hops, DEFAULT_RATE_PACK}; use crate::sub_lib::utils::make_new_multi_config; use crate::sub_lib::wallet::Wallet; use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; @@ -670,6 +677,7 @@ mod tests { vec![Box::new(CommandLineVcl::new( ArgsBuilder::new() .param("--neighborhood-mode", "standard") + .param("--min-hops", "1") .param("--ip", "1.2.3.4") .param( "--neighbors", @@ -705,11 +713,91 @@ mod tests { .unwrap() ], DEFAULT_RATE_PACK - ) + ), + min_hops_count: Hops::OneHop, }) ); } + #[test] + fn make_neighborhood_config_standard_missing_min_hops_count() { + running_test(); + let multi_config = make_new_multi_config( + &app_node(), + vec![Box::new(CommandLineVcl::new( + ArgsBuilder::new() + .param("--neighborhood-mode", "standard") + .param( + "--neighbors", + &format!("masq://{identifier}:QmlsbA@1.2.3.4:1234/2345,masq://{identifier}:VGVk@2.3.4.5:3456/4567",identifier = DEFAULT_CHAIN.rec().literal_identifier), + ) + .param("--fake-public-key", "booga") + .into(), + ))], + ) + .unwrap(); + + let result = make_neighborhood_config( + &UnprivilegedParseArgsConfigurationDaoReal {}, + &multi_config, + &mut configure_default_persistent_config(RATE_PACK), + &mut BootstrapperConfig::new(), + ); + + let min_hops_count = result.unwrap().min_hops_count; + assert_eq!(min_hops_count, Hops::ThreeHops); + } + + #[test] + fn make_neighborhood_config_standard_uses_default_value_when_no_min_hops_count_value_is_provided( + ) { + running_test(); + let args = ArgsBuilder::new() + .param("--neighborhood-mode", "standard") + .param( + "--neighbors", + &format!("masq://{identifier}:QmlsbA@1.2.3.4:1234/2345,masq://{identifier}:VGVk@2.3.4.5:3456/4567",identifier = DEFAULT_CHAIN.rec().literal_identifier), + ) + .param("--fake-public-key", "booga") + .opt("--min-hops"); + let vcl = CommandLineVcl::new(args.into()); + let multi_config = make_new_multi_config(&app_node(), vec![Box::new(vcl)]).unwrap(); + + let result = make_neighborhood_config( + &UnprivilegedParseArgsConfigurationDaoReal {}, + &multi_config, + &mut configure_default_persistent_config(RATE_PACK), + &mut BootstrapperConfig::new(), + ); + + let min_hops_count = result.unwrap().min_hops_count; + assert_eq!(min_hops_count, Hops::ThreeHops); + } + + #[test] + fn make_neighborhood_config_standard_throws_err_when_undesirable_min_hops_count_value_is_provided( + ) { + running_test(); + let args = ArgsBuilder::new() + .param("--neighborhood-mode", "standard") + .param( + "--neighbors", + &format!("masq://{identifier}:QmlsbA@1.2.3.4:1234/2345,masq://{identifier}:VGVk@2.3.4.5:3456/4567",identifier = DEFAULT_CHAIN.rec().literal_identifier), + ) + .param("--fake-public-key", "booga") + .param("--min-hops", "100"); + let vcl = CommandLineVcl::new(args.into()); + + let result = make_new_multi_config(&app_node(), vec![Box::new(vcl)]) + .err() + .unwrap(); + + assert_eq!( + result, + ConfiguratorError::required("min-hops", "Invalid value: '100'") + ); + } + #[test] fn make_neighborhood_config_standard_missing_ip() { running_test(); @@ -738,6 +826,7 @@ mod tests { let node_addr = match result { Ok(NeighborhoodConfig { mode: NeighborhoodMode::Standard(node_addr, _, _), + min_hops_count: Hops::ThreeHops, }) => node_addr, x => panic!("Wasn't expecting {:?}", x), }; @@ -770,32 +859,30 @@ mod tests { ); assert_eq!( - result, - Ok(NeighborhoodConfig { - mode: NeighborhoodMode::OriginateOnly( - vec![ - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:QmlsbA@1.2.3.4:1234/2345", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap(), - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:VGVk@2.3.4.5:3456/4567", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap() - ], - DEFAULT_RATE_PACK - ) - }) + result.unwrap().mode, + NeighborhoodMode::OriginateOnly( + vec![ + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:QmlsbA@1.2.3.4:1234/2345", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap(), + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:VGVk@2.3.4.5:3456/4567", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap() + ], + DEFAULT_RATE_PACK + ) ); } @@ -848,29 +935,27 @@ mod tests { ); assert_eq!( - result, - Ok(NeighborhoodConfig { - mode: NeighborhoodMode::ConsumeOnly(vec![ - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:QmlsbA@1.2.3.4:1234/2345", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap(), - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:VGVk@2.3.4.5:3456/4567", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap() - ],) - }) + result.unwrap().mode, + NeighborhoodMode::ConsumeOnly(vec![ + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:QmlsbA@1.2.3.4:1234/2345", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap(), + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:VGVk@2.3.4.5:3456/4567", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap() + ],), ); } @@ -929,12 +1014,7 @@ mod tests { &mut BootstrapperConfig::new(), ); - assert_eq!( - result, - Ok(NeighborhoodConfig { - mode: NeighborhoodMode::ZeroHop - }) - ); + assert_eq!(result.unwrap().mode, NeighborhoodMode::ZeroHop); } #[test] @@ -1259,12 +1339,7 @@ mod tests { ) .unwrap(); - assert_eq!( - config.neighborhood_config, - NeighborhoodConfig { - mode: NeighborhoodMode::ZeroHop - } - ); + assert_eq!(config.neighborhood_config.mode, NeighborhoodMode::ZeroHop); let set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); assert_eq!( *set_past_neighbors_params, @@ -1305,12 +1380,7 @@ mod tests { ) .unwrap(); - assert_eq!( - config.neighborhood_config, - NeighborhoodConfig { - mode: NeighborhoodMode::ZeroHop - } - ); + assert_eq!(config.neighborhood_config.mode, NeighborhoodMode::ZeroHop); let set_past_neighbors_params = set_past_neighbors_params_arc.lock().unwrap(); assert!(set_past_neighbors_params.is_empty()) } @@ -1464,33 +1534,31 @@ mod tests { )), ); assert_eq!( - config.neighborhood_config, - NeighborhoodConfig { - mode: NeighborhoodMode::Standard( - NodeAddr::new(&IpAddr::from_str("34.56.78.90").unwrap(), &[]), - vec![ - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:QmlsbA@1.2.3.4:1234/2345", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap(), - NodeDescriptor::try_from(( - main_cryptde(), - format!( - "masq://{}:VGVk@2.3.4.5:3456/4567", - DEFAULT_CHAIN.rec().literal_identifier - ) - .as_str() - )) - .unwrap(), - ], - DEFAULT_RATE_PACK.clone() - ) - } + config.neighborhood_config.mode, + NeighborhoodMode::Standard( + NodeAddr::new(&IpAddr::from_str("34.56.78.90").unwrap(), &[]), + vec![ + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:QmlsbA@1.2.3.4:1234/2345", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap(), + NodeDescriptor::try_from(( + main_cryptde(), + format!( + "masq://{}:VGVk@2.3.4.5:3456/4567", + DEFAULT_CHAIN.rec().literal_identifier + ) + .as_str() + )) + .unwrap(), + ], + DEFAULT_RATE_PACK.clone() + ) ); assert_eq!(config.db_password_opt, Some(password.to_string())); assert_eq!(config.mapping_protocol_opt, Some(AutomapProtocol::Pcp)); diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 2e0dd4701..0076b555e 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -33,9 +33,7 @@ use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; use crate::sub_lib::proxy_server::ProxyServerSubs; -use crate::sub_lib::proxy_server::{ - AddReturnRouteMessage, AddRouteMessage, DEFAULT_MINIMUM_HOP_COUNT, -}; +use crate::sub_lib::proxy_server::{AddReturnRouteMessage, AddRouteMessage}; use crate::sub_lib::route::Route; use crate::sub_lib::set_consuming_wallet_message::SetConsumingWalletMessage; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; @@ -952,11 +950,6 @@ impl IBCDHelperReal { route_source .send(RouteQueryMessage::data_indefinite_route_request( hostname_opt, - if common_args.is_decentralized { - DEFAULT_MINIMUM_HOP_COUNT - } else { - 0 - }, payload_size, )) .then(move |route_result| { @@ -1290,11 +1283,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Some("nowhere.com".to_string()), - DEFAULT_MINIMUM_HOP_COUNT, - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); let recording = proxy_server_recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); @@ -1416,7 +1405,6 @@ mod tests { neighborhood_record, &RouteQueryMessage::data_indefinite_route_request( Some("realdomain.nu".to_string()), - DEFAULT_MINIMUM_HOP_COUNT, 12 ) ); @@ -1813,7 +1801,6 @@ mod tests { &RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count: 0, return_component_opt: Some(Component::ProxyServer), payload_size: 47, hostname_opt: Some("nowhere.com".to_string()) @@ -1894,7 +1881,6 @@ mod tests { &RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count: 0, return_component_opt: Some(Component::ProxyServer), payload_size: 16, hostname_opt: None @@ -2207,11 +2193,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Some("nowhere.com".to_string()), - 3, - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); } @@ -2723,11 +2705,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Some("nowhere.com".to_string()), - 3, - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); TestLogHandler::new() .exists_log_containing("ERROR: ProxyServer: Failed to find route to nowhere.com"); @@ -2899,11 +2877,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Some("nowhere.com".to_string()), - 3, - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); TestLogHandler::new() .exists_log_containing("ERROR: ProxyServer: Failed to find route to nowhere.com"); diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 4a1b7f78f..be7e742a3 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -87,6 +87,18 @@ impl Display for NeighborhoodMode { } } +#[allow(clippy::from_over_into)] +impl Into for &NeighborhoodMode { + fn into(self) -> NeighborhoodModeLight { + match self { + NeighborhoodMode::Standard(_, _, _) => NeighborhoodModeLight::Standard, + NeighborhoodMode::ConsumeOnly(_) => NeighborhoodModeLight::ConsumeOnly, + NeighborhoodMode::OriginateOnly(_, _) => NeighborhoodModeLight::OriginateOnly, + NeighborhoodMode::ZeroHop => NeighborhoodModeLight::ZeroHop, + } + } +} + impl NeighborhoodMode { pub fn is_decentralized(&self) -> bool { self != &NeighborhoodMode::ZeroHop @@ -142,15 +154,6 @@ impl NeighborhoodMode { pub fn is_zero_hop(&self) -> bool { matches!(self, NeighborhoodMode::ZeroHop) } - - pub fn make_light(&self) -> NeighborhoodModeLight { - match self { - NeighborhoodMode::Standard(_, _, _) => NeighborhoodModeLight::Standard, - NeighborhoodMode::ConsumeOnly(_) => NeighborhoodModeLight::ConsumeOnly, - NeighborhoodMode::OriginateOnly(_, _) => NeighborhoodModeLight::OriginateOnly, - NeighborhoodMode::ZeroHop => NeighborhoodModeLight::ZeroHop, - } - } } //TODO we could write our own impl of serde for Chain in order to optimize @@ -367,9 +370,36 @@ impl Display for DescriptorParsingError<'_> { } } +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Hops { + OneHop = 1, + TwoHops = 2, + ThreeHops = 3, // minimum for anonymity + FourHops = 4, + FiveHops = 5, + SixHops = 6, +} + +impl FromStr for Hops { + type Err = String; + + fn from_str(value: &str) -> Result { + match value { + "1" => Ok(Hops::OneHop), + "2" => Ok(Hops::TwoHops), + "3" => Ok(Hops::ThreeHops), + "4" => Ok(Hops::FourHops), + "5" => Ok(Hops::FiveHops), + "6" => Ok(Hops::SixHops), + _ => Err("Invalid value for min hops count provided".to_string()), + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub struct NeighborhoodConfig { pub mode: NeighborhoodMode, + pub min_hops_count: Hops, } lazy_static! { @@ -443,7 +473,6 @@ pub struct DispatcherNodeQueryMessage { pub struct RouteQueryMessage { pub target_key_opt: Option, pub target_component: Component, - pub minimum_hop_count: usize, pub return_component_opt: Option, pub payload_size: usize, pub hostname_opt: Option, @@ -456,13 +485,11 @@ impl Message for RouteQueryMessage { impl RouteQueryMessage { pub fn data_indefinite_route_request( hostname_opt: Option, - minimum_hop_count: usize, payload_size: usize, ) -> RouteQueryMessage { RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count, return_component_opt: Some(Component::ProxyServer), payload_size, hostname_opt, @@ -550,6 +577,13 @@ impl fmt::Display for GossipFailure_0v1 { } } +// This metadata is only passed from Neighborhood to GossipHandler +pub struct NeighborhoodMetadata { + pub connection_progress_peers: Vec, + pub cpm_recipient: Recipient, + pub min_hops_count: Hops, +} + pub struct NeighborhoodTools { pub notify_later_ask_about_gossip: Box>, @@ -1003,14 +1037,13 @@ mod tests { #[test] fn data_indefinite_route_request() { - let result = RouteQueryMessage::data_indefinite_route_request(None, 2, 7500); + let result = RouteQueryMessage::data_indefinite_route_request(None, 7500); assert_eq!( result, RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, - minimum_hop_count: 2, return_component_opt: Some(Component::ProxyServer), payload_size: 7500, hostname_opt: None @@ -1186,7 +1219,7 @@ mod tests { #[test] fn neighborhood_mode_light_can_be_made_from_neighborhood_mode() { assert_make_light( - NeighborhoodMode::Standard( + &NeighborhoodMode::Standard( NodeAddr::new(&localhost(), &[1234, 2345]), vec![], rate_pack(100), @@ -1194,18 +1227,19 @@ mod tests { NeighborhoodModeLight::Standard, ); assert_make_light( - NeighborhoodMode::ConsumeOnly(vec![]), + &NeighborhoodMode::ConsumeOnly(vec![]), NeighborhoodModeLight::ConsumeOnly, ); assert_make_light( - NeighborhoodMode::OriginateOnly(vec![], rate_pack(100)), + &NeighborhoodMode::OriginateOnly(vec![], rate_pack(100)), NeighborhoodModeLight::OriginateOnly, ); - assert_make_light(NeighborhoodMode::ZeroHop, NeighborhoodModeLight::ZeroHop) + assert_make_light(&NeighborhoodMode::ZeroHop, NeighborhoodModeLight::ZeroHop) } - fn assert_make_light(heavy: NeighborhoodMode, expected_value: NeighborhoodModeLight) { - assert_eq!(heavy.make_light(), expected_value) + fn assert_make_light(heavy: &NeighborhoodMode, expected_value: NeighborhoodModeLight) { + let result: NeighborhoodModeLight = heavy.into(); + assert_eq!(result, expected_value) } #[test] @@ -1218,4 +1252,21 @@ mod tests { .unwrap(); assert_eq!(subject.ask_about_gossip_interval, Duration::from_secs(10)); } + + #[test] + fn min_hops_count_can_be_converted_from_str() { + let result = Hops::from_str("6").unwrap(); + + assert_eq!(result, Hops::SixHops); + } + + #[test] + fn min_hops_count_conversion_from_str_returns_error() { + let result = Hops::from_str("100"); + + assert_eq!( + result, + Err("Invalid value for min hops count provided".to_string()) + ) + } } diff --git a/node/src/sub_lib/proxy_server.rs b/node/src/sub_lib/proxy_server.rs index 8c69e6878..5d3b242ae 100644 --- a/node/src/sub_lib/proxy_server.rs +++ b/node/src/sub_lib/proxy_server.rs @@ -96,11 +96,6 @@ mod tests { use crate::test_utils::recorder::Recorder; use actix::Actor; - #[test] - fn constants_have_correct_values() { - assert_eq!(DEFAULT_MINIMUM_HOP_COUNT, 3); - } - #[test] fn proxy_server_subs_debug() { let recorder = Recorder::new().start(); diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index c8c5250ce..e85ccc4cf 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -1,13 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::bootstrapper::BootstrapperConfig; -use crate::neighborhood::gossip::GossipNodeRecord; +use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord, Gossip_0v1}; use crate::neighborhood::neighborhood_database::NeighborhoodDatabase; use crate::neighborhood::node_record::{NodeRecord, NodeRecordInner_0v1}; -use crate::neighborhood::{AccessibleGossipRecord, Neighborhood}; +use crate::neighborhood::{AccessibleGossipRecord, Neighborhood, DEFAULT_MIN_HOPS_COUNT}; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::cryptde::{CryptDE, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; -use crate::sub_lib::neighborhood::{NeighborhoodConfig, NeighborhoodMode, NodeDescriptor}; +use crate::sub_lib::neighborhood::{Hops, NeighborhoodConfig, NeighborhoodMode, NodeDescriptor}; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::wallet::Wallet; use crate::test_utils::*; @@ -18,6 +18,8 @@ use std::convert::TryFrom; use std::net::IpAddr; use std::net::Ipv4Addr; +pub const MIN_HOPS_COUNT_FOR_TEST: Hops = DEFAULT_MIN_HOPS_COUNT; + impl From<(&NeighborhoodDatabase, &PublicKey, bool)> for AccessibleGossipRecord { fn from( (database, public_key, reveal_node_addr): (&NeighborhoodDatabase, &PublicKey, bool), @@ -95,9 +97,11 @@ pub fn neighborhood_from_nodes( vec![NodeDescriptor::from((neighbor, Chain::EthRopsten, cryptde))], *root.rate_pack(), ), + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, None => NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, + min_hops_count: MIN_HOPS_COUNT_FOR_TEST, }, }; config.earning_wallet = root.earning_wallet(); @@ -293,3 +297,48 @@ pub fn make_node(nonce: u8) -> (IpAddr, NodeDescriptor) { (ip_addr, node_descriptor) } + +pub fn make_node_records(count: u16) -> Vec { + (1..=count) + .into_iter() + .map(|i| make_node_record(i, true)) + .collect::>() +} + +pub fn linearly_connect_nodes(nodes: &[NodeRecord]) -> NeighborhoodDatabase { + let root_node = nodes.first().unwrap(); + let mut database = db_from_node(root_node); + let nodes_count = nodes.len(); + for i in 1..nodes_count { + database.add_node(nodes[i].clone()).unwrap(); + database.add_arbitrary_full_neighbor(nodes[i - 1].public_key(), nodes[i].public_key()); + } + + database +} + +pub fn gossip_about_nodes_from_database( + database: &NeighborhoodDatabase, + nodes: &[NodeRecord], +) -> Gossip_0v1 { + nodes + .iter() + .fold(GossipBuilder::new(database), |builder, node| { + builder.node(node.public_key(), false) + }) + .build() +} + +pub fn public_keys_from_node_records(nodes: &[NodeRecord]) -> HashSet { + nodes + .iter() + .map(|node| node.public_key().clone()) + .collect::>() +} + +pub fn cryptdes_from_node_records(nodes: &[NodeRecord]) -> Vec { + nodes + .iter() + .map(|node| CryptDENull::from(node.public_key(), TEST_DEFAULT_CHAIN)) + .collect::>() +}