From 443a398cd1236cc9f7a7d81e056f0e2eef18615a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojte=CC=8Cch=20Parka=CC=81n?= Date: Thu, 27 Feb 2025 19:49:22 +0100 Subject: [PATCH 01/27] working on exit_location_command --- ip_country/src/ip_country.rs | 5 +-- masq/src/commands/exit_location_command.rs | 52 +++++++++++++--------- 2 files changed, 32 insertions(+), 25 deletions(-) diff --git a/ip_country/src/ip_country.rs b/ip_country/src/ip_country.rs index e2fb0651b..24fe9f83a 100644 --- a/ip_country/src/ip_country.rs +++ b/ip_country/src/ip_country.rs @@ -7,7 +7,6 @@ use std::io; const COUNTRY_BLOCK_BIT_SIZE: usize = 64; -#[allow(unused_must_use)] pub fn ip_country( _args: Vec, stdin: &mut dyn io::Read, @@ -52,8 +51,8 @@ pub fn ip_country( *** DO NOT USE THIS CODE *** "#, error_list - ); - write!(stderr, "{}", error_list); + ).expect("expected WANRNING output"); + write!(stderr, "{}", error_list).expect("expected error list output"); 1 } } diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index 15cb329f6..5a961303b 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -5,7 +5,7 @@ use crate::commands::commands_common::CommandError::Payload; use crate::commands::commands_common::{ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; -use clap::{App, Arg, SubCommand}; +use clap::{App, Arg, ArgGroup, SubCommand}; use masq_lib::constants::{EXIT_COUNTRY_ERROR, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR}; use masq_lib::messages::{ CountryCodes, ExitLocationSet, UiSetExitLocationRequest, UiSetExitLocationResponse, @@ -40,7 +40,7 @@ const FALLBACK_ROUTING_HELP: &str = "If you just want to make a suggestion, and masq> exit-location --country-codes \"CZ\" --fallback-routing \n\t// Set exit-location for \"CZ\" country with fallback-routing on \n\ masq> exit-location --country-codes \"CZ\" \n\t// Set exit-location for \"CZ\" country with fallback-routing off \n\n"; -const SHOW_COUNTRIES_HELP: &str = "To display all country codes available for exit in Database, use this flag without anything else: \n\n\ +const SHOW_COUNTRIES_HELP: &str = "Use this flag to display all country codes available for exit Nodes in your Neighborhood: \n\n\ masq> exit-location --show-countries "; pub fn exit_location_subcommand() -> App<'static, 'static> { @@ -111,10 +111,11 @@ impl Command for SetExitLocationCommand { "No countries available for exit-location!" ), } - } - match exit_location_response.fallback_routing { - true => short_writeln!(context.stdout(), "Fallback Routing is set.",), - false => short_writeln!(context.stdout(), "Fallback Routing NOT set.",), + } else { + match exit_location_response.fallback_routing { + true => short_writeln!(context.stdout(), "Fallback Routing is set.",), + false => short_writeln!(context.stdout(), "Fallback Routing NOT set.",), + } } if !exit_location_response.exit_locations.is_empty() { let location_set = ExitLocationSet { @@ -194,6 +195,10 @@ pub fn set_exit_location_subcommand() -> App<'static, 'static> { .takes_value(false) .required(false), ) + .group(ArgGroup::with_name("show-countries-fallback") + .args(&["show-countries", "fallback-routing"])) + .group(ArgGroup::with_name("show-countries-ccodes") + .args(&["show-countries", "country-codes"])) } #[cfg(test)] @@ -246,7 +251,7 @@ pub mod tests { ); assert_eq!( SHOW_COUNTRIES_HELP, - "To display all country codes available for exit in Database, use this flag without anything else: \n\n\ + "Use this flag to display all country codes available for exit Nodes in your Neighborhood: \n\n\ masq> exit-location --show-countries " ); } @@ -303,33 +308,36 @@ pub mod tests { #[test] fn testing_handler_for_exit_location_responose() { let message_body = Ok(UiSetExitLocationResponse { - fallback_routing: false, + fallback_routing: true, exit_locations: vec![ - ExitLocation { - country_codes: vec!["CZ".to_string()], - priority: 1, - }, - ExitLocation { - country_codes: vec!["FR".to_string()], - priority: 2, - }, + // ExitLocation { + // country_codes: vec!["CZ".to_string()], + // priority: 1, + // }, + // ExitLocation { + // country_codes: vec!["FR".to_string()], + // priority: 2, + // }, ], - exit_countries: Some(vec!["FR".to_string()]), - missing_countries: vec!["CZ".to_string()], + exit_countries: None, //Some(vec!["FR".to_string()]), + missing_countries: vec![], // vec!["CZ".to_string()], } .tmb(1234)); let factory = CommandFactoryReal::new(); let mut context = CommandContextMock::new().transact_result(message_body); - let subject = factory.make(&["exit-location".to_string()]).unwrap(); + let subject = factory.make(&[ + "exit-location".to_string(), + "--fallback-routing".to_string() + ]).unwrap(); let result = subject.execute(&mut context); let stderr = context.stderr_arc(); let stdout = context.stdout_arc(); - assert_eq!(stdout.lock().unwrap().get_string(), "Countries available for exit-location: [\"FR\"]\nFallback Routing NOT set.\nFollowing countries are missing in Database: [\"CZ\"]\nExit location set: Country Codes: [\"CZ\"] - Priority: 1; Country Codes: [\"FR\"] - Priority: 2; \n".to_string()); + assert_eq!(stdout.lock().unwrap().get_string(), "Fallback Routing is set.\nExit location is Unset.\n".to_string()); assert_eq!( stderr.lock().unwrap().get_string(), - "code: 9223372036854775817\nmessage: [\"CZ\"]\n".to_string() + "".to_string() ); assert_eq!(result, Ok(())); } @@ -527,7 +535,7 @@ pub mod tests { let stdout = stdout_arc.lock().unwrap(); assert_eq!( &stdout.get_string(), - "Countries available for exit-location: [\"CZ\"]\nFallback Routing NOT set.\n" + "Countries available for exit-location: [\"CZ\"]\n" ); } } From 849214c6975e4841c212a750308ab67cb42438d4 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 4 Mar 2025 18:22:13 +0100 Subject: [PATCH 02/27] addressing issues from review --- masq/src/commands/exit_location_command.rs | 129 ++++++------------ masq_lib/src/constants.rs | 3 +- masq_lib/src/exit_locations.rs | 21 +++ masq_lib/src/lib.rs | 1 + masq_lib/src/messages.rs | 25 +--- masq_lib/src/shared_schema.rs | 14 +- .../src/masq_mock_node.rs | 4 + multinode_integration_tests/src/masq_node.rs | 11 ++ .../src/masq_real_node.rs | 5 + multinode_integration_tests/src/utils.rs | 7 +- node/src/neighborhood/mod.rs | 6 +- 11 files changed, 97 insertions(+), 129 deletions(-) create mode 100644 masq_lib/src/exit_locations.rs diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index 5a961303b..429a0664d 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -6,9 +6,10 @@ use crate::commands::commands_common::{ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use clap::{App, Arg, ArgGroup, SubCommand}; -use masq_lib::constants::{EXIT_COUNTRY_ERROR, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR}; +use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; +use masq_lib::exit_locations::ExitLocationSet; use masq_lib::messages::{ - CountryCodes, ExitLocationSet, UiSetExitLocationRequest, UiSetExitLocationResponse, + CountryCodes, UiSetExitLocationRequest, UiSetExitLocationResponse, }; use masq_lib::shared_schema::common_validators; use masq_lib::{as_any_ref_in_trait_impl, short_writeln}; @@ -99,7 +100,7 @@ impl Command for SetExitLocationCommand { transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS); match output { Ok(exit_location_response) => { - if let Some(exit_countries) = exit_location_response.exit_countries { + if let Some(exit_countries) = exit_location_response.exit_country_selection { match !exit_countries.is_empty() { true => short_writeln!( context.stdout(), @@ -117,9 +118,9 @@ impl Command for SetExitLocationCommand { false => short_writeln!(context.stdout(), "Fallback Routing NOT set.",), } } - if !exit_location_response.exit_locations.is_empty() { + if !exit_location_response.exit_country_selection.is_empty() { let location_set = ExitLocationSet { - locations: exit_location_response.exit_locations, + locations: exit_location_response.exit_country_selection, }; if !exit_location_response.missing_countries.is_empty() { short_writeln!( @@ -136,16 +137,12 @@ impl Command for SetExitLocationCommand { } short_writeln!(context.stdout(), "Exit location set: {}", location_set); } else if exit_location_response.fallback_routing - && exit_location_response.exit_locations.is_empty() + && exit_location_response.exit_country_selection.is_empty() { short_writeln!(context.stdout(), "Exit location is Unset."); } Ok(()) } - Err(Payload(code, message)) if code == EXIT_COUNTRY_ERROR => { - short_writeln!(context.stderr(), "Error: Something went wrong!"); - Err(Payload(code, message)) - } Err(Payload(code, message)) => { short_writeln!(context.stderr(), "code: {}\nmessage: {}", code, message); if code == EXIT_COUNTRY_MISSING_COUNTRIES_ERROR { @@ -195,10 +192,13 @@ pub fn set_exit_location_subcommand() -> App<'static, 'static> { .takes_value(false) .required(false), ) - .group(ArgGroup::with_name("show-countries-fallback") - .args(&["show-countries", "fallback-routing"])) - .group(ArgGroup::with_name("show-countries-ccodes") - .args(&["show-countries", "country-codes"])) + .group( + ArgGroup::with_name("show-countries-fallback") + .args(&["show-countries", "fallback-routing"]), + ) + .group( + ArgGroup::with_name("show-countries-ccodes").args(&["show-countries", "country-codes"]), + ) } #[cfg(test)] @@ -210,7 +210,7 @@ pub mod tests { Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS, }; use crate::test_utils::mocks::CommandContextMock; - use masq_lib::constants::{EXIT_COUNTRY_ERROR, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR}; + use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; use masq_lib::messages::{ CountryCodes, ExitLocation, ToMessageBody, UiSetExitLocationRequest, UiSetExitLocationResponse, @@ -275,70 +275,44 @@ pub mod tests { ); assert_eq!( stderr.lock().unwrap().get_string(), - "code: 9223372036854775817\nmessage: \"CZ, SK, IN\"\n".to_string() + "code: 9223372036854775816\nmessage: \"CZ, SK, IN\"\n".to_string() ); assert_eq!( result, Err(CommandError::Payload( - 9223372036854775817, + 9223372036854775816, "\"CZ, SK, IN\"".to_string() )) ); } - #[test] - fn testing_exit_location_general_error() { - let factory = CommandFactoryReal::new(); - let mut context = CommandContextMock::new().transact_result(Err( - ContextError::PayloadError(EXIT_COUNTRY_ERROR, "".to_string()), - )); - let subject = factory.make(&["exit-location".to_string()]).unwrap(); - - let result = subject.execute(&mut context); - let stderr = context.stderr_arc(); - let stdout = context.stdout_arc(); - assert!(stdout.lock().unwrap().get_string().is_empty()); - assert_eq!( - stderr.lock().unwrap().get_string(), - "Error: Something went wrong!\n".to_string() - ); - assert_eq!(result, Err(Payload(9223372036854775816, "".to_string()))); - } - #[test] fn testing_handler_for_exit_location_responose() { let message_body = Ok(UiSetExitLocationResponse { fallback_routing: true, - exit_locations: vec![ - // ExitLocation { - // country_codes: vec!["CZ".to_string()], - // priority: 1, - // }, - // ExitLocation { - // country_codes: vec!["FR".to_string()], - // priority: 2, - // }, - ], - exit_countries: None, //Some(vec!["FR".to_string()]), + exit_country_selection: vec![], + exit_countries: None, //Some(vec!["FR".to_string()]), missing_countries: vec![], // vec!["CZ".to_string()], } .tmb(1234)); let factory = CommandFactoryReal::new(); let mut context = CommandContextMock::new().transact_result(message_body); - let subject = factory.make(&[ - "exit-location".to_string(), - "--fallback-routing".to_string() - ]).unwrap(); + let subject = factory + .make(&[ + "exit-location".to_string(), + "--fallback-routing".to_string(), + ]) + .unwrap(); let result = subject.execute(&mut context); let stderr = context.stderr_arc(); let stdout = context.stdout_arc(); - assert_eq!(stdout.lock().unwrap().get_string(), "Fallback Routing is set.\nExit location is Unset.\n".to_string()); assert_eq!( - stderr.lock().unwrap().get_string(), - "".to_string() + stdout.lock().unwrap().get_string(), + "Fallback Routing is set.\nExit location is Unset.\n".to_string() ); + assert_eq!(stderr.lock().unwrap().get_string(), "".to_string()); assert_eq!(result, Ok(())); } @@ -349,7 +323,7 @@ pub mod tests { .transact_params(&transact_params_arc) .transact_result(Ok(UiSetExitLocationResponse { fallback_routing: true, - exit_locations: vec![ + exit_country_selection: vec![ ExitLocation { country_codes: vec!["CZ".to_string(), "SK".to_string()], priority: 1, @@ -417,7 +391,7 @@ pub mod tests { .transact_params(&transact_params_arc) .transact_result(Ok(UiSetExitLocationResponse { fallback_routing: false, - exit_locations: vec![ExitLocation { + exit_country_selection: vec![ExitLocation { country_codes: vec!["CZ".to_string()], priority: 1, }], @@ -459,51 +433,26 @@ pub mod tests { #[test] fn providing_no_arguments_cause_exit_location_reset_request() { - let transact_params_arc = Arc::new(Mutex::new(vec![])); - let mut context = CommandContextMock::new() - .transact_params(&transact_params_arc) - .transact_result(Ok(UiSetExitLocationResponse { - fallback_routing: true, - exit_locations: vec![], - exit_countries: None, - missing_countries: vec![], - } - .tmb(0))); - let stderr_arc = context.stderr_arc(); - let stdout_arc = context.stdout_arc(); - let subject = SetExitLocationCommand::new(&["exit-location".to_string()]).unwrap(); - - let result = subject.execute(&mut context); + let result = SetExitLocationCommand::new(&["exit-location".to_string()]).unwrap(); - assert_eq!(result, Ok(())); - let expected_request = UiSetExitLocationRequest { - fallback_routing: true, - exit_locations: vec![], - show_countries: false, - }; - let transact_params = transact_params_arc.lock().unwrap(); - let expected_message_body = expected_request.tmb(0); - assert_eq!( - transact_params.as_slice(), - &[(expected_message_body, STANDARD_COMMAND_TIMEOUT_MILLIS)] - ); - let stderr = stderr_arc.lock().unwrap(); - assert_eq!(&stderr.get_string(), ""); - let stdout = stdout_arc.lock().unwrap(); assert_eq!( - &stdout.get_string(), - "Fallback Routing is set.\nExit location is Unset.\n" + result, + SetExitLocationCommand { + exit_locations: vec![], + fallback_routing: true, + show_countries: false + } ); } #[test] - fn providing_show_countries_cause_request_for_list_of_countries() { + fn providing_show_countries_flag_cause_request_for_list_of_countries() { let transact_params_arc = Arc::new(Mutex::new(vec![])); let mut context = CommandContextMock::new() .transact_params(&transact_params_arc) .transact_result(Ok(UiSetExitLocationResponse { fallback_routing: false, - exit_locations: vec![], + exit_country_selection: vec![], exit_countries: Some(vec!["CZ".to_string()]), missing_countries: vec![], } diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index a073232b8..1d8f5ac21 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -75,8 +75,7 @@ pub const UNMARSHAL_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 4; pub const SETUP_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 5; pub const TIMEOUT_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 6; pub const SCAN_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 7; -pub const EXIT_COUNTRY_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 8; -pub const EXIT_COUNTRY_MISSING_COUNTRIES_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 9; +pub const EXIT_COUNTRY_MISSING_COUNTRIES_ERROR: u64 = UI_NODE_COMMUNICATION_PREFIX | 8; //accountant pub const ACCOUNTANT_PREFIX: u64 = 0x0040_0000_0000_0000; diff --git a/masq_lib/src/exit_locations.rs b/masq_lib/src/exit_locations.rs new file mode 100644 index 000000000..c763f5cd5 --- /dev/null +++ b/masq_lib/src/exit_locations.rs @@ -0,0 +1,21 @@ +// Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use std::fmt::{Display, Formatter}; +use crate::messages::ExitLocation; + +pub struct ExitLocationSet { + pub locations: Vec, +} + +impl Display for ExitLocationSet { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + for exit_location in self.locations.iter() { + write!( + f, + "Country Codes: {:?} - Priority: {}; ", + exit_location.country_codes, exit_location.priority + )?; + } + Ok(()) + } +} \ No newline at end of file diff --git a/masq_lib/src/lib.rs b/masq_lib/src/lib.rs index f8e72a956..1fc5eb68d 100644 --- a/masq_lib/src/lib.rs +++ b/masq_lib/src/lib.rs @@ -22,6 +22,7 @@ pub mod command; pub mod constants; pub mod crash_point; pub mod data_version; +pub mod exit_locations; pub mod shared_schema; pub mod test_utils; pub mod type_obfuscation; diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index 86d8b083b..426b95f7e 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -852,14 +852,13 @@ conversation_message!(UiWalletAddressesResponse, "walletAddresses"); pub struct CountryCodes { #[serde(rename = "countryCodes")] pub country_codes: Vec, - #[serde(rename = "priority")] pub priority: usize, } impl From<(String, usize)> for CountryCodes { - fn from((item, priority): (String, usize)) -> Self { + fn from((country, priority): (String, usize)) -> Self { CountryCodes { - country_codes: item + country_codes: country .split(',') .into_iter() .map(|x| x.to_string()) @@ -882,6 +881,7 @@ conversation_message!(UiSetExitLocationRequest, "exitLocation"); #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct ExitLocation { + #[serde(rename = "countryCodes")] pub country_codes: Vec, pub priority: usize, } @@ -896,29 +896,12 @@ impl Display for ExitLocation { } } -pub struct ExitLocationSet { - pub locations: Vec, -} - -impl Display for ExitLocationSet { - fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for exit_location in self.locations.iter() { - write!( - f, - "Country Codes: {:?} - Priority: {}; ", - exit_location.country_codes, exit_location.priority - )?; - } - Ok(()) - } -} - #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct UiSetExitLocationResponse { #[serde(rename = "fallbackRouting")] pub fallback_routing: bool, #[serde(rename = "exitCountrySelection")] - pub exit_locations: Vec, + pub exit_country_selection: Vec, #[serde(rename = "exitCountries")] pub exit_countries: Option>, #[serde(rename = "missingCountries")] diff --git a/masq_lib/src/shared_schema.rs b/masq_lib/src/shared_schema.rs index 9bd948e8d..46c01fe14 100644 --- a/masq_lib/src/shared_schema.rs +++ b/masq_lib/src/shared_schema.rs @@ -537,16 +537,16 @@ pub mod common_validators { pub fn validate_exit_locations(exit_location: String) -> Result<(), String> { validate_pipe_separated_values(exit_location, |country: String| { - let mut collect_fails = "".to_string(); + let mut collect_fails = vec![]; country.split(',').into_iter().for_each(|country_code| { match validate_country_code(country_code) { Ok(_) => (), - Err(e) => collect_fails.push_str(&e), + Err(e) => collect_fails.push(e), } }); match collect_fails.is_empty() { true => Ok(()), - false => Err(collect_fails), + false => Err(collect_fails.join(", ")), } }) } @@ -651,14 +651,14 @@ pub mod common_validators { fn validate_pipe_separated_values( values_with_delimiters: String, - closure: fn(String) -> Result<(), String>, + parse_value: fn(String) -> Result<(), String>, ) -> Result<(), String> { let mut error_collection = vec![]; values_with_delimiters .split('|') .into_iter() .for_each(|segment| { - match closure(segment.to_string()) { + match parse_value(segment.to_string()) { Ok(_) => (), Err(msg) => error_collection.push(msg), }; @@ -980,11 +980,11 @@ mod tests { #[test] fn validate_exit_key_fails_on_not_valid_country_code() { - let result = common_validators::validate_exit_locations(String::from("CZ|SK,RR")); + let result = common_validators::validate_exit_locations(String::from("CZ|SK,RR,XP")); assert_eq!( result, - Err("'RR' is not a valid ISO3166 country code".to_string()) + Err("'RR' is not a valid ISO3166 country code, 'XP' is not a valid ISO3166 country code".to_string()) ); } diff --git a/multinode_integration_tests/src/masq_mock_node.rs b/multinode_integration_tests/src/masq_mock_node.rs index 801c694d1..384209e20 100644 --- a/multinode_integration_tests/src/masq_mock_node.rs +++ b/multinode_integration_tests/src/masq_mock_node.rs @@ -147,6 +147,10 @@ impl MASQNode for MASQMockNode { fn routes_data(&self) -> bool { true // just a guess } + + fn country_code_opt(&self) -> Option { + None + } } pub struct MutableMASQMockNode { diff --git a/multinode_integration_tests/src/masq_node.rs b/multinode_integration_tests/src/masq_node.rs index 1e2528ed8..5ea782ef9 100644 --- a/multinode_integration_tests/src/masq_node.rs +++ b/multinode_integration_tests/src/masq_node.rs @@ -26,6 +26,7 @@ use std::str::FromStr; use std::thread; use std::time::Duration; use std::time::Instant; +use node_lib::neighborhood::node_location::get_node_location; #[derive(PartialEq, Eq, Clone, Debug)] pub struct NodeReference { @@ -210,6 +211,7 @@ pub trait MASQNode: Any { fn chain(&self) -> Chain; fn accepts_connections(&self) -> bool; fn routes_data(&self) -> bool; + fn country_code_opt(&self) -> Option; } pub struct MASQNodeUtils {} @@ -300,6 +302,15 @@ impl MASQNodeUtils { Self::start_from(start.parent().unwrap()) } } + + pub fn derive_country_code_opt(node_addr: &NodeAddr) -> Option { + let country_code = get_node_location(Some(node_addr.ip_addr())); + if let Some(cc) = country_code { + Some(cc.country_code) + } else { + None + } + } } #[cfg(test)] diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index d9dc2c6da..4eadca514 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -36,6 +36,7 @@ use std::str::FromStr; use std::string::ToString; use std::thread; use std::time::Duration; +use node_lib::neighborhood::node_location::get_node_location; pub const DATA_DIRECTORY: &str = "/node_root/home"; pub const STANDARD_CLIENT_TIMEOUT_MILLIS: u64 = 1000; @@ -760,6 +761,10 @@ impl MASQNode for MASQRealNode { fn routes_data(&self) -> bool { self.guts.routes_data } + + fn country_code_opt(&self) -> Option { + MASQNodeUtils::derive_country_code_opt(&self.node_addr()) + } } impl MASQRealNode { diff --git a/multinode_integration_tests/src/utils.rs b/multinode_integration_tests/src/utils.rs index 76b555388..d0cb6e988 100644 --- a/multinode_integration_tests/src/utils.rs +++ b/multinode_integration_tests/src/utils.rs @@ -135,17 +135,12 @@ impl From<&dyn MASQNode> for AccessibleGossipRecord { accepts_connections: masq_node.accepts_connections(), routes_data: masq_node.routes_data(), version: 0, - country_code_opt: None, + country_code_opt: masq_node.country_code_opt(), }, node_addr_opt: Some(masq_node.node_addr()), signed_gossip: PlainData::new(b""), signature: CryptData::new(b""), }; - let ip_addr = masq_node.node_addr().ip_addr(); - let country_code = get_node_location(Some(ip_addr)); - if let Some(cc) = country_code { - agr.inner.country_code_opt = Some(cc.country_code) - }; agr.regenerate_signed_gossip(cryptde); agr } diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index ffe3e3215..cda51cca3 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -3983,7 +3983,7 @@ mod tests { let log_handler = TestLogHandler::new(); assert_eq!( exit_handler_response, - Err((9223372036854775817, "CZ, SK, IN".to_string(),)) + Err((9223372036854775816, "CZ, SK, IN".to_string(),)) ); log_handler.assert_logs_contain_in_order(vec![ &format!( @@ -4197,7 +4197,7 @@ mod tests { record_one.body, UiSetExitLocationResponse { fallback_routing: false, - exit_locations: vec![ + exit_country_selection: vec![ ExitLocation { country_codes: vec!["CZ".to_string()], priority: 1 @@ -4218,7 +4218,7 @@ mod tests { target: MessageTarget::ClientId(6543), body: UiSetExitLocationResponse { fallback_routing: true, - exit_locations: vec![], + exit_country_selection: vec![], exit_countries: None, missing_countries: vec![], } From 14b4c03b26c5facda40a1e0d9ff2faf99da4daa2 Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 5 Mar 2025 15:50:18 +0100 Subject: [PATCH 03/27] continue addressing review comments --- .../tests/blockchain_interaction_test.rs | 2 +- node/src/bootstrapper.rs | 2 +- node/src/neighborhood/gossip_acceptor.rs | 2 +- node/src/neighborhood/mod.rs | 5 +++-- .../unprivileged_parse_args_configuration.rs | 1 - 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/multinode_integration_tests/tests/blockchain_interaction_test.rs b/multinode_integration_tests/tests/blockchain_interaction_test.rs index 805d2a5f4..44d95dbf2 100644 --- a/multinode_integration_tests/tests/blockchain_interaction_test.rs +++ b/multinode_integration_tests/tests/blockchain_interaction_test.rs @@ -23,7 +23,7 @@ use std::time::{Duration, SystemTime}; #[test] fn debtors_are_credited_once_but_not_twice() { if is_running_under_github_actions() { - eprintln!("This test doesn't pass under GitHub Actions; don't know why, test_node_1 does not contain local descriptor in log after startup"); + eprintln!("This test doesn't pass under GitHub Actions; don't know why"); return; } let mbcs_port = find_free_port(); diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index b1e920307..f49bb5707 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -506,7 +506,7 @@ impl ConfiguredByPrivilege for Bootstrapper { &alias_cryptde_null_opt, self.config.blockchain_bridge_config.chain, ); - // initialization od CountryFinder + // initialization of CountryFinder let _ = get_node_location(Some(IpAddr::V4(Ipv4Addr::new(8, 8, 8, 8)))); let node_descriptor = Bootstrapper::make_local_descriptor( cryptdes.main, diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index e2570b176..6180dfaca 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -356,7 +356,7 @@ impl DebutHandler { fn create_debut_gossip_response( cryptde: &dyn CryptDE, - database: &mut NeighborhoodDatabase, + database: &NeighborhoodDatabase, debut_node_key: PublicKey, ) -> Gossip_0v1 { let mut root_node = database.root().clone(); diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index cda51cca3..c2705a085 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -63,9 +63,10 @@ use itertools::Itertools; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; use masq_lib::crash_point::CrashPoint; +use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; use masq_lib::messages::{ - ExitLocation, ExitLocationSet, FromMessageBody, ToMessageBody, UiConnectionStage, + ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse, }; use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest}; @@ -1806,7 +1807,7 @@ impl Neighborhood { target: MessageTarget::ClientId(client_id), body: UiSetExitLocationResponse { fallback_routing, - exit_locations, + exit_country_selection: exit_locations, exit_countries: show_countries, missing_countries, } diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index e124b150c..4238bd8d5 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -853,7 +853,6 @@ mod tests { Ok(NeighborhoodConfig { mode: NeighborhoodMode::Standard(node_addr, _, _), min_hops: Hops::ThreeHops, - .. }) => node_addr, x => panic!("Wasn't expecting {:?}", x), }; From a803c46a89cf31bb8aeb3673ae80749edd93d7d9 Mon Sep 17 00:00:00 2001 From: czarte Date: Mon, 17 Mar 2025 12:52:39 +0100 Subject: [PATCH 04/27] more addressed comments from review --- masq_lib/src/constants.rs | 3 + node/src/neighborhood/gossip_acceptor.rs | 141 +++++++------- node/src/neighborhood/mod.rs | 183 +++++++++--------- .../src/neighborhood/neighborhood_database.rs | 43 ++-- node/src/neighborhood/node_location.rs | 1 + node/src/neighborhood/node_record.rs | 5 +- .../src/test_utils/neighborhood_test_utils.rs | 12 +- 7 files changed, 178 insertions(+), 210 deletions(-) diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index 1d8f5ac21..8393b1cd9 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -105,6 +105,9 @@ pub const BASE_MAINNET_FULL_IDENTIFIER: &str = concatcp!(BASE_FAMILY, LINK, MAIN pub const BASE_SEPOLIA_FULL_IDENTIFIER: &str = concatcp!(BASE_FAMILY, LINK, "sepolia"); pub const DEV_CHAIN_FULL_IDENTIFIER: &str = "dev"; +//allocations +pub const DEFAULT_PREALLOCATION_VEC: usize = 10; + #[cfg(test)] mod tests { use super::*; diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 6180dfaca..4096c8234 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -91,6 +91,23 @@ impl GossipHandler for DebutHandler { if database.node_by_key(&agrs[0].inner.public_key).is_some() { return Qualification::Unmatched; } + // TODO create optimization card: drive in the test and following commented out code, + // TODO: Imagine a brand-new network, consisting only of Node A. + // When Node B debuts, Node A cannot respond with an Introduction, + // since there's nobody to introduce. Therefore, Node A must + // respond with a single-Node Gossip that will currently be + // interpreted as a Debut by Node B, resulting in another + // single-Node Gossip from B to A. This last Gossip isn't a + // problem, because Node A will ignore it since B is already + // in its database. However, there is a possible optimization: + // drive in the code below, and Node B will no longer interpret + // Node A's acceptance Gossip as another Debut, because it will + // see that Node A already has Node B as a neighbor. This means + // Node A's original response will be interpreted as Standard + // Gossip. + // if agrs[0].inner.neighbors.contains(database.root_key()) { + // return Qualification::Unmatched; + // } match &agrs[0].node_addr_opt { None => { if agrs[0].inner.accepts_connections { @@ -1424,6 +1441,7 @@ mod tests { use std::ops::{Add, Sub}; use std::str::FromStr; use std::time::Duration; + use itertools::Itertools; #[test] fn constants_have_correct_values() { @@ -1706,13 +1724,13 @@ mod tests { #[test] fn two_parallel_debuts_in_progress_handled_by_try_accept_debut_without_introduction() { - let mut root_node = make_node_record(1234, true); - let half_debuted_node = make_node_record(2345, true); + let root_node = make_node_record(1234, true); + let half_neighbor_debutant = make_node_record(2345, true); let new_debutant = make_node_record(4567, true); let root_node_cryptde = CryptDENull::from(&root_node.public_key(), TEST_DEFAULT_CHAIN); let mut dest_db = db_from_node(&root_node); - dest_db.add_node(half_debuted_node.clone()).unwrap(); - dest_db.add_arbitrary_half_neighbor(root_node.public_key(), half_debuted_node.public_key()); + dest_db.add_node(half_neighbor_debutant.clone()).unwrap(); + dest_db.add_arbitrary_half_neighbor(root_node.public_key(), half_neighbor_debutant.public_key()); let logger = Logger::new("Debut test"); let subject = DebutHandler::new(logger); let neighborhood_metadata = make_default_neighborhood_metadata(); @@ -1735,12 +1753,8 @@ mod tests { ) => (debut_reply, dest_public_key, dest_node_addr), x => panic!("Expected Reply, got {:?}", x), }; - assert_eq!(dest_public_key, new_debutant.public_key()); assert_eq!(dest_node_addr, &new_debutant.node_addr_opt().unwrap()); - root_node - .add_half_neighbor_key(new_debutant.public_key().clone()) - .unwrap(); assert_eq!( counter_debut, GossipAcceptanceResult::Reply( @@ -2094,7 +2108,7 @@ mod tests { ); let result_introducer: &NodeRecord = - dest_db.node_by_key_mut(&agrs[0].inner.public_key).unwrap(); + dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); let mut expected_introducer = NodeRecord::from(&agrs[0]); expected_introducer.metadata.last_update = result_introducer.metadata.last_update; expected_introducer.metadata.country_undesirability = COUNTRY_UNDESIRABILITY_FACTOR; @@ -2172,7 +2186,7 @@ mod tests { handle_result ); let result_introducer: &NodeRecord = - dest_db.node_by_key_mut(&agrs[0].inner.public_key).unwrap(); + dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); let mut expected_introducer = NodeRecord::from(&agrs[0]); expected_introducer.metadata.last_update = result_introducer.metadata.last_update; expected_introducer.resign(); @@ -2229,7 +2243,7 @@ mod tests { ); let result_introducer: &NodeRecord = - dest_db.node_by_key_mut(&agrs[0].inner.public_key).unwrap(); + dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); let mut expected_introducer = NodeRecord::from(&agrs[0]); expected_introducer.metadata.last_update = result_introducer.metadata.last_update; expected_introducer.resign(); @@ -2427,11 +2441,15 @@ mod tests { The source node(S) will gossip about Node A and B to the destination node(D). + + Country Codes + node_a: "FR" + node_b: "US" */ let src_root = make_node_record(1234, true); let dest_root = make_node_record(2345, true); let mut src_db = db_from_node(&src_root); - let node_a = make_node_record(3456, true); + let node_a = make_node_record(5678, true); let node_b = make_node_record(4567, true); let mut dest_db = db_from_node(&dest_root); dest_db.add_node(src_root.clone()).unwrap(); @@ -3051,10 +3069,16 @@ mod tests { fn first_debut_is_handled() { let mut root_node = make_node_record(1234, true); let root_node_cryptde = CryptDENull::from(&root_node.public_key(), TEST_DEFAULT_CHAIN); - let mut dest_db = db_from_node(&root_node); - let (gossip, mut debut_node, gossip_source) = make_debut(2345, Mode::Standard); + let mut source_db = db_from_node(&root_node); + let (gossip, mut debut_node, gossip_source) = make_debut(2345, Mode::Standard); //debut node is FR + let mut expected_source_db = db_from_node(&root_node); + expected_source_db.add_arbitrary_half_neighbor(root_node.public_key(), debut_node.public_key()); + expected_source_db.root_mut().inner.version = 1; + expected_source_db.root_mut().resign(); + let expected_gossip_response = GossipBuilder::new(&expected_source_db) + .node(root_node.public_key(), true) + .build(); let subject = make_subject(&root_node_cryptde); - let before = time_t_timestamp(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.user_exit_preferences_opt = Some(UserExitPreferences { exit_countries: vec!["CZ".to_string()], @@ -3065,59 +3089,26 @@ mod tests { }]), db_countries: vec!["CZ".to_string()], }); + let before = time_t_timestamp(); let result = subject.handle( - &mut dest_db, + &mut source_db, gossip.try_into().unwrap(), gossip_source, neighborhood_metadata, ); let after = time_t_timestamp(); - - let expected = "Reply(Gossip_0v1 { node_records: [ -GossipNodeRecord { - inner: NodeRecordInner_0v1 { - public_key: 0x01020304, - node_addr_opt: Some(1.2.3.4:[1234]), - earning_wallet: Wallet { kind: Address(0x546900db8d6e0937497133d1ae6fdf5f4b75bcd0) }, - rate_pack: RatePack { routing_byte_rate: 1235, routing_service_rate: 1434, exit_byte_rate: 1237, exit_service_rate: 1634 }, - neighbors: [0x02030405], - version: 1, - }, - node_addr_opt: Some(1.2.3.4:[1234]), - signed_data: -Length: 254 (0xfe) bytes -0000: a8 6a 70 75 62 6c 69 63 5f 6b 65 79 44 01 02 03 .jpublic_keyD... -0010: 04 6e 65 61 72 6e 69 6e 67 5f 77 61 6c 6c 65 74 .nearning_wallet -0020: a1 67 61 64 64 72 65 73 73 94 18 54 18 69 00 18 .gaddress..T.i.. -0030: db 18 8d 18 6e 09 18 37 18 49 18 71 18 33 18 d1 ....n..7.I.q.3.. -0040: 18 ae 18 6f 18 df 18 5f 18 4b 18 75 18 bc 18 d0 ...o..._.K.u.... -0050: 69 72 61 74 65 5f 70 61 63 6b a4 71 72 6f 75 74 irate_pack.qrout -0060: 69 6e 67 5f 62 79 74 65 5f 72 61 74 65 19 04 d3 ing_byte_rate... -0070: 74 72 6f 75 74 69 6e 67 5f 73 65 72 76 69 63 65 trouting_service -0080: 5f 72 61 74 65 19 05 9a 6e 65 78 69 74 5f 62 79 _rate...nexit_by -0090: 74 65 5f 72 61 74 65 19 04 d5 71 65 78 69 74 5f te_rate...qexit_ -00a0: 73 65 72 76 69 63 65 5f 72 61 74 65 19 06 62 69 service_rate..bi -00b0: 6e 65 69 67 68 62 6f 72 73 81 44 02 03 04 05 73 neighbors.D....s -00c0: 61 63 63 65 70 74 73 5f 63 6f 6e 6e 65 63 74 69 accepts_connecti -00d0: 6f 6e 73 f5 6b 72 6f 75 74 65 73 5f 64 61 74 61 ons.kroutes_data -00e0: f5 67 76 65 72 73 69 6f 6e 01 70 63 6f 75 6e 74 .gversion.pcount -00f0: 72 79 5f 63 6f 64 65 5f 6f 70 74 62 41 55 ry_code_optbAU - signature: -Length: 24 (0x18) bytes -0000: 01 02 03 04 f7 26 1e a5 d4 4a 71 d9 f8 42 08 35 .....&...Jq..B.5 -0010: e4 99 27 4a 85 7f 01 11 ..'J.... -}] }, 0x02030405, 2.3.4.5:[2345])".to_string(); - assert_eq!(format!("{:?}", result), expected); + let expected_result = GossipAcceptanceResult::Reply(expected_gossip_response, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap()); + assert_eq!(result, expected_result); root_node .add_half_neighbor_key(debut_node.public_key().clone()) .unwrap(); root_node.increment_version(); - root_node.metadata.last_update = dest_db.root().metadata.last_update; + root_node.metadata.last_update = source_db.root().metadata.last_update; root_node.resign(); - assert_eq!(&root_node, dest_db.root()); - let reference_node = dest_db.node_by_key_mut(debut_node.public_key()).unwrap(); + assert_eq!(&root_node, source_db.root()); + let reference_node = source_db.node_by_key_mut(debut_node.public_key()).unwrap(); debut_node.metadata.last_update = reference_node.metadata.last_update; debut_node.resign(); assert_eq!( @@ -3174,10 +3165,10 @@ Length: 24 (0x18) bytes root_node.metadata.last_update = dest_db.root().metadata.last_update; root_node.resign(); assert_eq!(&root_node, dest_db.root()); - let reference_node = dest_db.node_by_key_mut(debut_node.public_key()).unwrap(); + let reference_node = dest_db.node_by_key(debut_node.public_key()).unwrap(); debut_node.metadata.last_update = reference_node.metadata.last_update; debut_node.resign(); - assert_node_records_eq(reference_node, &debut_node, before, after) + assert_node_records_eq(reference_node, &debut_node, before, after) } #[test] @@ -3263,7 +3254,7 @@ Length: 24 (0x18) bytes root_node.metadata.last_update = dest_db.root().metadata.last_update; root_node.resign(); assert_eq!(&root_node, dest_db.root()); - let reference_node = dest_db.node_by_key_mut(debut_node.public_key()).unwrap(); + let reference_node = dest_db.node_by_key(debut_node.public_key()).unwrap(); debut_node.metadata.last_update = reference_node.metadata.last_update; debut_node.resign(); assert_node_records_eq(reference_node, &debut_node, before, after) @@ -3967,14 +3958,14 @@ Length: 24 (0x18) bytes &mut expected_dest_db, vec![&node_a, &node_b, &node_d, &node_e, &node_f], ); - fix_last_update_nodes(&mut expected_dest_db, &dest_db); + fix_nodes_last_updates(&mut expected_dest_db, &dest_db); expected_dest_db .node_by_key_mut(node_c.public_key()) .unwrap() .metadata .node_location_opt = None; assert_node_records_eq( - dest_db.node_by_key_mut(root_node.public_key()).unwrap(), + dest_db.node_by_key(root_node.public_key()).unwrap(), expected_dest_db .node_by_key(root_node.public_key()) .unwrap(), @@ -3982,25 +3973,25 @@ Length: 24 (0x18) bytes after, ); assert_node_records_eq( - dest_db.node_by_key_mut(node_a.public_key()).unwrap(), + dest_db.node_by_key(node_a.public_key()).unwrap(), expected_dest_db.node_by_key(node_a.public_key()).unwrap(), before, after, ); assert_node_records_eq( - dest_db.node_by_key_mut(node_b.public_key()).unwrap(), + dest_db.node_by_key(node_b.public_key()).unwrap(), expected_dest_db.node_by_key(node_b.public_key()).unwrap(), before, after, ); assert_node_records_eq( - dest_db.node_by_key_mut(node_c.public_key()).unwrap(), + dest_db.node_by_key(node_c.public_key()).unwrap(), expected_dest_db.node_by_key(node_c.public_key()).unwrap(), before, after, ); assert_node_records_eq( - dest_db.node_by_key_mut(node_d.public_key()).unwrap(), + dest_db.node_by_key(node_d.public_key()).unwrap(), expected_dest_db.node_by_key(node_d.public_key()).unwrap(), before, after, @@ -4009,7 +4000,7 @@ Length: 24 (0x18) bytes assert_eq!(dest_db.node_by_key(node_f.public_key()), None); } - fn fix_last_update_nodes( + fn fix_nodes_last_updates( expected_db: &mut NeighborhoodDatabase, dest_db: &NeighborhoodDatabase, ) { @@ -4017,7 +4008,7 @@ Length: 24 (0x18) bytes .keys() .iter() .map(|key| (*key).clone()) - .collect::>(); + .collect_vec(); keys.into_iter() .for_each(|pubkey| match dest_db.node_by_key(&pubkey) { Some(node_record) => { @@ -4118,9 +4109,9 @@ Length: 24 (0x18) bytes dest_node_mut.increment_version(); dest_node_mut.resign(); assert_eq!(result, GossipAcceptanceResult::Accepted); - fix_last_update_nodes(&mut expected_dest_db, &dest_db); + fix_nodes_last_updates(&mut expected_dest_db, &dest_db); assert_node_records_eq( - dest_db.node_by_key_mut(third_node.public_key()).unwrap(), + dest_db.node_by_key(third_node.public_key()).unwrap(), expected_dest_db .node_by_key(third_node.public_key()) .unwrap(), @@ -4128,13 +4119,13 @@ Length: 24 (0x18) bytes after, ); assert_node_records_eq( - dest_db.node_by_key_mut(src_node.public_key()).unwrap(), + dest_db.node_by_key(src_node.public_key()).unwrap(), expected_dest_db.node_by_key(src_node.public_key()).unwrap(), before, after, ); assert_node_records_eq( - dest_db.node_by_key_mut(dest_node.public_key()).unwrap(), + dest_db.node_by_key(dest_node.public_key()).unwrap(), expected_dest_db .node_by_key(dest_node.public_key()) .unwrap(), @@ -4191,7 +4182,7 @@ Length: 24 (0x18) bytes let after = time_t_timestamp(); assert_eq!(result, GossipAcceptanceResult::Ignored); assert_node_records_eq( - dest_db.node_by_key_mut(dest_root.public_key()).unwrap(), + dest_db.node_by_key(dest_root.public_key()).unwrap(), original_dest_db .node_by_key(dest_root.public_key()) .unwrap(), @@ -4199,13 +4190,13 @@ Length: 24 (0x18) bytes after, ); assert_node_records_eq( - dest_db.node_by_key_mut(src_root.public_key()).unwrap(), + dest_db.node_by_key(src_root.public_key()).unwrap(), original_dest_db.node_by_key(src_root.public_key()).unwrap(), before, after, ); assert_node_records_eq( - dest_db.node_by_key_mut(current_node.public_key()).unwrap(), + dest_db.node_by_key(current_node.public_key()).unwrap(), original_dest_db .node_by_key(current_node.public_key()) .unwrap(), @@ -4213,7 +4204,7 @@ Length: 24 (0x18) bytes after, ); assert_node_records_eq( - dest_db.node_by_key_mut(obsolete_node.public_key()).unwrap(), + dest_db.node_by_key(obsolete_node.public_key()).unwrap(), original_dest_db .node_by_key(obsolete_node.public_key()) .unwrap(), @@ -4547,7 +4538,7 @@ Length: 24 (0x18) bytes } fn assert_node_records_eq( - actual: &mut NodeRecord, + actual: &NodeRecord, expected: &NodeRecord, before: u32, after: u32, diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index c2705a085..e5c203431 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -61,7 +61,7 @@ use gossip_producer::GossipProducer; use gossip_producer::GossipProducerReal; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; +use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR}; use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; @@ -104,9 +104,9 @@ pub enum FallbackPreference { #[derive(Clone, Debug)] pub struct UserExitPreferences { - exit_countries: Vec, //if we cross number of countries used in one workflow, we want to change this member to HashSet + exit_countries: Vec, //if we cross number of country_codes used in one workflow over 34, we want to change this member to HashSet fallback_preference: FallbackPreference, - locations_opt: Option>, + locations_opt: Option>, //TODO remove Option from NeighborhoodMetadata and create there TODO to ptimize it in future via reference db_countries: Vec, } @@ -583,12 +583,10 @@ impl Neighborhood { fn handle_new_ip_location(&mut self, new_public_ip: IpAddr) { let node_location_opt = get_node_location(Some(new_public_ip)); - self.neighborhood_database - .root_mut() - .metadata - .node_location_opt = node_location_opt.clone(); - self.neighborhood_database.root_mut().inner.country_code_opt = - node_location_opt.map(|nl| nl.country_code); + let root_node = self.neighborhood_database + .root_mut(); + root_node.metadata.node_location_opt = node_location_opt.clone(); + root_node.inner.country_code_opt = node_location_opt.map(|nl| nl.country_code); } fn handle_route_query_message(&mut self, msg: RouteQueryMessage) -> Option { @@ -892,8 +890,8 @@ impl Neighborhood { self.handle_gossip_ignored(ignored_node_name, gossip_record_count) } GossipAcceptanceResult::Ban(reason) => { - // TODO in case we introduce Ban machinery we need to reinitialize the db_countries here as well - in that case, we need to make new process to subtract - // result in init_db_countries guts by one for particular country + // TODO in case we introduce Ban machinery we want to reinitialize the db_countries here as well + // in that case, we need to make new process in init_db_countries to exclude banned node, from the result warning!(self.logger, "Malefactor detected at {}, but malefactor bans not yet implemented; ignoring: {}", gossip_source, reason); self.handle_gossip_ignored(ignored_node_name, gossip_record_count); } @@ -1287,7 +1285,7 @@ impl Neighborhood { fn validate_fallback_country_exit_codes(&self, last_node: &PublicKey) -> bool { let last_cc = match self.neighborhood_database.node_by_key(last_node) { - Some(nr) => match nr.clone().inner.country_code_opt { + Some(nr) => match nr.inner.country_code_opt.clone() { Some(cc) => cc, None => "ZZ".to_string(), }, @@ -1300,9 +1298,6 @@ impl Neighborhood { return true; } for country in &self.user_exit_preferences.exit_countries { - if country == &last_cc { - return true; - } if self.user_exit_preferences.db_countries.contains(country) && country != &last_cc { return false; } @@ -1312,29 +1307,28 @@ impl Neighborhood { fn validate_last_node_country_code( &self, - first_node_key: &PublicKey, + last_node_key: &PublicKey, research_neighborhood: bool, direction: RouteDirection, ) -> bool { if self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing || (self.user_exit_preferences.fallback_preference == FallbackPreference::ExitCountryWithFallback - && self.validate_fallback_country_exit_codes(first_node_key)) + && self.validate_fallback_country_exit_codes(last_node_key)) || research_neighborhood || direction == RouteDirection::Back { true // Zero- and single-hop routes are not subject to exit-too-close restrictions } else { - match self.neighborhood_database.node_by_key(first_node_key) { - Some(node_record) => match &node_record.inner.country_code_opt { - Some(country_code) => self + if let Some(node_record) = self.neighborhood_database.node_by_key(last_node_key) { + if let Some(country_code) = &node_record.inner.country_code_opt { + return self .user_exit_preferences .exit_countries - .contains(country_code), - _ => false, - }, - _ => false, + .contains(country_code) + } } + false } } @@ -1344,33 +1338,34 @@ impl Neighborhood { undesirability_type: UndesirabilityType, logger: &Logger, ) -> i64 { - let mut rate_undesirability = match undesirability_type { - UndesirabilityType::Relay => node_record.inner.rate_pack.routing_charge(payload_size), - UndesirabilityType::ExitRequest(_) => { - node_record.inner.rate_pack.exit_charge(payload_size) - + node_record.metadata.country_undesirability as u64 - } - UndesirabilityType::ExitAndRouteResponse => { - node_record.inner.rate_pack.exit_charge(payload_size) - + node_record.inner.rate_pack.routing_charge(payload_size) - } - } as i64; - if let UndesirabilityType::ExitRequest(Some(hostname)) = undesirability_type { - if node_record.metadata.unreachable_hosts.contains(hostname) { - trace!( - logger, - "Node with PubKey {:?} failed to reach host {:?} during ExitRequest; Undesirability: {} + {} = {}", - node_record.public_key(), - hostname, - rate_undesirability, - UNREACHABLE_HOST_PENALTY, - rate_undesirability + UNREACHABLE_HOST_PENALTY - ); - rate_undesirability += UNREACHABLE_HOST_PENALTY; - } + match undesirability_type { + UndesirabilityType::Relay => node_record.inner.rate_pack.routing_charge(payload_size) as i64, + UndesirabilityType::ExitRequest(None) => node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.metadata.country_undesirability as i64, + UndesirabilityType::ExitRequest(Some(hostname)) => { + let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; + let country_undesirability = node_record.metadata.country_undesirability as i64; + let unreachable_undesirability = + if node_record.metadata.unreachable_hosts.contains(hostname) { + trace!( + logger, + "Node with PubKey {:?} failed to reach host {:?} during ExitRequest; Undesirability: {} + {} + {} = {}", + node_record.public_key(), + hostname, + exit_undesirability, + UNREACHABLE_HOST_PENALTY, + country_undesirability, + exit_undesirability + UNREACHABLE_HOST_PENALTY + country_undesirability + ); + UNREACHABLE_HOST_PENALTY + } else { + 0i64 + }; + exit_undesirability + unreachable_undesirability + country_undesirability + }, + UndesirabilityType::ExitAndRouteResponse => node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.inner.rate_pack.routing_charge(payload_size) as i64 } - - rate_undesirability } fn is_orig_node_on_back_leg( @@ -1393,7 +1388,7 @@ impl Neighborhood { return_route_id } - pub fn find_exit_location<'a>( + pub fn find_exit_locations<'a>( &'a self, source: &'a PublicKey, minimum_hops: usize, @@ -1402,10 +1397,10 @@ impl Neighborhood { let mut minimum_undesirability = i64::MAX; let initial_undesirability = 0; let research_exits: &mut Vec<&'a PublicKey> = &mut vec![]; - let mut prefix = Vec::with_capacity(10); + let mut prefix = Vec::with_capacity(DEFAULT_PREALLOCATION_VEC); prefix.push(source); let over_routes = self.routing_engine( - &mut prefix, + prefix, initial_undesirability, None, minimum_hops, @@ -1458,7 +1453,7 @@ impl Neighborhood { prefix.push(source); let result = self .routing_engine( - &mut vec![source], + vec![source], initial_undesirability, target_opt, minimum_hops, @@ -1482,7 +1477,7 @@ impl Neighborhood { #[allow(clippy::too_many_arguments)] fn routing_engine<'a>( &'a self, - prefix: &mut Vec<&'a PublicKey>, + prefix: Vec<&'a PublicKey>, undesirability: i64, target_opt: Option<&'a PublicKey>, hops_remaining: usize, @@ -1496,7 +1491,6 @@ impl Neighborhood { if undesirability > *minimum_undesirability && !research_neighborhood { return vec![]; } - //TODO when target node is present ignore all country_codes selection - write test for route back and ignore the country code let first_node_key = prefix.first().expect("Empty prefix"); let previous_node = self .neighborhood_database @@ -1546,7 +1540,7 @@ impl Neighborhood { && (self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing || self.user_exit_preferences.exit_countries.is_empty()) { - // in case we do not investigate neighborhood for country codes, or we do not looking for particular country exit: + // in case we do not investigate neighborhood for country codes, or we are not looking for particular country exit: // don't continue a targetless search past the minimum hop count vec![] } else { @@ -1569,7 +1563,7 @@ impl Neighborhood { #[allow(clippy::too_many_arguments)] fn routing_guts<'a>( &'a self, - prefix: &mut [&'a PublicKey], + prefix: Vec<&'a PublicKey>, undesirability: i64, target_opt: Option<&'a PublicKey>, hops_remaining: usize, @@ -1578,7 +1572,7 @@ impl Neighborhood { minimum_undesirability: &mut i64, hostname_opt: Option<&str>, research_neighborhood: bool, - research_exits: &mut Vec<&'a PublicKey>, + exits_research: &mut Vec<&'a PublicKey>, previous_node: &NodeRecord, ) -> Vec { // Go through all the neighbors and compute shorter routes through all the ones we're not already using. @@ -1591,7 +1585,7 @@ impl Neighborhood { || Self::is_orig_node_on_back_leg(**node_record, target_opt, direction) }) .flat_map(|node_record| { - let mut new_prefix = prefix.to_owned(); + let mut new_prefix = prefix.clone(); new_prefix.push(node_record.public_key()); let new_hops_remaining = if hops_remaining == 0 { @@ -1611,7 +1605,7 @@ impl Neighborhood { ); self.routing_engine( - &mut new_prefix, + new_prefix, new_undesirability, target_opt, new_hops_remaining, @@ -1620,7 +1614,7 @@ impl Neighborhood { minimum_undesirability, hostname_opt, research_neighborhood, - research_exits, + exits_research, ) }) .collect() @@ -1707,15 +1701,13 @@ impl Neighborhood { message.fallback_routing, exit_locations_by_priority.is_empty(), ) { - (true, true) => FallbackPreference::Nothing, + (true, true) | (false, true) => FallbackPreference::Nothing, (true, false) => FallbackPreference::ExitCountryWithFallback, (false, false) => FallbackPreference::ExitCountryNoFallback, - (false, true) => FallbackPreference::Nothing, }; let fallback_status = match self.user_exit_preferences.fallback_preference { - FallbackPreference::Nothing => "Fallback Routing is set.", - FallbackPreference::ExitCountryWithFallback => "Fallback Routing is set.", + FallbackPreference::Nothing | FallbackPreference::ExitCountryWithFallback => "Fallback Routing is set.", FallbackPreference::ExitCountryNoFallback => "Fallback Routing NOT set.", }; @@ -1725,23 +1717,7 @@ impl Neighborhood { match self.neighborhood_database.keys().len() > 1 { true => { self.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); - let location_set = ExitLocationSet { - locations: exit_locations_by_priority, - }; - let exit_location_status = match location_set.locations.is_empty() { - false => "Exit location set: ", - true => "Exit location unset.", - }; - info!( - self.logger, - "{} {}{}", fallback_status, exit_location_status, location_set - ); - if !missing_locations.is_empty() { - warning!( - self.logger, - "Exit Location: following desired countries are missing in Neighborhood {:?}", &missing_locations - ); - } + self.exit_location_logger_output(exit_locations_by_priority, &missing_locations, fallback_status); } false => info!( self.logger, @@ -1761,23 +1737,40 @@ impl Neighborhood { .expect("UiGateway is dead"); } + fn exit_location_logger_output(&mut self, exit_locations_by_priority: Vec, missing_locations: &Vec, fallback_status: &str) { + + self.logger.info(|| { + let location_set = ExitLocationSet { + locations: exit_locations_by_priority, + }; + let exit_location_status = match location_set.locations.is_empty() { + false => "Exit location set: ", + true => "Exit location unset.", + }; + format!("{} {}{}", fallback_status, exit_location_status, location_set) + }); + if !missing_locations.is_empty() { + warning!( + self.logger, + "Exit Location: following desired countries are missing in Neighborhood {:?}", &missing_locations + ); + } + } + fn error_message_indicates(&self, missing_countries: &mut Vec) -> bool { let mut desired_countries: Vec = vec![]; if let Some(exit_vec) = self.user_exit_preferences.locations_opt.as_ref() { for location in exit_vec { - let mut to_appedn = location.country_codes.clone(); - desired_countries.append(&mut to_appedn) + let mut to_append = location.country_codes.clone(); + desired_countries.append(&mut to_append) } } - if desired_countries.is_empty() && missing_countries.is_empty() { return false; } missing_countries.sort(); desired_countries.sort(); - let comp_missing_countries = missing_countries.clone(); - let comp_exit_countries = desired_countries; - comp_missing_countries.eq(&comp_exit_countries) + missing_countries == &desired_countries } fn create_exit_location_response( @@ -1797,7 +1790,7 @@ impl Neighborhood { .locations_opt .clone() .unwrap_or_default(); - let show_countries = match show_countries_flag { + let countries_to_show = match show_countries_flag { true => Some(self.user_exit_preferences.db_countries.clone()), false => None, }; @@ -1808,7 +1801,7 @@ impl Neighborhood { body: UiSetExitLocationResponse { fallback_routing, exit_country_selection: exit_locations, - exit_countries: show_countries, + exit_countries: countries_to_show, missing_countries, } .tmb(context_id), @@ -1900,7 +1893,7 @@ impl Neighborhood { let root_key = self.neighborhood_database.root_key(); let min_hops = self.min_hops as usize; let exit_nodes = self - .find_exit_location(root_key, min_hops, 0usize) + .find_exit_locations(root_key, min_hops, 0usize) .to_owned(); let mut db_countries = vec![]; if !exit_nodes.is_empty() { @@ -4461,7 +4454,7 @@ mod tests { join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); designate_root_node(db, &l); - let mut exit_nodes = subject.find_exit_location(&l, 3, 10_000); + let mut exit_nodes = subject.find_exit_locations(&l, 3, 10_000); let total_exit_nodes = exit_nodes.len(); exit_nodes.sort(); @@ -4507,7 +4500,7 @@ mod tests { }; designate_root_node(db, &n1); - let mut exit_nodes = subject.find_exit_location(&n1, 3, 10_000); + let mut exit_nodes = subject.find_exit_locations(&n1, 3, 10_000); let total_exit_nodes = exit_nodes.len(); exit_nodes.sort(); @@ -5035,7 +5028,7 @@ mod tests { TestLogHandler::new().exists_log_containing( "TRACE: Neighborhood: Node with PubKey 0x02030405 \ failed to reach host \"hostname.com\" during ExitRequest; \ - Undesirability: 2350745 + 100000000 = 102350745", + Undesirability: 2350745 + 100000000 + 0 = 102350745", ); } diff --git a/node/src/neighborhood/neighborhood_database.rs b/node/src/neighborhood/neighborhood_database.rs index 63fc633ce..2d99bf088 100644 --- a/node/src/neighborhood/neighborhood_database.rs +++ b/node/src/neighborhood/neighborhood_database.rs @@ -410,7 +410,6 @@ mod tests { let last_update = subject.root().metadata.last_update; this_node.metadata.last_update = last_update; - assert_eq!(subject.this_node, this_node.public_key().clone()); assert_eq!( subject.by_public_key, @@ -446,7 +445,6 @@ mod tests { let last_update = subject.root().metadata.last_update; this_node.metadata.last_update = last_update; - assert_eq!(subject.this_node, this_node.public_key().clone()); assert_eq!( subject.by_public_key, @@ -506,7 +504,6 @@ mod tests { #[test] fn node_by_key_works() { let mut this_node = make_node_record(1234, true); - let one_node = make_node_record(4567, true); let another_node = make_node_record(5678, true); let mut subject = NeighborhoodDatabase::new( @@ -519,14 +516,8 @@ mod tests { subject.add_node(one_node.clone()).unwrap(); let this_pubkey = this_node.public_key(); - let updated_record = subject - .by_public_key - .iter() - .filter(|(pubkey, _node_record)| *pubkey == this_pubkey) - .exactly_one() - .unwrap(); - this_node.metadata.last_update = updated_record.1.metadata.last_update; - + let updated_record = subject.node_by_key(this_pubkey).unwrap(); + this_node.metadata.last_update = updated_record.metadata.last_update; assert_eq!( subject.node_by_key(this_node.public_key()).unwrap().clone(), this_node @@ -554,14 +545,8 @@ mod tests { subject.add_node(one_node.clone()).unwrap(); let this_pubkey = this_node.public_key(); - let updated_record = subject - .by_public_key - .iter() - .filter(|(pubkey, _node_record)| *pubkey == this_pubkey) - .exactly_one() - .unwrap(); - this_node.metadata.last_update = updated_record.1.metadata.last_update; - + let updated_record = subject.node_by_key(this_pubkey).unwrap(); + this_node.metadata.last_update = updated_record.metadata.last_update; assert_eq!( subject .node_by_ip(&this_node.node_addr_opt().unwrap().ip_addr()) @@ -595,20 +580,19 @@ mod tests { ); subject.add_node(node_a.clone()).unwrap(); subject.add_node(node_b.clone()).unwrap(); - let mut iterator: u16 = 7890; + let mut ipnumber: u16 = 7890; let mut keys_nums: Vec<(PublicKey, u16)> = vec![]; let mutable_nodes = subject.nodes_mut(); for node in mutable_nodes { - let (seg1, seg2, seg3, seg4) = make_segments(iterator); + let (seg1, seg2, seg3, seg4) = make_segments(ipnumber); node.metadata.node_addr_opt = Some(NodeAddr::new( &make_segmented_ip(seg1, seg2, seg3, seg4), - &[iterator], + &[ipnumber], )); - keys_nums.push((node.inner.public_key.clone(), iterator)); - iterator += 1; + keys_nums.push((node.inner.public_key.clone(), ipnumber)); + ipnumber += 1; } - for (pub_key, num) in keys_nums { let (seg1, seg2, seg3, seg4) = make_segments(num); assert_eq!( @@ -903,13 +887,8 @@ mod tests { subject.new_public_ip(new_public_ip); let this_pubkey = this_node.public_key(); - let updated_record = subject - .by_public_key - .iter() - .filter(|(pubkey, _node_record)| *pubkey == this_pubkey) - .exactly_one() - .unwrap(); - old_node.metadata.last_update = updated_record.1.metadata.last_update; + let updated_record = subject.node_by_key(this_pubkey).unwrap(); + old_node.metadata.last_update = updated_record.metadata.last_update; let mut new_node = subject.root().clone(); assert_eq!(subject.node_by_ip(&new_public_ip), Some(&new_node)); diff --git a/node/src/neighborhood/node_location.rs b/node/src/neighborhood/node_location.rs index 7e960fdc2..6142b5cc6 100644 --- a/node/src/neighborhood/node_location.rs +++ b/node/src/neighborhood/node_location.rs @@ -40,6 +40,7 @@ mod tests { #[test] fn construct_node_record_metadata_with_free_world_bit() { + //TODO check in From impl for AGR that construction of metadata contains proper country_code and fwb, then delete this test let mut metadata = NodeRecordMetadata::new(); metadata.node_location_opt = get_node_location(Some(IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)))); assert_eq!( diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs index 7a29c0b1e..e3e56fb58 100644 --- a/node/src/neighborhood/node_record.rs +++ b/node/src/neighborhood/node_record.rs @@ -382,10 +382,11 @@ mod tests { node_record_wo_location.inner.accepts_connections = false; let no_location_db = db_from_node(&node_record_wo_location); let no_location_gossip = - GossipBuilder::new(&no_location_db).node(node_record_wo_location.public_key(), false); + GossipBuilder::new(&no_location_db).node(node_record_wo_location.public_key(), false) + .build(); let nr_wo_location = - NodeRecord::try_from(no_location_gossip.build().node_records.first().unwrap()).unwrap(); + NodeRecord::try_from(no_location_gossip.node_records.first().unwrap()).unwrap(); assert_eq!(nr_wo_location.inner.country_code_opt, None); } diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index 5c3904ad7..a3f415f93 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -26,16 +26,16 @@ lazy_static! { pub static ref COUNTRY_CODE_DIGEST: Vec<(IpAddr, String, bool)> = vec![ ( IpAddr::V4(Ipv4Addr::new(123, 123, 123, 123)), - "FR".to_string(), - true + "CN".to_string(), + false ), ( IpAddr::V4(Ipv4Addr::new(0, 0, 0, 123)), "US".to_string(), true ), - ( - IpAddr::V4(Ipv4Addr::new(99, 99, 99, 99)), + ( + IpAddr::V4(Ipv4Addr::new(99, 99, 99, 99)), "FR".to_string(), true ), @@ -83,7 +83,7 @@ pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord { let key = PublicKey::new(&[seg1, seg2, seg3, seg4]); let ip_addr = make_segmented_ip(seg1, seg2, seg3, seg4); let node_addr = NodeAddr::new(&ip_addr, &[n % 10000]); - let (_ip, country_code, free_world_bit) = pick_country_code_record(n % 6); + let (_ip, country_code, free_world_bit) = pick_country_code_record(n); let location_opt = match has_ip { true => match country_code.is_empty() { false => Some(NodeLocation { @@ -105,7 +105,7 @@ pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord { ) } -pub fn pick_country_code_record(n: u16) -> (IpAddr, String, bool) { +pub fn pick_country_code_record(n: u16) -> (IpAddr, String, bool) { COUNTRY_CODE_DIGEST[n as usize % COUNTRY_CODE_DIGEST.len()].clone() } From cb56d7ba12f853c4da7802370e0f79a0ca52300b Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 18 Mar 2025 22:00:25 +0100 Subject: [PATCH 05/27] test for min_hops_change affects exit_countries and enrich_exit_countries in extract_exit_locations_from_message to be implemented in handle_config_change_msg --- node/src/neighborhood/mod.rs | 95 ++++++++++++++++++++++++++++-------- 1 file changed, 74 insertions(+), 21 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index e5c203431..36f3cf205 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -65,10 +65,7 @@ use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTR use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; -use masq_lib::messages::{ - ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, - UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse, -}; +use masq_lib::messages::{CountryCodes, ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse}; use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest}; use masq_lib::ui_gateway::MessagePath::Conversation; use masq_lib::ui_gateway::{MessageBody, MessageTarget, NodeFromUiMessage, NodeToUiMessage}; @@ -651,6 +648,10 @@ impl Neighborhood { &self.logger, ); } + self.user_exit_preferences.db_countries = self.init_db_countries(); + // if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.as_ref() { + // self.set_country_undesirability_and_exit_countries(exit_locations_by_priority); + // } self.search_for_a_new_route(); } ConfigChange::UpdatePassword(new_password) => { @@ -1694,6 +1695,7 @@ impl Neighborhood { client_id: u64, context_id: u64, ) { + //TODO write test that contains more CountryCodes than countries in neighborhood db to check if unexistent country codes in db are filtered out from ExitLocation let (exit_locations_by_priority, missing_locations) = self.extract_exit_locations_from_message(&message); @@ -1856,6 +1858,7 @@ impl Neighborhood { message: &UiSetExitLocationRequest, ) -> (Vec, Vec) { //TODO perform following update of db_countries only in Gossip_Acceptor + //TODO implement enrich_exit_countries in handle_config_change_msg self.user_exit_preferences.db_countries = self.init_db_countries(); let mut countries_lack_in_neighborhood = vec![]; ( @@ -1864,21 +1867,7 @@ impl Neighborhood { .exit_locations .into_iter() .map(|cc| { - for code in &cc.country_codes { - if self.user_exit_preferences.db_countries.contains(code) - || self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback - { - self.user_exit_preferences.exit_countries.push(code.clone()); - if self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback - { - countries_lack_in_neighborhood.push(code.clone()); - } - } else { - countries_lack_in_neighborhood.push(code.clone()); - } - } + countries_lack_in_neighborhood = self.enrich_exit_countries(&cc); ExitLocation { country_codes: cc.country_codes, priority: cc.priority, @@ -1889,6 +1878,27 @@ impl Neighborhood { ) } + fn enrich_exit_countries(&mut self, cc: &CountryCodes) -> Vec { + let mut countries_lack_in_neighborhood = vec![]; + for code in &cc.country_codes { + if self.user_exit_preferences.db_countries.contains(code) + || self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback + { + self.user_exit_preferences.exit_countries.push(code.clone()); + if (self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback) && + !self.user_exit_preferences.db_countries.contains(code) + { + countries_lack_in_neighborhood.push(code.clone()); + } + } else { + countries_lack_in_neighborhood.push(code.clone()); + } + } + countries_lack_in_neighborhood + } + fn init_db_countries(&mut self) -> Vec { let root_key = self.neighborhood_database.root_key(); let min_hops = self.min_hops as usize; @@ -2132,6 +2142,7 @@ mod tests { use std::thread; use std::time::Duration; use std::time::Instant; + use libc::printf; use tokio::prelude::Future; use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; @@ -3561,6 +3572,50 @@ mod tests { assert_eq!(juicy_parts(result_1), (1, 1)); } + #[test] + fn min_hops_change_affects_db_countries() { + let mut subject = make_standard_subject(); + let root_node = subject.neighborhood_database.root().clone(); //CH + let neighbor_one = make_node_record(1234, true); //AU + let neighbor_two = make_node_record(2345, true); //FR + let neighbor_three = make_node_record(3456, true); //CN + let neighbor_four = make_node_record(4567, true); //US + subject.neighborhood_database.add_node(neighbor_one.clone()).unwrap(); + subject.neighborhood_database.add_node(neighbor_two.clone()).unwrap(); + subject.neighborhood_database.add_node(neighbor_three.clone()).unwrap(); + subject.neighborhood_database.add_node(neighbor_four.clone()).unwrap(); + subject.neighborhood_database.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_one.public_key()); + subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_one.public_key(), neighbor_two.public_key()); + subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); + subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); + subject.user_exit_preferences.db_countries = subject.init_db_countries(); + let tree_hop_db_countries = subject.user_exit_preferences.db_countries.clone(); + let config_msg_two_hops = ConfigChangeMsg { + change: ConfigChange::UpdateMinHops(Hops::TwoHops), + }; + let config_msg_four_hops = ConfigChangeMsg { + change: ConfigChange::UpdateMinHops(Hops::FourHops), + }; + let peer_actors = peer_actors_builder().build(); + subject.handle_bind_message(BindMessage { peer_actors }); + let exit_locations_by_priority = vec![ExitLocation { + country_codes: vec!["US".to_string(), "FR".to_string()], + priority: 1, + }]; + subject.user_exit_preferences.exit_countries.push("US".to_string()); + + subject.handle_config_change_msg(config_msg_two_hops); + subject.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); + println!("exit_countries {:?}", subject.user_exit_preferences.exit_countries); + let two_hops_db_countries = subject.user_exit_preferences.db_countries.clone(); + subject.handle_config_change_msg(config_msg_four_hops); + + let four_hops_db_countries = subject.user_exit_preferences.db_countries; + assert_eq!(tree_hop_db_countries, vec!["CN".to_string(), "US".to_string()]); + assert_eq!(two_hops_db_countries, vec!["CN".to_string(), "FR".to_string(), "US".to_string()]); + assert_eq!(four_hops_db_countries, vec!["US".to_string()]); + } + #[test] fn neighborhood_handles_config_change_msg() { assert_handling_of_config_change_msg( @@ -3612,7 +3667,6 @@ mod tests { init_test_logging(); let mut subject = make_standard_subject(); subject.logger = Logger::new("ConfigChange"); - subject.handle_config_change_msg(msg); assertions(&subject); @@ -4812,7 +4866,6 @@ mod tests { #[test] fn route_for_au_country_code_is_constructed_with_fallback_routing() { let mut subject = make_standard_subject(); - //let db = &mut subject.neighborhood_database; let p = &subject .neighborhood_database .root_mut() From c23f6c8cd34513ecb729ca43aa3f70f9298416eb Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 19 Mar 2025 17:30:08 +0100 Subject: [PATCH 06/27] new machinery for min-hop affects exit location settings --- node/src/neighborhood/mod.rs | 52 +++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 36f3cf205..bae6ab78d 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -65,7 +65,7 @@ use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTR use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; -use masq_lib::messages::{CountryCodes, ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse}; +use masq_lib::messages::{ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse}; use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest}; use masq_lib::ui_gateway::MessagePath::Conversation; use masq_lib::ui_gateway::{MessageBody, MessageTarget, NodeFromUiMessage, NodeToUiMessage}; @@ -649,9 +649,13 @@ impl Neighborhood { ); } self.user_exit_preferences.db_countries = self.init_db_countries(); - // if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.as_ref() { - // self.set_country_undesirability_and_exit_countries(exit_locations_by_priority); - // } + if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.clone() { + for exit_location in &exit_locations_by_priority { + self.enrich_exit_countries(&exit_location.country_codes); + } + self.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); + } + self.search_for_a_new_route(); } ConfigChange::UpdatePassword(new_password) => { @@ -1867,7 +1871,7 @@ impl Neighborhood { .exit_locations .into_iter() .map(|cc| { - countries_lack_in_neighborhood = self.enrich_exit_countries(&cc); + countries_lack_in_neighborhood.extend(self.enrich_exit_countries(&cc.country_codes)); ExitLocation { country_codes: cc.country_codes, priority: cc.priority, @@ -1878,14 +1882,16 @@ impl Neighborhood { ) } - fn enrich_exit_countries(&mut self, cc: &CountryCodes) -> Vec { + fn enrich_exit_countries(&mut self, country_codes: &Vec) -> Vec { let mut countries_lack_in_neighborhood = vec![]; - for code in &cc.country_codes { + for code in country_codes { if self.user_exit_preferences.db_countries.contains(code) - || self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback + || (self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback) { - self.user_exit_preferences.exit_countries.push(code.clone()); + if !self.user_exit_preferences.exit_countries.contains(code) { + self.user_exit_preferences.exit_countries.push(code.clone()); + } if (self.user_exit_preferences.fallback_preference == FallbackPreference::ExitCountryWithFallback) && !self.user_exit_preferences.db_countries.contains(code) @@ -1893,6 +1899,9 @@ impl Neighborhood { countries_lack_in_neighborhood.push(code.clone()); } } else { + if let Some(index) = self.user_exit_preferences.exit_countries.iter().position(|item| item.eq(code)) { + self.user_exit_preferences.exit_countries.remove(index); + } countries_lack_in_neighborhood.push(code.clone()); } } @@ -2142,7 +2151,6 @@ mod tests { use std::thread; use std::time::Duration; use std::time::Instant; - use libc::printf; use tokio::prelude::Future; use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; @@ -3589,6 +3597,11 @@ mod tests { subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); subject.user_exit_preferences.db_countries = subject.init_db_countries(); + subject.user_exit_preferences.fallback_preference = FallbackPreference::ExitCountryWithFallback; + subject.user_exit_preferences.locations_opt = Some(vec![ExitLocation { + country_codes: vec!["FR".to_string(), "US".to_string()], + priority: 1, + }]); let tree_hop_db_countries = subject.user_exit_preferences.db_countries.clone(); let config_msg_two_hops = ConfigChangeMsg { change: ConfigChange::UpdateMinHops(Hops::TwoHops), @@ -3598,18 +3611,19 @@ mod tests { }; let peer_actors = peer_actors_builder().build(); subject.handle_bind_message(BindMessage { peer_actors }); - let exit_locations_by_priority = vec![ExitLocation { - country_codes: vec!["US".to_string(), "FR".to_string()], - priority: 1, - }]; - subject.user_exit_preferences.exit_countries.push("US".to_string()); subject.handle_config_change_msg(config_msg_two_hops); - subject.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); - println!("exit_countries {:?}", subject.user_exit_preferences.exit_countries); + //subject.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); + for node in subject.neighborhood_database.nodes_mut() { + println!("node country undesirability: {:?} {}", node.inner.country_code_opt, node.metadata.country_undesirability); + } + println!("db_countries two_hops {:?}", subject.user_exit_preferences.db_countries); + println!("exit_countries two_hops {:?}", subject.user_exit_preferences.exit_countries); let two_hops_db_countries = subject.user_exit_preferences.db_countries.clone(); subject.handle_config_change_msg(config_msg_four_hops); - + + println!("db_countries four_hops {:?}", subject.user_exit_preferences.db_countries); + println!("exit_countries four_hops {:?}", subject.user_exit_preferences.exit_countries); let four_hops_db_countries = subject.user_exit_preferences.db_countries; assert_eq!(tree_hop_db_countries, vec!["CN".to_string(), "US".to_string()]); assert_eq!(two_hops_db_countries, vec!["CN".to_string(), "FR".to_string(), "US".to_string()]); From 666699e3e9f97de843cbae291cee0846b67e3570 Mon Sep 17 00:00:00 2001 From: czarte Date: Thu, 20 Mar 2025 14:45:51 +0100 Subject: [PATCH 07/27] handle_config_change_msg debugging --- node/src/neighborhood/mod.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index bae6ab78d..060a52310 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -649,6 +649,7 @@ impl Neighborhood { ); } self.user_exit_preferences.db_countries = self.init_db_countries(); + println!("db_countries for {} hops: {:?}", self.min_hops, self.user_exit_preferences.db_countries); if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.clone() { for exit_location in &exit_locations_by_priority { self.enrich_exit_countries(&exit_location.country_codes); @@ -1900,6 +1901,7 @@ impl Neighborhood { } } else { if let Some(index) = self.user_exit_preferences.exit_countries.iter().position(|item| item.eq(code)) { + println!("removing index {}", index); self.user_exit_preferences.exit_countries.remove(index); } countries_lack_in_neighborhood.push(code.clone()); @@ -3597,6 +3599,7 @@ mod tests { subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); subject.user_exit_preferences.db_countries = subject.init_db_countries(); + subject.user_exit_preferences.exit_countries = vec!["FR".to_string(), "CN".to_string()]; subject.user_exit_preferences.fallback_preference = FallbackPreference::ExitCountryWithFallback; subject.user_exit_preferences.locations_opt = Some(vec![ExitLocation { country_codes: vec!["FR".to_string(), "US".to_string()], From 60643e0801698cebd9cb1598d4f49244db9b04e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojte=CC=8Cch=20Parka=CC=81n?= Date: Thu, 20 Mar 2025 15:19:06 +0100 Subject: [PATCH 08/27] finalize min_hops_change_affects_db_countries test --- node/src/neighborhood/mod.rs | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 060a52310..2cd7a03d8 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -649,7 +649,6 @@ impl Neighborhood { ); } self.user_exit_preferences.db_countries = self.init_db_countries(); - println!("db_countries for {} hops: {:?}", self.min_hops, self.user_exit_preferences.db_countries); if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.clone() { for exit_location in &exit_locations_by_priority { self.enrich_exit_countries(&exit_location.country_codes); @@ -1901,7 +1900,6 @@ impl Neighborhood { } } else { if let Some(index) = self.user_exit_preferences.exit_countries.iter().position(|item| item.eq(code)) { - println!("removing index {}", index); self.user_exit_preferences.exit_countries.remove(index); } countries_lack_in_neighborhood.push(code.clone()); @@ -3599,13 +3597,17 @@ mod tests { subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); subject.user_exit_preferences.db_countries = subject.init_db_countries(); - subject.user_exit_preferences.exit_countries = vec!["FR".to_string(), "CN".to_string()]; - subject.user_exit_preferences.fallback_preference = FallbackPreference::ExitCountryWithFallback; - subject.user_exit_preferences.locations_opt = Some(vec![ExitLocation { + let exit_locations_by_priority = vec![ExitLocation { country_codes: vec!["FR".to_string(), "US".to_string()], priority: 1, - }]); + }]; + for exit_location in &exit_locations_by_priority { + subject.enrich_exit_countries(&exit_location.country_codes); + } + subject.user_exit_preferences.fallback_preference = FallbackPreference::ExitCountryNoFallback; + subject.user_exit_preferences.locations_opt = Some(exit_locations_by_priority); let tree_hop_db_countries = subject.user_exit_preferences.db_countries.clone(); + let tree_hops_exit_countries = subject.user_exit_preferences.exit_countries.clone(); let config_msg_two_hops = ConfigChangeMsg { change: ConfigChange::UpdateMinHops(Hops::TwoHops), }; @@ -3616,21 +3618,18 @@ mod tests { subject.handle_bind_message(BindMessage { peer_actors }); subject.handle_config_change_msg(config_msg_two_hops); - //subject.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); - for node in subject.neighborhood_database.nodes_mut() { - println!("node country undesirability: {:?} {}", node.inner.country_code_opt, node.metadata.country_undesirability); - } - println!("db_countries two_hops {:?}", subject.user_exit_preferences.db_countries); - println!("exit_countries two_hops {:?}", subject.user_exit_preferences.exit_countries); + let two_hops_exit_countries = subject.user_exit_preferences.exit_countries.clone(); let two_hops_db_countries = subject.user_exit_preferences.db_countries.clone(); subject.handle_config_change_msg(config_msg_four_hops); - println!("db_countries four_hops {:?}", subject.user_exit_preferences.db_countries); - println!("exit_countries four_hops {:?}", subject.user_exit_preferences.exit_countries); + let four_hops_exit_countries = subject.user_exit_preferences.exit_countries.clone(); let four_hops_db_countries = subject.user_exit_preferences.db_countries; assert_eq!(tree_hop_db_countries, vec!["CN".to_string(), "US".to_string()]); + assert_eq!(tree_hops_exit_countries, vec!["US".to_string()]); assert_eq!(two_hops_db_countries, vec!["CN".to_string(), "FR".to_string(), "US".to_string()]); + assert_eq!(two_hops_exit_countries, vec!["US".to_string(), "FR".to_string()]); assert_eq!(four_hops_db_countries, vec!["US".to_string()]); + assert_eq!(four_hops_exit_countries, vec!["US".to_string()]); } #[test] From bbf67dfed208e1a0322ad3a38295a42c9745033d Mon Sep 17 00:00:00 2001 From: czarte Date: Thu, 20 Mar 2025 15:36:41 +0100 Subject: [PATCH 09/27] rename test --- node/src/neighborhood/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 2cd7a03d8..c56fe949e 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -3581,7 +3581,7 @@ mod tests { } #[test] - fn min_hops_change_affects_db_countries() { + fn min_hops_change_affects_db_countries_and_exit_location_settings() { let mut subject = make_standard_subject(); let root_node = subject.neighborhood_database.root().clone(); //CH let neighbor_one = make_node_record(1234, true); //AU From b0e74e8fb80a9e70cde853140cc2f53d69f4cde0 Mon Sep 17 00:00:00 2001 From: czarte Date: Thu, 20 Mar 2025 16:56:17 +0100 Subject: [PATCH 10/27] refactoring neighborhood/mod.rs, moving AccessibleGossipRecord to gossip.rs, formatting --- ip_country/src/ip_country.rs | 3 +- masq/src/commands/exit_location_command.rs | 4 +- masq_lib/src/exit_locations.rs | 4 +- multinode_integration_tests/src/masq_node.rs | 4 +- .../src/masq_real_node.rs | 2 +- node/src/neighborhood/gossip.rs | 46 ++- node/src/neighborhood/gossip_acceptor.rs | 33 +- node/src/neighborhood/gossip_producer.rs | 2 +- node/src/neighborhood/mod.rs | 330 +++++++++--------- node/src/neighborhood/node_record.rs | 9 +- .../src/test_utils/neighborhood_test_utils.rs | 14 +- 11 files changed, 260 insertions(+), 191 deletions(-) diff --git a/ip_country/src/ip_country.rs b/ip_country/src/ip_country.rs index 24fe9f83a..cfd335150 100644 --- a/ip_country/src/ip_country.rs +++ b/ip_country/src/ip_country.rs @@ -51,7 +51,8 @@ pub fn ip_country( *** DO NOT USE THIS CODE *** "#, error_list - ).expect("expected WANRNING output"); + ) + .expect("expected WANRNING output"); write!(stderr, "{}", error_list).expect("expected error list output"); 1 } diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index 429a0664d..4433f7a7b 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -8,9 +8,7 @@ use crate::commands::commands_common::{ use clap::{App, Arg, ArgGroup, SubCommand}; use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; use masq_lib::exit_locations::ExitLocationSet; -use masq_lib::messages::{ - CountryCodes, UiSetExitLocationRequest, UiSetExitLocationResponse, -}; +use masq_lib::messages::{CountryCodes, UiSetExitLocationRequest, UiSetExitLocationResponse}; use masq_lib::shared_schema::common_validators; use masq_lib::{as_any_ref_in_trait_impl, short_writeln}; use std::fmt::Debug; diff --git a/masq_lib/src/exit_locations.rs b/masq_lib/src/exit_locations.rs index c763f5cd5..a11970456 100644 --- a/masq_lib/src/exit_locations.rs +++ b/masq_lib/src/exit_locations.rs @@ -1,7 +1,7 @@ // Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use std::fmt::{Display, Formatter}; use crate::messages::ExitLocation; +use std::fmt::{Display, Formatter}; pub struct ExitLocationSet { pub locations: Vec, @@ -18,4 +18,4 @@ impl Display for ExitLocationSet { } Ok(()) } -} \ No newline at end of file +} diff --git a/multinode_integration_tests/src/masq_node.rs b/multinode_integration_tests/src/masq_node.rs index 5ea782ef9..20bc4fb49 100644 --- a/multinode_integration_tests/src/masq_node.rs +++ b/multinode_integration_tests/src/masq_node.rs @@ -7,6 +7,7 @@ use masq_lib::constants::{ MASQ_URL_PREFIX, }; use masq_lib::utils::to_string; +use node_lib::neighborhood::node_location::get_node_location; use node_lib::sub_lib::cryptde::{CryptDE, PublicKey}; use node_lib::sub_lib::cryptde_null::CryptDENull; use node_lib::sub_lib::neighborhood::{NodeDescriptor, RatePack}; @@ -26,7 +27,6 @@ use std::str::FromStr; use std::thread; use std::time::Duration; use std::time::Instant; -use node_lib::neighborhood::node_location::get_node_location; #[derive(PartialEq, Eq, Clone, Debug)] pub struct NodeReference { @@ -302,7 +302,7 @@ impl MASQNodeUtils { Self::start_from(start.parent().unwrap()) } } - + pub fn derive_country_code_opt(node_addr: &NodeAddr) -> Option { let country_code = get_node_location(Some(node_addr.ip_addr())); if let Some(cc) = country_code { diff --git a/multinode_integration_tests/src/masq_real_node.rs b/multinode_integration_tests/src/masq_real_node.rs index 4eadca514..c1d07698d 100644 --- a/multinode_integration_tests/src/masq_real_node.rs +++ b/multinode_integration_tests/src/masq_real_node.rs @@ -15,6 +15,7 @@ use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::{localhost, to_string}; use masq_lib::utils::{DEFAULT_CONSUMING_DERIVATION_PATH, DEFAULT_EARNING_DERIVATION_PATH}; use node_lib::blockchain::bip32::Bip32EncryptionKeyProvider; +use node_lib::neighborhood::node_location::get_node_location; use node_lib::neighborhood::DEFAULT_MIN_HOPS; use node_lib::sub_lib::accountant::{ PaymentThresholds, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, @@ -36,7 +37,6 @@ use std::str::FromStr; use std::string::ToString; use std::thread; use std::time::Duration; -use node_lib::neighborhood::node_location::get_node_location; pub const DATA_DIRECTORY: &str = "/node_root/home"; pub const STANDARD_CLIENT_TIMEOUT_MILLIS: u64 = 1000; diff --git a/node/src/neighborhood/gossip.rs b/node/src/neighborhood/gossip.rs index f2b82c843..cae5007cd 100644 --- a/node/src/neighborhood/gossip.rs +++ b/node/src/neighborhood/gossip.rs @@ -5,7 +5,6 @@ use crate::neighborhood::dot_graph::{ render_dot_graph, DotRenderable, EdgeRenderable, NodeRenderable, NodeRenderableInner, }; use crate::neighborhood::neighborhood_database::NeighborhoodDatabase; -use crate::neighborhood::AccessibleGossipRecord; use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData, PublicKey}; use crate::sub_lib::hopper::MessageType; use crate::sub_lib::node_addr::NodeAddr; @@ -465,6 +464,51 @@ impl<'a> GossipBuilder<'a> { } } +#[derive(Debug, PartialEq, Eq, Clone)] +pub struct AccessibleGossipRecord { + pub signed_gossip: PlainData, + pub signature: CryptData, + pub node_addr_opt: Option, + pub inner: NodeRecordInner_0v1, +} + +impl AccessibleGossipRecord { + pub fn regenerate_signed_gossip(&mut self, cryptde: &dyn CryptDE) { + let (signed_gossip, signature) = regenerate_signed_gossip(&self.inner, cryptde); + self.signed_gossip = signed_gossip; + self.signature = signature; + } +} + +impl TryFrom for AccessibleGossipRecord { + type Error = String; + + fn try_from(value: GossipNodeRecord) -> Result { + match serde_cbor::de::from_slice(value.signed_data.as_slice()) { + Ok(inner) => Ok(AccessibleGossipRecord { + signed_gossip: value.signed_data, + signature: value.signature, + node_addr_opt: value.node_addr_opt, + inner, + }), + Err(e) => Err(format!("{}", e)), + } + } +} + +pub fn regenerate_signed_gossip( + inner: &NodeRecordInner_0v1, + cryptde: &dyn CryptDE, // Must be the correct CryptDE for the Node from which inner came: used for signing +) -> (PlainData, CryptData) { + let signed_gossip = + PlainData::from(serde_cbor::ser::to_vec(&inner).expect("Serialization failed")); + let signature = match cryptde.sign(&signed_gossip) { + Ok(sig) => sig, + Err(e) => unimplemented!("TODO: Signing error: {:?}", e), + }; + (signed_gossip, signature) +} + #[cfg(test)] mod tests { use super::super::gossip::GossipBuilder; diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 4096c8234..a3e131554 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -1,9 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord, Gossip_0v1}; +use crate::neighborhood::gossip::{ + AccessibleGossipRecord, GossipBuilder, GossipNodeRecord, Gossip_0v1, +}; use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, NeighborhoodDatabaseError}; use crate::neighborhood::node_record::NodeRecord; -use crate::neighborhood::{AccessibleGossipRecord, UserExitPreferences}; +use crate::neighborhood::UserExitPreferences; use crate::sub_lib::cryptde::{CryptDE, PublicKey}; use crate::sub_lib::neighborhood::{ ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, @@ -1433,6 +1435,7 @@ mod tests { use crate::test_utils::unshared_test_utils::make_cpm_recipient; use crate::test_utils::{assert_contains, main_cryptde, vec_to_set}; use actix::System; + use itertools::Itertools; use masq_lib::messages::ExitLocation; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; @@ -1441,7 +1444,6 @@ mod tests { use std::ops::{Add, Sub}; use std::str::FromStr; use std::time::Duration; - use itertools::Itertools; #[test] fn constants_have_correct_values() { @@ -1730,7 +1732,10 @@ mod tests { let root_node_cryptde = CryptDENull::from(&root_node.public_key(), TEST_DEFAULT_CHAIN); let mut dest_db = db_from_node(&root_node); dest_db.add_node(half_neighbor_debutant.clone()).unwrap(); - dest_db.add_arbitrary_half_neighbor(root_node.public_key(), half_neighbor_debutant.public_key()); + dest_db.add_arbitrary_half_neighbor( + root_node.public_key(), + half_neighbor_debutant.public_key(), + ); let logger = Logger::new("Debut test"); let subject = DebutHandler::new(logger); let neighborhood_metadata = make_default_neighborhood_metadata(); @@ -3072,7 +3077,8 @@ mod tests { let mut source_db = db_from_node(&root_node); let (gossip, mut debut_node, gossip_source) = make_debut(2345, Mode::Standard); //debut node is FR let mut expected_source_db = db_from_node(&root_node); - expected_source_db.add_arbitrary_half_neighbor(root_node.public_key(), debut_node.public_key()); + expected_source_db + .add_arbitrary_half_neighbor(root_node.public_key(), debut_node.public_key()); expected_source_db.root_mut().inner.version = 1; expected_source_db.root_mut().resign(); let expected_gossip_response = GossipBuilder::new(&expected_source_db) @@ -3099,7 +3105,11 @@ mod tests { ); let after = time_t_timestamp(); - let expected_result = GossipAcceptanceResult::Reply(expected_gossip_response, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap()); + let expected_result = GossipAcceptanceResult::Reply( + expected_gossip_response, + debut_node.public_key().clone(), + debut_node.node_addr_opt().unwrap(), + ); assert_eq!(result, expected_result); root_node .add_half_neighbor_key(debut_node.public_key().clone()) @@ -3165,10 +3175,10 @@ mod tests { root_node.metadata.last_update = dest_db.root().metadata.last_update; root_node.resign(); assert_eq!(&root_node, dest_db.root()); - let reference_node = dest_db.node_by_key(debut_node.public_key()).unwrap(); + let reference_node = dest_db.node_by_key(debut_node.public_key()).unwrap(); debut_node.metadata.last_update = reference_node.metadata.last_update; debut_node.resign(); - assert_node_records_eq(reference_node, &debut_node, before, after) + assert_node_records_eq(reference_node, &debut_node, before, after) } #[test] @@ -4537,12 +4547,7 @@ mod tests { GossipAcceptorReal::new(crypt_de) } - fn assert_node_records_eq( - actual: &NodeRecord, - expected: &NodeRecord, - before: u32, - after: u32, - ) { + fn assert_node_records_eq(actual: &NodeRecord, expected: &NodeRecord, before: u32, after: u32) { assert!( actual.metadata.last_update >= before, "Timestamp should have been at least {}, but was {}", diff --git a/node/src/neighborhood/gossip_producer.rs b/node/src/neighborhood/gossip_producer.rs index 4f0ef44a9..85d9cdc06 100644 --- a/node/src/neighborhood/gossip_producer.rs +++ b/node/src/neighborhood/gossip_producer.rs @@ -102,9 +102,9 @@ impl GossipProducerReal { mod tests { use super::super::gossip::GossipNodeRecord; use super::*; + use crate::neighborhood::gossip::AccessibleGossipRecord; use crate::neighborhood::neighborhood_database::ISOLATED_NODE_GRACE_PERIOD_SECS; use crate::neighborhood::node_record::{NodeRecord, NodeRecordInner_0v1}; - use crate::neighborhood::AccessibleGossipRecord; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::utils::time_t_timestamp; diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index c56fe949e..69207e5e9 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -15,16 +15,15 @@ use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::db_config::persistent_configuration::{ PersistentConfigError, PersistentConfiguration, PersistentConfigurationReal, }; -use crate::neighborhood::gossip::{DotGossipEndpoint, GossipNodeRecord, Gossip_0v1}; +use crate::neighborhood::gossip::{AccessibleGossipRecord, DotGossipEndpoint, Gossip_0v1}; use crate::neighborhood::gossip_acceptor::GossipAcceptanceResult; use crate::neighborhood::node_location::get_node_location; -use crate::neighborhood::node_record::NodeRecordInner_0v1; use crate::neighborhood::overall_connection_status::{ OverallConnectionStage, OverallConnectionStatus, }; use crate::stream_messages::RemovedStreamType; +use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::PublicKey; -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}; @@ -65,7 +64,10 @@ use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTR use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; -use masq_lib::messages::{ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, UiSetExitLocationRequest, UiSetExitLocationResponse}; +use masq_lib::messages::{ + ExitLocation, FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest, + UiSetExitLocationRequest, UiSetExitLocationResponse, +}; use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest}; use masq_lib::ui_gateway::MessagePath::Conversation; use masq_lib::ui_gateway::{MessageBody, MessageTarget, NodeFromUiMessage, NodeToUiMessage}; @@ -87,70 +89,6 @@ pub const COUNTRY_UNDESIRABILITY_FACTOR: u32 = 1_000; pub const RESPONSE_UNDESIRABILITY_FACTOR: usize = 1_000; // assumed response length is request * this pub const ZZ_COUNTRY_CODE_STRING: &str = "ZZ"; -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct ExitLocationsRoutes<'a> { - routes: Vec<(Vec<&'a PublicKey>, i64)>, -} - -#[derive(Debug, PartialEq, Eq, Clone)] -pub enum FallbackPreference { - Nothing, - ExitCountryWithFallback, - ExitCountryNoFallback, -} - -#[derive(Clone, Debug)] -pub struct UserExitPreferences { - exit_countries: Vec, //if we cross number of country_codes used in one workflow over 34, we want to change this member to HashSet - fallback_preference: FallbackPreference, - locations_opt: Option>, //TODO remove Option from NeighborhoodMetadata and create there TODO to ptimize it in future via reference - db_countries: Vec, -} - -impl UserExitPreferences { - fn new() -> UserExitPreferences { - UserExitPreferences { - exit_countries: vec![], - fallback_preference: FallbackPreference::Nothing, - locations_opt: None, - db_countries: vec![], - } - } - - pub fn assign_nodes_country_undesirability(&self, node_record: &mut NodeRecord) { - let country_code = node_record - .inner - .country_code_opt - .clone() - .unwrap_or_else(|| ZZ_COUNTRY_CODE_STRING.to_string()); - match &self.locations_opt { - Some(exit_locations_by_priority) => { - for exit_location in exit_locations_by_priority { - if exit_location.country_codes.contains(&country_code) - && country_code != ZZ_COUNTRY_CODE_STRING - { - node_record.metadata.country_undesirability = - Self::calculate_country_undesirability( - (exit_location.priority - 1) as u32, - ); - } - if (self.fallback_preference == FallbackPreference::ExitCountryWithFallback - && !self.exit_countries.contains(&country_code)) - || country_code == ZZ_COUNTRY_CODE_STRING - { - node_record.metadata.country_undesirability = UNREACHABLE_COUNTRY_PENALTY; - } - } - } - None => (), - } - } - - fn calculate_country_undesirability(priority: u32) -> u32 { - COUNTRY_UNDESIRABILITY_FACTOR * priority - } -} - pub struct Neighborhood { cryptde: &'static dyn CryptDE, hopper_opt: Option>, @@ -432,38 +370,6 @@ impl Handler for Neighborhood { } } -#[derive(Debug, PartialEq, Eq, Clone)] -pub struct AccessibleGossipRecord { - pub signed_gossip: PlainData, - pub signature: CryptData, - pub node_addr_opt: Option, - pub inner: NodeRecordInner_0v1, -} - -impl AccessibleGossipRecord { - pub fn regenerate_signed_gossip(&mut self, cryptde: &dyn CryptDE) { - let (signed_gossip, signature) = regenerate_signed_gossip(&self.inner, cryptde); - self.signed_gossip = signed_gossip; - self.signature = signature; - } -} - -impl TryFrom for AccessibleGossipRecord { - type Error = String; - - fn try_from(value: GossipNodeRecord) -> Result { - match serde_cbor::de::from_slice(value.signed_data.as_slice()) { - Ok(inner) => Ok(AccessibleGossipRecord { - signed_gossip: value.signed_data, - signature: value.signature, - node_addr_opt: value.node_addr_opt, - inner, - }), - Err(e) => Err(format!("{}", e)), - } - } -} - #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum RouteDirection { Over, @@ -580,8 +486,7 @@ impl Neighborhood { fn handle_new_ip_location(&mut self, new_public_ip: IpAddr) { let node_location_opt = get_node_location(Some(new_public_ip)); - let root_node = self.neighborhood_database - .root_mut(); + let root_node = self.neighborhood_database.root_mut(); root_node.metadata.node_location_opt = node_location_opt.clone(); root_node.inner.country_code_opt = node_location_opt.map(|nl| nl.country_code); } @@ -649,7 +554,9 @@ impl Neighborhood { ); } self.user_exit_preferences.db_countries = self.init_db_countries(); - if let Some(exit_locations_by_priority) = self.user_exit_preferences.locations_opt.clone() { + if let Some(exit_locations_by_priority) = + self.user_exit_preferences.locations_opt.clone() + { for exit_location in &exit_locations_by_priority { self.enrich_exit_countries(&exit_location.country_codes); } @@ -1325,12 +1232,12 @@ impl Neighborhood { { true // Zero- and single-hop routes are not subject to exit-too-close restrictions } else { - if let Some(node_record) = self.neighborhood_database.node_by_key(last_node_key) { - if let Some(country_code) = &node_record.inner.country_code_opt { + if let Some(node_record) = self.neighborhood_database.node_by_key(last_node_key) { + if let Some(country_code) = &node_record.inner.country_code_opt { return self .user_exit_preferences .exit_countries - .contains(country_code) + .contains(country_code); } } false @@ -1344,15 +1251,23 @@ impl Neighborhood { logger: &Logger, ) -> i64 { match undesirability_type { - UndesirabilityType::Relay => node_record.inner.rate_pack.routing_charge(payload_size) as i64, - UndesirabilityType::ExitRequest(None) => node_record.inner.rate_pack.exit_charge(payload_size) as i64 - + node_record.metadata.country_undesirability as i64, + UndesirabilityType::Relay => { + node_record.inner.rate_pack.routing_charge(payload_size) as i64 + } + UndesirabilityType::ExitRequest(None) => { + node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.metadata.country_undesirability as i64 + } UndesirabilityType::ExitRequest(Some(hostname)) => { - let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; + let exit_undesirability = + node_record.inner.rate_pack.exit_charge(payload_size) as i64; let country_undesirability = node_record.metadata.country_undesirability as i64; - let unreachable_undesirability = - if node_record.metadata.unreachable_hosts.contains(hostname) { - trace!( + let unreachable_undesirability = if node_record + .metadata + .unreachable_hosts + .contains(hostname) + { + trace!( logger, "Node with PubKey {:?} failed to reach host {:?} during ExitRequest; Undesirability: {} + {} + {} = {}", node_record.public_key(), @@ -1362,14 +1277,16 @@ impl Neighborhood { country_undesirability, exit_undesirability + UNREACHABLE_HOST_PENALTY + country_undesirability ); - UNREACHABLE_HOST_PENALTY - } else { - 0i64 - }; + UNREACHABLE_HOST_PENALTY + } else { + 0i64 + }; exit_undesirability + unreachable_undesirability + country_undesirability - }, - UndesirabilityType::ExitAndRouteResponse => node_record.inner.rate_pack.exit_charge(payload_size) as i64 - + node_record.inner.rate_pack.routing_charge(payload_size) as i64 + } + UndesirabilityType::ExitAndRouteResponse => { + node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.inner.rate_pack.routing_charge(payload_size) as i64 + } } } @@ -1713,7 +1630,9 @@ impl Neighborhood { }; let fallback_status = match self.user_exit_preferences.fallback_preference { - FallbackPreference::Nothing | FallbackPreference::ExitCountryWithFallback => "Fallback Routing is set.", + FallbackPreference::Nothing | FallbackPreference::ExitCountryWithFallback => { + "Fallback Routing is set." + } FallbackPreference::ExitCountryNoFallback => "Fallback Routing NOT set.", }; @@ -1723,7 +1642,11 @@ impl Neighborhood { match self.neighborhood_database.keys().len() > 1 { true => { self.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); - self.exit_location_logger_output(exit_locations_by_priority, &missing_locations, fallback_status); + self.exit_location_logger_output( + exit_locations_by_priority, + &missing_locations, + fallback_status, + ); } false => info!( self.logger, @@ -1743,8 +1666,12 @@ impl Neighborhood { .expect("UiGateway is dead"); } - fn exit_location_logger_output(&mut self, exit_locations_by_priority: Vec, missing_locations: &Vec, fallback_status: &str) { - + fn exit_location_logger_output( + &mut self, + exit_locations_by_priority: Vec, + missing_locations: &Vec, + fallback_status: &str, + ) { self.logger.info(|| { let location_set = ExitLocationSet { locations: exit_locations_by_priority, @@ -1753,12 +1680,16 @@ impl Neighborhood { false => "Exit location set: ", true => "Exit location unset.", }; - format!("{} {}{}", fallback_status, exit_location_status, location_set) + format!( + "{} {}{}", + fallback_status, exit_location_status, location_set + ) }); if !missing_locations.is_empty() { warning!( self.logger, - "Exit Location: following desired countries are missing in Neighborhood {:?}", &missing_locations + "Exit Location: following desired countries are missing in Neighborhood {:?}", + &missing_locations ); } } @@ -1871,7 +1802,8 @@ impl Neighborhood { .exit_locations .into_iter() .map(|cc| { - countries_lack_in_neighborhood.extend(self.enrich_exit_countries(&cc.country_codes)); + countries_lack_in_neighborhood + .extend(self.enrich_exit_countries(&cc.country_codes)); ExitLocation { country_codes: cc.country_codes, priority: cc.priority, @@ -1887,19 +1819,24 @@ impl Neighborhood { for code in country_codes { if self.user_exit_preferences.db_countries.contains(code) || (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback) + == FallbackPreference::ExitCountryWithFallback) { if !self.user_exit_preferences.exit_countries.contains(code) { self.user_exit_preferences.exit_countries.push(code.clone()); } if (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback) && - !self.user_exit_preferences.db_countries.contains(code) + == FallbackPreference::ExitCountryWithFallback) + && !self.user_exit_preferences.db_countries.contains(code) { countries_lack_in_neighborhood.push(code.clone()); } } else { - if let Some(index) = self.user_exit_preferences.exit_countries.iter().position(|item| item.eq(code)) { + if let Some(index) = self + .user_exit_preferences + .exit_countries + .iter() + .position(|item| item.eq(code)) + { self.user_exit_preferences.exit_countries.remove(index); } countries_lack_in_neighborhood.push(code.clone()); @@ -2100,17 +2037,68 @@ impl Neighborhood { } } -pub fn regenerate_signed_gossip( - inner: &NodeRecordInner_0v1, - cryptde: &dyn CryptDE, // Must be the correct CryptDE for the Node from which inner came: used for signing -) -> (PlainData, CryptData) { - let signed_gossip = - PlainData::from(serde_cbor::ser::to_vec(&inner).expect("Serialization failed")); - let signature = match cryptde.sign(&signed_gossip) { - Ok(sig) => sig, - Err(e) => unimplemented!("TODO: Signing error: {:?}", e), - }; - (signed_gossip, signature) +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ExitLocationsRoutes<'a> { + routes: Vec<(Vec<&'a PublicKey>, i64)>, +} + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum FallbackPreference { + Nothing, + ExitCountryWithFallback, + ExitCountryNoFallback, +} + +#[derive(Clone, Debug)] +pub struct UserExitPreferences { + exit_countries: Vec, //if we cross number of country_codes used in one workflow over 34, we want to change this member to HashSet + fallback_preference: FallbackPreference, + locations_opt: Option>, //TODO remove Option from NeighborhoodMetadata and create there TODO to ptimize it in future via reference + db_countries: Vec, +} + +impl UserExitPreferences { + fn new() -> UserExitPreferences { + UserExitPreferences { + exit_countries: vec![], + fallback_preference: FallbackPreference::Nothing, + locations_opt: None, + db_countries: vec![], + } + } + + pub fn assign_nodes_country_undesirability(&self, node_record: &mut NodeRecord) { + let country_code = node_record + .inner + .country_code_opt + .clone() + .unwrap_or_else(|| ZZ_COUNTRY_CODE_STRING.to_string()); + match &self.locations_opt { + Some(exit_locations_by_priority) => { + for exit_location in exit_locations_by_priority { + if exit_location.country_codes.contains(&country_code) + && country_code != ZZ_COUNTRY_CODE_STRING + { + node_record.metadata.country_undesirability = + Self::calculate_country_undesirability( + (exit_location.priority - 1) as u32, + ); + } + if (self.fallback_preference == FallbackPreference::ExitCountryWithFallback + && !self.exit_countries.contains(&country_code)) + || country_code == ZZ_COUNTRY_CODE_STRING + { + node_record.metadata.country_undesirability = UNREACHABLE_COUNTRY_PENALTY; + } + } + } + None => (), + } + } + + fn calculate_country_undesirability(priority: u32) -> u32 { + COUNTRY_UNDESIRABILITY_FACTOR * priority + } } #[derive(PartialEq, Eq, Debug)] @@ -2164,11 +2152,11 @@ mod tests { use masq_lib::utils::running_test; use crate::db_config::persistent_configuration::PersistentConfigError; - use crate::neighborhood::gossip::GossipBuilder; + use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord}; use crate::neighborhood::gossip::Gossip_0v1; use crate::neighborhood::node_record::{NodeRecordInner_0v1, NodeRecordInputs}; use crate::stream_messages::{NonClandestineAttributes, RemovedStreamType}; - use crate::sub_lib::cryptde::{decodex, encodex, CryptData}; + use crate::sub_lib::cryptde::{decodex, encodex, CryptData, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; @@ -3588,14 +3576,34 @@ mod tests { let neighbor_two = make_node_record(2345, true); //FR let neighbor_three = make_node_record(3456, true); //CN let neighbor_four = make_node_record(4567, true); //US - subject.neighborhood_database.add_node(neighbor_one.clone()).unwrap(); - subject.neighborhood_database.add_node(neighbor_two.clone()).unwrap(); - subject.neighborhood_database.add_node(neighbor_three.clone()).unwrap(); - subject.neighborhood_database.add_node(neighbor_four.clone()).unwrap(); - subject.neighborhood_database.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_one.public_key()); - subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_one.public_key(), neighbor_two.public_key()); - subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); - subject.neighborhood_database.add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); + subject + .neighborhood_database + .add_node(neighbor_one.clone()) + .unwrap(); + subject + .neighborhood_database + .add_node(neighbor_two.clone()) + .unwrap(); + subject + .neighborhood_database + .add_node(neighbor_three.clone()) + .unwrap(); + subject + .neighborhood_database + .add_node(neighbor_four.clone()) + .unwrap(); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(root_node.public_key(), neighbor_one.public_key()); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(neighbor_one.public_key(), neighbor_two.public_key()); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); subject.user_exit_preferences.db_countries = subject.init_db_countries(); let exit_locations_by_priority = vec![ExitLocation { country_codes: vec!["FR".to_string(), "US".to_string()], @@ -3604,7 +3612,8 @@ mod tests { for exit_location in &exit_locations_by_priority { subject.enrich_exit_countries(&exit_location.country_codes); } - subject.user_exit_preferences.fallback_preference = FallbackPreference::ExitCountryNoFallback; + subject.user_exit_preferences.fallback_preference = + FallbackPreference::ExitCountryNoFallback; subject.user_exit_preferences.locations_opt = Some(exit_locations_by_priority); let tree_hop_db_countries = subject.user_exit_preferences.db_countries.clone(); let tree_hops_exit_countries = subject.user_exit_preferences.exit_countries.clone(); @@ -3624,10 +3633,19 @@ mod tests { let four_hops_exit_countries = subject.user_exit_preferences.exit_countries.clone(); let four_hops_db_countries = subject.user_exit_preferences.db_countries; - assert_eq!(tree_hop_db_countries, vec!["CN".to_string(), "US".to_string()]); + assert_eq!( + tree_hop_db_countries, + vec!["CN".to_string(), "US".to_string()] + ); assert_eq!(tree_hops_exit_countries, vec!["US".to_string()]); - assert_eq!(two_hops_db_countries, vec!["CN".to_string(), "FR".to_string(), "US".to_string()]); - assert_eq!(two_hops_exit_countries, vec!["US".to_string(), "FR".to_string()]); + assert_eq!( + two_hops_db_countries, + vec!["CN".to_string(), "FR".to_string(), "US".to_string()] + ); + assert_eq!( + two_hops_exit_countries, + vec!["US".to_string(), "FR".to_string()] + ); assert_eq!(four_hops_db_countries, vec!["US".to_string()]); assert_eq!(four_hops_exit_countries, vec!["US".to_string()]); } diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs index e3e56fb58..35cc1af91 100644 --- a/node/src/neighborhood/node_record.rs +++ b/node/src/neighborhood/node_record.rs @@ -1,9 +1,10 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::neighborhood::gossip::GossipNodeRecord; +use crate::neighborhood::gossip::{ + regenerate_signed_gossip, AccessibleGossipRecord, GossipNodeRecord, +}; use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, NeighborhoodDatabaseError}; use crate::neighborhood::node_location::{get_node_location, NodeLocation}; -use crate::neighborhood::{regenerate_signed_gossip, AccessibleGossipRecord}; use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData, PublicKey}; use crate::sub_lib::neighborhood::{NodeDescriptor, RatePack}; use crate::sub_lib::node_addr::NodeAddr; @@ -381,8 +382,8 @@ mod tests { let mut node_record_wo_location = make_node_record(2222, false); node_record_wo_location.inner.accepts_connections = false; let no_location_db = db_from_node(&node_record_wo_location); - let no_location_gossip = - GossipBuilder::new(&no_location_db).node(node_record_wo_location.public_key(), false) + let no_location_gossip = GossipBuilder::new(&no_location_db) + .node(node_record_wo_location.public_key(), false) .build(); let nr_wo_location = diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs index a3f415f93..1b0af210f 100644 --- a/node/src/test_utils/neighborhood_test_utils.rs +++ b/node/src/test_utils/neighborhood_test_utils.rs @@ -1,10 +1,12 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::bootstrapper::BootstrapperConfig; -use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord, Gossip_0v1}; +use crate::neighborhood::gossip::{ + AccessibleGossipRecord, GossipBuilder, GossipNodeRecord, Gossip_0v1, +}; use crate::neighborhood::neighborhood_database::NeighborhoodDatabase; use crate::neighborhood::node_location::NodeLocation; use crate::neighborhood::node_record::{NodeRecord, NodeRecordInner_0v1, NodeRecordInputs}; -use crate::neighborhood::{AccessibleGossipRecord, Neighborhood, DEFAULT_MIN_HOPS}; +use crate::neighborhood::{Neighborhood, DEFAULT_MIN_HOPS}; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::cryptde::{CryptDE, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; @@ -34,8 +36,8 @@ lazy_static! { "US".to_string(), true ), - ( - IpAddr::V4(Ipv4Addr::new(99, 99, 99, 99)), + ( + IpAddr::V4(Ipv4Addr::new(99, 99, 99, 99)), "FR".to_string(), true ), @@ -83,7 +85,7 @@ pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord { let key = PublicKey::new(&[seg1, seg2, seg3, seg4]); let ip_addr = make_segmented_ip(seg1, seg2, seg3, seg4); let node_addr = NodeAddr::new(&ip_addr, &[n % 10000]); - let (_ip, country_code, free_world_bit) = pick_country_code_record(n); + let (_ip, country_code, free_world_bit) = pick_country_code_record(n); let location_opt = match has_ip { true => match country_code.is_empty() { false => Some(NodeLocation { @@ -105,7 +107,7 @@ pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord { ) } -pub fn pick_country_code_record(n: u16) -> (IpAddr, String, bool) { +pub fn pick_country_code_record(n: u16) -> (IpAddr, String, bool) { COUNTRY_CODE_DIGEST[n as usize % COUNTRY_CODE_DIGEST.len()].clone() } From 04807b0bd4554f656673022d6e04467268192452 Mon Sep 17 00:00:00 2001 From: czarte Date: Mon, 24 Mar 2025 14:19:58 +0100 Subject: [PATCH 11/27] fix db to dot grap test --- node/src/neighborhood/neighborhood_database.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/neighborhood/neighborhood_database.rs b/node/src/neighborhood/neighborhood_database.rs index 2d99bf088..ddb0fb879 100644 --- a/node/src/neighborhood/neighborhood_database.rs +++ b/node/src/neighborhood/neighborhood_database.rs @@ -849,7 +849,7 @@ mod tests { ); assert_string_contains( &result, - "\"AwQFBg\" [label=\"AR v0 FR\\nAwQFBg\\n3.4.5.6:3456\"];", + "\"AwQFBg\" [label=\"AR v0 CN\\nAwQFBg\\n3.4.5.6:3456\"];", ); assert_string_contains( &result, From 7fb6d7e4c2e8d01c5d176021074fb1f987245f00 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 25 Mar 2025 14:57:33 +0100 Subject: [PATCH 12/27] addressing more comments from review --- node/src/neighborhood/mod.rs | 98 ++++++++++++++++++++---------------- 1 file changed, 55 insertions(+), 43 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 69207e5e9..a2b217fb7 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -1195,12 +1195,9 @@ impl Neighborhood { } } - fn validate_fallback_country_exit_codes(&self, last_node: &PublicKey) -> bool { + fn validate_country_code_when_fallback_routing(&self, last_node: &PublicKey) -> bool { let last_cc = match self.neighborhood_database.node_by_key(last_node) { - Some(nr) => match nr.inner.country_code_opt.clone() { - Some(cc) => cc, - None => "ZZ".to_string(), - }, + Some(nr) => nr.inner.country_code_opt.clone().unwrap_or_else(|| "ZZ".to_string()), None => "ZZ".to_string(), }; if self.user_exit_preferences.exit_countries.contains(&last_cc) { @@ -1226,7 +1223,7 @@ impl Neighborhood { if self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing || (self.user_exit_preferences.fallback_preference == FallbackPreference::ExitCountryWithFallback - && self.validate_fallback_country_exit_codes(last_node_key)) + && self.validate_country_code_when_fallback_routing(last_node_key)) || research_neighborhood || direction == RouteDirection::Back { @@ -1616,7 +1613,7 @@ impl Neighborhood { client_id: u64, context_id: u64, ) { - //TODO write test that contains more CountryCodes than countries in neighborhood db to check if unexistent country codes in db are filtered out from ExitLocation + //TODO write test that contains more CountryGroups than countries in neighborhood db to check if unexistent country codes in db are filtered out from ExitLocation let (exit_locations_by_priority, missing_locations) = self.extract_exit_locations_from_message(&message); @@ -1717,20 +1714,9 @@ impl Neighborhood { missing_countries: Vec, show_countries_flag: bool, ) -> NodeToUiMessage { - let fallback_routing = match &self.user_exit_preferences.fallback_preference { - FallbackPreference::Nothing => true, - FallbackPreference::ExitCountryWithFallback => true, - FallbackPreference::ExitCountryNoFallback => false, - }; - let exit_locations = self - .user_exit_preferences - .locations_opt - .clone() - .unwrap_or_default(); - let countries_to_show = match show_countries_flag { - true => Some(self.user_exit_preferences.db_countries.clone()), - false => None, - }; + let fallback_routing = self.is_fallback_routing_active(); + let exit_locations = self.get_locations_opt(); + let countries_to_show = self.get_countries_to_show(show_countries_flag); if !self.error_message_indicates(&mut missing_countries.clone()) { NodeToUiMessage { @@ -1756,6 +1742,28 @@ impl Neighborhood { } } + fn get_countries_to_show(&self, show_countries_flag: bool) -> Option> { + match show_countries_flag { + true => Some(self.user_exit_preferences.db_countries.clone()), + false => None, + } + } + + fn is_fallback_routing_active(&self) -> bool { + match &self.user_exit_preferences.fallback_preference { + FallbackPreference::Nothing => true, + FallbackPreference::ExitCountryWithFallback => true, + FallbackPreference::ExitCountryNoFallback => false, + } + } + + fn get_locations_opt(&self) -> Vec { + self.user_exit_preferences + .locations_opt + .clone() + .unwrap_or_default() + } + fn set_exit_locations_opt(&mut self, exit_locations_by_priority: &[ExitLocation]) { self.user_exit_preferences.locations_opt = match self.user_exit_preferences.exit_countries.is_empty() { @@ -1792,8 +1800,6 @@ impl Neighborhood { &mut self, message: &UiSetExitLocationRequest, ) -> (Vec, Vec) { - //TODO perform following update of db_countries only in Gossip_Acceptor - //TODO implement enrich_exit_countries in handle_config_change_msg self.user_exit_preferences.db_countries = self.init_db_countries(); let mut countries_lack_in_neighborhood = vec![]; ( @@ -2076,17 +2082,12 @@ impl UserExitPreferences { match &self.locations_opt { Some(exit_locations_by_priority) => { for exit_location in exit_locations_by_priority { - if exit_location.country_codes.contains(&country_code) - && country_code != ZZ_COUNTRY_CODE_STRING + if Self::should_set_country_undesirability(&country_code, exit_location) { node_record.metadata.country_undesirability = - Self::calculate_country_undesirability( - (exit_location.priority - 1) as u32, - ); + Self::calculate_country_undesirability(exit_location.priority as u32); } - if (self.fallback_preference == FallbackPreference::ExitCountryWithFallback - && !self.exit_countries.contains(&country_code)) - || country_code == ZZ_COUNTRY_CODE_STRING + if self.is_unreachable_country_penalty(&country_code) { node_record.metadata.country_undesirability = UNREACHABLE_COUNTRY_PENALTY; } @@ -2096,8 +2097,19 @@ impl UserExitPreferences { } } + fn should_set_country_undesirability(country_code: &String, exit_location: &ExitLocation) -> bool { + exit_location.country_codes.contains(&country_code) + && country_code != ZZ_COUNTRY_CODE_STRING + } + + fn is_unreachable_country_penalty(&self, country_code: &String) -> bool { + (self.fallback_preference == FallbackPreference::ExitCountryWithFallback + && !self.exit_countries.contains(&country_code)) + || country_code == ZZ_COUNTRY_CODE_STRING + } + fn calculate_country_undesirability(priority: u32) -> u32 { - COUNTRY_UNDESIRABILITY_FACTOR * priority + COUNTRY_UNDESIRABILITY_FACTOR * (priority - 1u32) } } @@ -2143,7 +2155,7 @@ mod tests { use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; use masq_lib::messages::{ - CountryCodes, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, + CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, }; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; use masq_lib::ui_gateway::MessageBody; @@ -3744,15 +3756,15 @@ mod tests { let request = UiSetExitLocationRequest { fallback_routing: true, exit_locations: vec![ - CountryCodes { + CountryGroups { country_codes: vec!["CZ".to_string(), "SK".to_string()], priority: 1, }, - CountryCodes { + CountryGroups { country_codes: vec!["AT".to_string(), "DE".to_string()], priority: 2, }, - CountryCodes { + CountryGroups { country_codes: vec!["PL".to_string()], priority: 3, }, @@ -3904,7 +3916,7 @@ mod tests { let test_name = "exit_location_with_multiple_countries_and_priorities_can_be_changed_using_exit_location_msg"; let request = UiSetExitLocationRequest { fallback_routing: true, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["CZ".to_string(), "SK".to_string(), "IN".to_string()], priority: 1, }], @@ -4088,11 +4100,11 @@ mod tests { let request = UiSetExitLocationRequest { fallback_routing: false, exit_locations: vec![ - CountryCodes { + CountryGroups { country_codes: vec!["CZ".to_string()], priority: 1, }, - CountryCodes { + CountryGroups { country_codes: vec!["FR".to_string()], priority: 2, }, @@ -4773,7 +4785,7 @@ mod tests { subject.node_to_ui_recipient_opt = Some(recipient); let message = UiSetExitLocationRequest { fallback_routing: false, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["CZ".to_string()], priority: 1, }], @@ -4875,7 +4887,7 @@ mod tests { FallbackPreference::ExitCountryWithFallback; let message = UiSetExitLocationRequest { fallback_routing: false, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["CZ".to_string()], priority: 1, }], @@ -4934,7 +4946,7 @@ mod tests { subject.node_to_ui_recipient_opt = Some(recipient); let message = UiSetExitLocationRequest { fallback_routing: true, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["AU".to_string()], priority: 1, }], @@ -4995,7 +5007,7 @@ mod tests { subject.node_to_ui_recipient_opt = Some(recipient); let message = UiSetExitLocationRequest { fallback_routing: false, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["FR".to_string()], priority: 1, }], From c1682ad9444ba850d1d93ea3627ca10bbe702cf7 Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 26 Mar 2025 16:48:12 +0100 Subject: [PATCH 13/27] addressing minor changes from review --- masq_lib/src/constants.rs | 10 +++ masq_lib/src/messages.rs | 10 ++- node/src/neighborhood/mod.rs | 114 ++++++++++++--------------- node/src/neighborhood/node_record.rs | 1 + 4 files changed, 67 insertions(+), 68 deletions(-) diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index 8393b1cd9..4962dd83e 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -26,6 +26,8 @@ pub const WEIS_IN_GWEI: i128 = 1_000_000_000; pub const DEFAULT_MAX_BLOCK_COUNT: u64 = 100_000; +pub const PAYLOAD_ZERO_SIZE: usize = 0usize; + pub const ETH_MAINNET_CONTRACT_CREATION_BLOCK: u64 = 11_170_708; pub const ETH_ROPSTEN_CONTRACT_CREATION_BLOCK: u64 = 8_688_171; pub const POLYGON_MAINNET_CONTRACT_CREATION_BLOCK: u64 = 14_863_650; @@ -202,6 +204,14 @@ mod tests { NODE_RECORD_INNER_CURRENT_VERSION, DataVersion { major: 0, minor: 1 } ); + assert_eq!( + PAYLOAD_ZERO_SIZE, + 0usize + ); + assert_eq!( + DEFAULT_PREALLOCATION_VEC, + 10 + ) } #[test] diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index 426b95f7e..bea7e2ab9 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -848,16 +848,18 @@ pub struct UiWalletAddressesResponse { } conversation_message!(UiWalletAddressesResponse, "walletAddresses"); +// CountryGroups are inbound data for ExitLocations from UI. These data structures could be enriched +// in the future according to future user interface needs of more specification #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] -pub struct CountryCodes { +pub struct CountryGroups { #[serde(rename = "countryCodes")] pub country_codes: Vec, pub priority: usize, } -impl From<(String, usize)> for CountryCodes { +impl From<(String, usize)> for CountryGroups { fn from((country, priority): (String, usize)) -> Self { - CountryCodes { + CountryGroups { country_codes: country .split(',') .into_iter() @@ -873,7 +875,7 @@ pub struct UiSetExitLocationRequest { #[serde(rename = "fallbackRouting")] pub fallback_routing: bool, #[serde(rename = "exitLocations")] - pub exit_locations: Vec, + pub exit_locations: Vec, #[serde(rename = "showCountries")] pub show_countries: bool, } diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index a2b217fb7..e20481b27 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -60,7 +60,7 @@ use gossip_producer::GossipProducer; use gossip_producer::GossipProducerReal; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR}; +use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, PAYLOAD_ZERO_SIZE}; use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; @@ -74,7 +74,7 @@ use masq_lib::ui_gateway::{MessageBody, MessageTarget, NodeFromUiMessage, NodeTo use masq_lib::utils::{exit_process, ExpectValue, NeighborhoodModeLight}; use neighborhood_database::NeighborhoodDatabase; use node_record::NodeRecord; -use std::collections::{HashMap, HashSet}; +use std::collections::HashSet; use std::convert::TryFrom; use std::fmt::Debug; use std::net::{IpAddr, SocketAddr}; @@ -85,6 +85,7 @@ pub const CRASH_KEY: &str = "NEIGHBORHOOD"; pub const DEFAULT_MIN_HOPS: Hops = Hops::ThreeHops; pub const UNREACHABLE_HOST_PENALTY: i64 = 100_000_000; pub const UNREACHABLE_COUNTRY_PENALTY: u32 = 100_000_000; +pub const ZERO_UNDESIRABILITY: u32 = 0; pub const COUNTRY_UNDESIRABILITY_FACTOR: u32 = 1_000; pub const RESPONSE_UNDESIRABILITY_FACTOR: usize = 1_000; // assumed response length is request * this pub const ZZ_COUNTRY_CODE_STRING: &str = "ZZ"; @@ -1220,12 +1221,7 @@ impl Neighborhood { research_neighborhood: bool, direction: RouteDirection, ) -> bool { - if self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing - || (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback - && self.validate_country_code_when_fallback_routing(last_node_key)) - || research_neighborhood - || direction == RouteDirection::Back + if self.is_always_true(last_node_key, research_neighborhood, direction) { true // Zero- and single-hop routes are not subject to exit-too-close restrictions } else { @@ -1241,6 +1237,15 @@ impl Neighborhood { } } + fn is_always_true(&self, last_node_key: &PublicKey, research_neighborhood: bool, direction: RouteDirection) -> bool { + self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing + || (self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback + && self.validate_country_code_when_fallback_routing(last_node_key)) + || research_neighborhood + || direction == RouteDirection::Back + } + fn compute_undesirability( node_record: &NodeRecord, payload_size: u64, @@ -1248,13 +1253,9 @@ impl Neighborhood { logger: &Logger, ) -> i64 { match undesirability_type { - UndesirabilityType::Relay => { - node_record.inner.rate_pack.routing_charge(payload_size) as i64 - } - UndesirabilityType::ExitRequest(None) => { - node_record.inner.rate_pack.exit_charge(payload_size) as i64 - + node_record.metadata.country_undesirability as i64 - } + UndesirabilityType::Relay => node_record.inner.rate_pack.routing_charge(payload_size) as i64, + UndesirabilityType::ExitRequest(None) => node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.metadata.country_undesirability as i64, UndesirabilityType::ExitRequest(Some(hostname)) => { let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; @@ -1311,40 +1312,24 @@ impl Neighborhood { &'a self, source: &'a PublicKey, minimum_hops: usize, - payload_size: usize, ) -> Vec<&'a PublicKey> { let mut minimum_undesirability = i64::MAX; let initial_undesirability = 0; let research_exits: &mut Vec<&'a PublicKey> = &mut vec![]; let mut prefix = Vec::with_capacity(DEFAULT_PREALLOCATION_VEC); prefix.push(source); - let over_routes = self.routing_engine( + let _ = self.routing_engine( prefix, initial_undesirability, None, minimum_hops, - payload_size, + PAYLOAD_ZERO_SIZE, RouteDirection::Over, &mut minimum_undesirability, None, true, research_exits, ); - let mut result_exit: HashMap = HashMap::new(); - over_routes.into_iter().for_each(|segment| { - if !segment.nodes.is_empty() { - let exit_node = segment.nodes[segment.nodes.len() - 1]; - result_exit - .entry(exit_node.clone()) - .and_modify(|e| { - e.routes - .push((segment.nodes.clone(), segment.undesirability)) - }) - .or_insert(ExitLocationsRoutes { - routes: vec![(segment.nodes.clone(), segment.undesirability)], - }); - } - }); research_exits.to_vec() } @@ -1368,7 +1353,8 @@ impl Neighborhood { let mut minimum_undesirability = i64::MAX; let initial_undesirability = self.compute_initial_undesirability(source, payload_size as u64, direction); - let mut prefix = Vec::with_capacity(10); + let mut prefix = Vec::with_capacity(DEFAULT_PREALLOCATION_VEC); + //TODO we can have an investigation, if this DEFAULT_PREALLOCATION_VEC is not too much, same in find_exit_locations prefix.push(source); let result = self .routing_engine( @@ -1614,7 +1600,7 @@ impl Neighborhood { context_id: u64, ) { //TODO write test that contains more CountryGroups than countries in neighborhood db to check if unexistent country codes in db are filtered out from ExitLocation - let (exit_locations_by_priority, missing_locations) = + let (exit_locations_by_priority, missing_countries) = self.extract_exit_locations_from_message(&message); self.user_exit_preferences.fallback_preference = match ( @@ -1641,7 +1627,7 @@ impl Neighborhood { self.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); self.exit_location_logger_output( exit_locations_by_priority, - &missing_locations, + &missing_countries, fallback_status, ); } @@ -1653,7 +1639,7 @@ impl Neighborhood { let message = self.create_exit_location_response( client_id, context_id, - missing_locations, + missing_countries, message.show_countries, ); self.node_to_ui_recipient_opt @@ -1702,8 +1688,8 @@ impl Neighborhood { if desired_countries.is_empty() && missing_countries.is_empty() { return false; } - missing_countries.sort(); desired_countries.sort(); + missing_countries.sort(); missing_countries == &desired_countries } @@ -1711,14 +1697,23 @@ impl Neighborhood { &self, client_id: u64, context_id: u64, - missing_countries: Vec, + mut missing_countries: Vec, show_countries_flag: bool, ) -> NodeToUiMessage { let fallback_routing = self.is_fallback_routing_active(); let exit_locations = self.get_locations_opt(); let countries_to_show = self.get_countries_to_show(show_countries_flag); - - if !self.error_message_indicates(&mut missing_countries.clone()) { + let missing_countries_message: String = missing_countries.join(", "); + if self.error_message_indicates(&mut missing_countries) { + NodeToUiMessage { + target: MessageTarget::ClientId(client_id), + body: MessageBody { + opcode: "exitLocation".to_string(), + path: Conversation(context_id), + payload: Err((EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, missing_countries_message)), + }, + } + } else { NodeToUiMessage { target: MessageTarget::ClientId(client_id), body: UiSetExitLocationResponse { @@ -1727,17 +1722,7 @@ impl Neighborhood { exit_countries: countries_to_show, missing_countries, } - .tmb(context_id), - } - } else { - let missing_message: String = missing_countries.join(", "); - NodeToUiMessage { - target: MessageTarget::ClientId(client_id), - body: MessageBody { - opcode: "exitLocation".to_string(), - path: Conversation(context_id), - payload: Err((EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, missing_message)), - }, + .tmb(context_id), } } } @@ -1790,7 +1775,7 @@ impl Neighborhood { false => { self.user_exit_preferences.exit_countries = vec![]; for node_record in nodes { - node_record.metadata.country_undesirability = 0u32; + node_record.metadata.country_undesirability = ZERO_UNDESIRABILITY; } } } @@ -1801,27 +1786,28 @@ impl Neighborhood { message: &UiSetExitLocationRequest, ) -> (Vec, Vec) { self.user_exit_preferences.db_countries = self.init_db_countries(); - let mut countries_lack_in_neighborhood = vec![]; + let mut countries_not_in_neighborhood = vec![]; ( message .to_owned() .exit_locations .into_iter() .map(|cc| { - countries_lack_in_neighborhood - .extend(self.enrich_exit_countries(&cc.country_codes)); + let requested_country_codes = &cc.country_codes; + countries_not_in_neighborhood + .extend(self.enrich_exit_countries(requested_country_codes)); ExitLocation { country_codes: cc.country_codes, priority: cc.priority, } }) .collect(), - countries_lack_in_neighborhood, + countries_not_in_neighborhood, ) } fn enrich_exit_countries(&mut self, country_codes: &Vec) -> Vec { - let mut countries_lack_in_neighborhood = vec![]; + let mut countries_not_in_neighborhood = vec![]; for code in country_codes { if self.user_exit_preferences.db_countries.contains(code) || (self.user_exit_preferences.fallback_preference @@ -1834,7 +1820,7 @@ impl Neighborhood { == FallbackPreference::ExitCountryWithFallback) && !self.user_exit_preferences.db_countries.contains(code) { - countries_lack_in_neighborhood.push(code.clone()); + countries_not_in_neighborhood.push(code.clone()); } } else { if let Some(index) = self @@ -1845,17 +1831,17 @@ impl Neighborhood { { self.user_exit_preferences.exit_countries.remove(index); } - countries_lack_in_neighborhood.push(code.clone()); + countries_not_in_neighborhood.push(code.clone()); } } - countries_lack_in_neighborhood + countries_not_in_neighborhood } fn init_db_countries(&mut self) -> Vec { let root_key = self.neighborhood_database.root_key(); let min_hops = self.min_hops as usize; let exit_nodes = self - .find_exit_locations(root_key, min_hops, 0usize) + .find_exit_locations(root_key, min_hops) .to_owned(); let mut db_countries = vec![]; if !exit_nodes.is_empty() { @@ -4554,7 +4540,7 @@ mod tests { join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); designate_root_node(db, &l); - let mut exit_nodes = subject.find_exit_locations(&l, 3, 10_000); + let mut exit_nodes = subject.find_exit_locations(&l, 3); let total_exit_nodes = exit_nodes.len(); exit_nodes.sort(); @@ -4600,7 +4586,7 @@ mod tests { }; designate_root_node(db, &n1); - let mut exit_nodes = subject.find_exit_locations(&n1, 3, 10_000); + let mut exit_nodes = subject.find_exit_locations(&n1, 3); let total_exit_nodes = exit_nodes.len(); exit_nodes.sort(); diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs index 35cc1af91..595e837ef 100644 --- a/node/src/neighborhood/node_record.rs +++ b/node/src/neighborhood/node_record.rs @@ -350,6 +350,7 @@ pub struct NodeRecordMetadata { pub node_location_opt: Option, pub country_undesirability: u32, //TODO introduce scores for latency #582 and reliability #583 + //TODO introduce check for node_location_opt, to verify full neighbors country code (we know his IP, so we can verify it) } impl NodeRecordMetadata { From 22936bfdddc74dad75e324a9122e80c53dffc673 Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 26 Mar 2025 18:52:34 +0100 Subject: [PATCH 14/27] addressing review comments --- node/src/neighborhood/mod.rs | 86 ++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index e20481b27..0b944fee5 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -1809,17 +1809,11 @@ impl Neighborhood { fn enrich_exit_countries(&mut self, country_codes: &Vec) -> Vec { let mut countries_not_in_neighborhood = vec![]; for code in country_codes { - if self.user_exit_preferences.db_countries.contains(code) - || (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback) - { + if self.code_in_db_countries_or_fallback_active(code) { if !self.user_exit_preferences.exit_countries.contains(code) { self.user_exit_preferences.exit_countries.push(code.clone()); } - if (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback) - && !self.user_exit_preferences.db_countries.contains(code) - { + if self.fallback_active_and_code_missing_in_db_countries(code) { countries_not_in_neighborhood.push(code.clone()); } } else { @@ -1837,6 +1831,18 @@ impl Neighborhood { countries_not_in_neighborhood } + fn fallback_active_and_code_missing_in_db_countries(&mut self, code: &String) -> bool { + (self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback) + && !self.user_exit_preferences.db_countries.contains(code) + } + + fn code_in_db_countries_or_fallback_active(&mut self, code: &String) -> bool { + self.user_exit_preferences.db_countries.contains(code) + || (self.user_exit_preferences.fallback_preference + == FallbackPreference::ExitCountryWithFallback) + } + fn init_db_countries(&mut self) -> Vec { let root_key = self.neighborhood_database.root_key(); let min_hops = self.min_hops as usize; @@ -2252,40 +2258,36 @@ mod tests { } #[test] - fn test_wtih_standard_gossip_have_new_exit_node_in_database() { + fn standard_gossip_results_in_exit_node_in_database() { let mut subject = make_standard_subject(); let root_node_key = subject.neighborhood_database.root_key().clone(); let root_node = subject.neighborhood_database.root().clone(); - let first_neighbor = make_node_record(1111, true); - let second_neighbor = make_node_record(2222, true); - + let source_node = make_node_record(1111, true); + let new_neighbor = make_node_record(2222, true); subject .neighborhood_database - .add_node(first_neighbor.clone()) + .add_node(source_node.clone()) .unwrap(); subject .neighborhood_database - .add_arbitrary_full_neighbor(&root_node_key, first_neighbor.public_key()); - let mut gossip_db = subject.neighborhood_database.clone(); - - gossip_db.set_root_key(first_neighbor.public_key()); - gossip_db.remove_node(&root_node_key); - gossip_db.add_node(root_node).unwrap(); - gossip_db.add_arbitrary_full_neighbor(first_neighbor.public_key(), &root_node_key); - gossip_db.add_node(second_neighbor.clone()).unwrap(); - gossip_db - .add_arbitrary_full_neighbor(first_neighbor.public_key(), second_neighbor.public_key()); - gossip_db.root_mut().inner.version = 1; - let resigner = gossip_db - .node_by_key_mut(first_neighbor.public_key()) + .add_arbitrary_full_neighbor(&root_node_key, source_node.public_key()); + let mut source_db = subject.neighborhood_database.clone(); + source_db.set_root_key(source_node.public_key()); + source_db.remove_node(&root_node_key); + source_db.add_node(root_node).unwrap(); + source_db.add_arbitrary_full_neighbor(source_node.public_key(), &root_node_key); + source_db.add_node(new_neighbor.clone()).unwrap(); + source_db + .add_arbitrary_full_neighbor(source_node.public_key(), new_neighbor.public_key()); + source_db.root_mut().inner.version = 1; + let resigner = source_db + .node_by_key_mut(source_node.public_key()) .unwrap(); resigner.resign(); - - let standard_gossip = GossipBuilder::new(&gossip_db) - .node(first_neighbor.public_key(), true) - .node(second_neighbor.public_key(), false) + let standard_gossip = GossipBuilder::new(&source_db) + .node(source_node.public_key(), true) + .node(new_neighbor.public_key(), false) .build(); - let peer_actors = peer_actors_builder().build(); subject.handle_bind_message(BindMessage { peer_actors }); subject.min_hops = Hops::OneHop; @@ -2305,7 +2307,7 @@ mod tests { } #[test] - fn test_with_introduction_always_have_half_neighborship_in_handle_gossip() { + fn introduction_always_results_with_half_neighborship_in_gossip() { let mut debut_subject = make_debut_subject(); let debut_root_key = debut_subject.neighborhood_database.root_key().clone(); let introducer_node = make_node_record(3333, true); @@ -2327,6 +2329,7 @@ mod tests { debut_subject.min_hops = Hops::OneHop; let exit_nodes_before_gossip = debut_subject.init_db_countries(); debut_subject.handle_bind_message(BindMessage { peer_actors }); + let introduction_gossip_check = introduction_gossip.clone(); debut_subject.handle_gossip( introduction_gossip, @@ -2334,6 +2337,7 @@ mod tests { make_cpm_recipient().0, ); + println!("introduction_gossip: {:?}", introduction_gossip_check.node_records); assert!(exit_nodes_before_gossip.is_empty()); assert_eq!( debut_subject.user_exit_preferences.db_countries, @@ -3758,7 +3762,7 @@ mod tests { show_countries: false, }; let message = NodeFromUiMessage { - client_id: 0, + client_id: 123, body: request.tmb(0), }; let system = System::new(test_name); @@ -3897,7 +3901,7 @@ mod tests { } #[test] - fn no_exit_location_is_set_if_desired_country_codes_not_present_in_neighborhood() { + fn no_exit_location_is_set_if_desired_country_codes_not_present_in_neighborhood_with_fallback_routing_set() { init_test_logging(); let test_name = "exit_location_with_multiple_countries_and_priorities_can_be_changed_using_exit_location_msg"; let request = UiSetExitLocationRequest { @@ -3956,7 +3960,7 @@ mod tests { let pl_public_key = pl.inner.public_key.clone(); let assertion_msg = AssertionsMessage { assertions: Box::new(move |neighborhood: &mut Neighborhood| { - assert!(neighborhood.user_exit_preferences.exit_countries.is_empty(),); + assert!(neighborhood.user_exit_preferences.exit_countries.is_empty()); assert_eq!( neighborhood.user_exit_preferences.locations_opt, Some(vec![ExitLocation { @@ -4048,12 +4052,10 @@ mod tests { subject_addr.try_send(BindMessage { peer_actors }).unwrap(); subject_addr.try_send(message).unwrap(); - subject_addr.try_send(assertion_msg).unwrap(); + subject_addr.try_send(assertion_msg).unwrap(); System::current().stop(); system.run(); - - //println!("recorder: {:#?}", &recorder.try_into().unwrap()); let exit_location_recording = &arc_recorder.lock().unwrap(); let exit_handler_response = exit_location_recording .get_record::(0) @@ -4777,7 +4779,6 @@ mod tests { }], show_countries: false, }; - println!("db {:?}", db.root()); let mut generator = 1000; let mut make_node = |db: &mut NeighborhoodDatabase| { let node = &db.add_node(make_node_record(generator, true)).unwrap(); @@ -4840,7 +4841,7 @@ mod tests { &route_cz .as_ref() .unwrap() - .get(route_cz.as_ref().unwrap().len() - 1) + .last() .unwrap(), ); assert_eq!( @@ -4865,7 +4866,6 @@ mod tests { #[test] fn find_best_segment_traces_unreachable_country_code_exit_node() { - init_test_logging(); let mut subject = make_standard_subject(); let (recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(recipient); @@ -4947,7 +4947,7 @@ mod tests { &route_au .as_ref() .unwrap() - .get(route_au.as_ref().unwrap().len() - 1) + .last() .unwrap(), ); assert_eq!( @@ -5008,7 +5008,7 @@ mod tests { &route_fr .as_ref() .unwrap() - .get(route_fr.as_ref().unwrap().len() - 1) + .last() .unwrap(), ); assert_eq!( From 3d4ffffcba91332735cd0d56583e9ac20559bb50 Mon Sep 17 00:00:00 2001 From: czarte Date: Thu, 27 Mar 2025 17:35:53 +0100 Subject: [PATCH 15/27] resolving comments from review --- node/src/neighborhood/mod.rs | 125 +++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 50 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 0b944fee5..c03fa4dc5 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -2150,7 +2150,7 @@ mod tests { CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, }; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::ui_gateway::MessageBody; + use masq_lib::ui_gateway::{MessageBody, MessagePath}; use masq_lib::ui_gateway::MessagePath::Conversation; use masq_lib::ui_gateway::MessageTarget; use masq_lib::utils::running_test; @@ -3763,10 +3763,10 @@ mod tests { }; let message = NodeFromUiMessage { client_id: 123, - body: request.tmb(0), + body: request.tmb(234), }; let system = System::new(test_name); - let (ui_gateway, _, _) = make_recorder(); + let (ui_gateway, _recorder, arc_recorder) = make_recorder(); let mut subject = make_standard_subject(); subject.logger = Logger::new(test_name); let cz = &mut make_node_record(3456, true); @@ -3890,7 +3890,21 @@ mod tests { System::current().stop(); system.run(); - + + let recorder_result = arc_recorder.lock().unwrap(); + assert_eq!( + recorder_result.get_record::(0).body, + MessageBody { + opcode: "exitLocation".to_string(), + path: MessagePath::Conversation(234), + payload: Ok("{\"fallbackRouting\":true,\"exitCountrySelection\":[{\"countryCodes\":[\"CZ\",\"SK\"],\"priority\":1},{\"countryCodes\":[\"AT\",\"DE\"],\"priority\":2},{\"countryCodes\":[\"PL\"],\"priority\":3}],\"exitCountries\":null,\"missingCountries\":[\"CZ\",\"DE\"]}".to_string()) + } + ); + assert_eq!( + recorder_result + .get_record::(0).target, + MessageTarget::ClientId(123) + ); TestLogHandler::new().assert_logs_contain_in_order(vec![ &format!( "INFO: {}: Fallback Routing is set. Exit location set:", @@ -3913,8 +3927,8 @@ mod tests { show_countries: false, }; let message = NodeFromUiMessage { - client_id: 0, - body: request.tmb(0), + client_id: 234, + body: request.tmb(123), }; let system = System::new(test_name); let (ui_gateway, _recorder, arc_recorder) = make_recorder(); @@ -4057,15 +4071,20 @@ mod tests { System::current().stop(); system.run(); let exit_location_recording = &arc_recorder.lock().unwrap(); - let exit_handler_response = exit_location_recording - .get_record::(0) - .body - .payload - .clone(); let log_handler = TestLogHandler::new(); assert_eq!( - exit_handler_response, - Err((9223372036854775816, "CZ, SK, IN".to_string(),)) + exit_location_recording + .get_record::(0).body, + MessageBody { + opcode: "exitLocation".to_string(), + path: MessagePath::Conversation(123), + payload: Err((9223372036854775816, "CZ, SK, IN".to_string(),)) + } + ); + assert_eq!( + exit_location_recording + .get_record::(0).target, + MessageTarget::ClientId(234) ); log_handler.assert_logs_contain_in_order(vec![ &format!( @@ -4496,7 +4515,7 @@ mod tests { All these Nodes are standard-mode. L is the root Node. */ #[test] - fn find_exit_location_test() { + fn find_exit_locations_in_packed_grid() { let mut subject = make_standard_subject(); let db = &mut subject.neighborhood_database; let mut generator = 1000; @@ -4553,7 +4572,7 @@ mod tests { } #[test] - fn find_exit_locations_in_row_structure_test() { + fn find_exit_locations_in_row_structure() { let mut subject = make_standard_subject(); let db = &mut subject.neighborhood_database; let mut generator = 1000; @@ -4678,7 +4697,7 @@ mod tests { */ #[test] - fn route_optimization_test() { + fn route_optimization_by_serving_rates() { let mut subject = make_standard_subject(); let db = &mut subject.neighborhood_database; let (recipient, _) = make_node_to_ui_recipient(); @@ -4762,11 +4781,11 @@ mod tests { | | | | | U---V---W---X---Y - All these Nodes are standard-mode. L is the root Node. + All these Nodes are standard-mode. L is the root Node. C and T are "CZ" standard nodes */ #[test] - fn route_optimization_country_codes() { + fn route_optimization_with_user_exit_preferences() { let mut subject = make_standard_subject(); let db = &mut subject.neighborhood_database; let (recipient, _) = make_node_to_ui_recipient(); @@ -4816,20 +4835,14 @@ mod tests { let (k, l, m, n, o) = make_row(db); let (p, q, r, s, t) = make_row(db); let (u, v, w, x, y) = make_row(db); - join_rows(db, (&a, &b, &c, &d, &e), (&f, &g, &h, &i, &j)); join_rows(db, (&f, &g, &h, &i, &j), (&k, &l, &m, &n, &o)); join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); - - let mut checkdb = db.clone(); - designate_root_node(db, &l); - db.node_by_key_mut(&c).unwrap().inner.country_code_opt = Some("CZ".to_string()); - checkdb.node_by_key_mut(&c).unwrap().inner.country_code_opt = Some("CZ".to_string()); db.node_by_key_mut(&t).unwrap().inner.country_code_opt = Some("CZ".to_string()); - checkdb.node_by_key_mut(&t).unwrap().inner.country_code_opt = Some("CZ".to_string()); - + let control_db = db.clone(); + designate_root_node(db, &l); subject.handle_exit_location_message(message, 0, 0); let before = Instant::now(); @@ -4837,7 +4850,7 @@ mod tests { subject.find_best_route_segment(&l, None, 3, 10000, RouteDirection::Over, None); let after = Instant::now(); - let exit_node = checkdb.node_by_key( + let exit_node = control_db.node_by_key( &route_cz .as_ref() .unwrap() @@ -4859,13 +4872,19 @@ mod tests { /* Database: - P---q---R - - Test is written from the standpoint of P. Node q is non-routing. + root_key---c_au---b_fr + | + a_fr + Test is written from the standpoint of root_key. + println!("a {}, b {}, c {}", + make_node_record(2345, true).inner.country_code_opt.unwrap(), + make_node_record(5678, true).inner.country_code_opt.unwrap(), + make_node_record(1234, true).inner.country_code_opt.unwrap() + ); */ #[test] - fn find_best_segment_traces_unreachable_country_code_exit_node() { + fn exit_node_not_found_due_to_country_code_strict_requirement() { let mut subject = make_standard_subject(); let (recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(recipient); @@ -4880,17 +4899,17 @@ mod tests { show_countries: false, }; let db = &mut subject.neighborhood_database; - let p = &db.root_mut().public_key().clone(); - let a = &db.add_node(make_node_record(2345, true)).unwrap(); - let b = &db.add_node(make_node_record(5678, true)).unwrap(); - let c = &db.add_node(make_node_record(1234, true)).unwrap(); - db.add_arbitrary_full_neighbor(p, c); - db.add_arbitrary_full_neighbor(c, b); - db.add_arbitrary_full_neighbor(c, a); + let root_key = &db.root_mut().public_key().clone(); + let a_fr = &db.add_node(make_node_record(2345, true)).unwrap(); + let b_fr = &db.add_node(make_node_record(5678, true)).unwrap(); + let c_au = &db.add_node(make_node_record(1234, true)).unwrap(); + db.add_arbitrary_full_neighbor(root_key, c_au); + db.add_arbitrary_full_neighbor(c_au, b_fr); + db.add_arbitrary_full_neighbor(c_au, a_fr); subject.handle_exit_location_message(message, 0, 0); let route_cz = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); assert_eq!(route_cz, None); } @@ -4898,35 +4917,41 @@ mod tests { #[test] fn route_for_au_country_code_is_constructed_with_fallback_routing() { let mut subject = make_standard_subject(); - let p = &subject + let root_key = &subject .neighborhood_database .root_mut() .public_key() .clone(); - let a = &subject + let mut a_fr_node = make_node_record(2345, true); + a_fr_node.inner.rate_pack.exit_byte_rate = 1; + a_fr_node.inner.rate_pack.exit_service_rate = 1; + let mut c_au_node = make_node_record(1234, true); + c_au_node.inner.rate_pack.exit_byte_rate = 10; + c_au_node.inner.rate_pack.exit_service_rate = 10; + let a_fr_key = &subject .neighborhood_database - .add_node(make_node_record(2345, true)) + .add_node(a_fr_node) .unwrap(); - let b = &subject + let b_fr_key = &subject .neighborhood_database .add_node(make_node_record(5678, true)) .unwrap(); - let c = &subject + let c_au_key = &subject .neighborhood_database - .add_node(make_node_record(1234, true)) + .add_node(c_au_node) .unwrap(); subject .neighborhood_database - .add_arbitrary_full_neighbor(p, b); + .add_arbitrary_full_neighbor(root_key, b_fr_key); subject .neighborhood_database - .add_arbitrary_full_neighbor(b, c); + .add_arbitrary_full_neighbor(b_fr_key, c_au_key); subject .neighborhood_database - .add_arbitrary_full_neighbor(b, a); + .add_arbitrary_full_neighbor(b_fr_key, a_fr_key); subject .neighborhood_database - .add_arbitrary_full_neighbor(a, c); + .add_arbitrary_full_neighbor(a_fr_key, c_au_key); let cdb = subject.neighborhood_database.clone(); let (recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(recipient); @@ -4941,7 +4966,7 @@ mod tests { subject.handle_exit_location_message(message, 0, 0); let route_au = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); let exit_node = cdb.node_by_key( &route_au From 824864947d41790948035a7dcb2db274d3537c18 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 1 Apr 2025 17:06:08 +0200 Subject: [PATCH 16/27] addressing comments from review --- node/src/neighborhood/mod.rs | 229 ++++++++++++++--------------------- 1 file changed, 90 insertions(+), 139 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index c03fa4dc5..b6bd8d138 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -2257,28 +2257,65 @@ mod tests { assert_eq!(subject.db_patch_size, expected_db_patch_size); } + #[test] + fn init_db_countries_works_properly() { + let mut subject = make_standard_subject(); + subject.min_hops = Hops::OneHop; + let root_node_key = subject.neighborhood_database.root().public_key().clone(); + let mut first_neighbor = make_node_record(1111, true); + first_neighbor.inner.country_code_opt = Some("CZ".to_string()); + let mut second_neighbor = make_node_record(2222, true); + second_neighbor.inner.country_code_opt = Some("DE".to_string()); + subject + .neighborhood_database + .add_node(first_neighbor.clone()) + .unwrap(); + subject.neighborhood_database + .add_node(second_neighbor.clone()) + .unwrap(); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(&root_node_key, first_neighbor.public_key()); + subject + .neighborhood_database + .add_arbitrary_full_neighbor(&root_node_key, second_neighbor.public_key()); + let filled_db_countries = subject.init_db_countries(); + + subject + .neighborhood_database + .remove_arbitrary_half_neighbor(&root_node_key, first_neighbor.public_key()); + subject + .neighborhood_database + .remove_arbitrary_half_neighbor(&root_node_key, second_neighbor.public_key()); + let emptied_db_countries = subject.init_db_countries(); + + assert_eq!(filled_db_countries, &["CZ".to_string(), "DE".to_string()]); + assert!(emptied_db_countries.is_empty()); + } + #[test] fn standard_gossip_results_in_exit_node_in_database() { let mut subject = make_standard_subject(); let root_node_key = subject.neighborhood_database.root_key().clone(); - let root_node = subject.neighborhood_database.root().clone(); let source_node = make_node_record(1111, true); - let new_neighbor = make_node_record(2222, true); + let first_node = make_node_record(2222, true); + let second_node = make_node_record(3333, false); subject .neighborhood_database .add_node(source_node.clone()) .unwrap(); + subject.neighborhood_database + .add_node(second_node.clone()) + .unwrap(); subject .neighborhood_database .add_arbitrary_full_neighbor(&root_node_key, source_node.public_key()); let mut source_db = subject.neighborhood_database.clone(); source_db.set_root_key(source_node.public_key()); - source_db.remove_node(&root_node_key); - source_db.add_node(root_node).unwrap(); source_db.add_arbitrary_full_neighbor(source_node.public_key(), &root_node_key); - source_db.add_node(new_neighbor.clone()).unwrap(); + source_db.add_node(first_node.clone()).unwrap(); source_db - .add_arbitrary_full_neighbor(source_node.public_key(), new_neighbor.public_key()); + .add_arbitrary_full_neighbor(source_node.public_key(), first_node.public_key()); source_db.root_mut().inner.version = 1; let resigner = source_db .node_by_key_mut(source_node.public_key()) @@ -2286,7 +2323,8 @@ mod tests { resigner.resign(); let standard_gossip = GossipBuilder::new(&source_db) .node(source_node.public_key(), true) - .node(new_neighbor.public_key(), false) + .node(second_node.public_key(), false) + .node(first_node.public_key(), false) .build(); let peer_actors = peer_actors_builder().build(); subject.handle_bind_message(BindMessage { peer_actors }); @@ -2345,32 +2383,6 @@ mod tests { ); } - #[test] - fn init_db_countries_works_properly() { - let mut subject = make_standard_subject(); - subject.min_hops = Hops::OneHop; - let root_node = subject.neighborhood_database.root().clone(); - let mut first_neighbor = make_node_record(1111, true); - first_neighbor.inner.country_code_opt = Some("CZ".to_string()); - subject - .neighborhood_database - .add_node(first_neighbor.clone()) - .unwrap(); - subject - .neighborhood_database - .add_arbitrary_full_neighbor(root_node.public_key(), first_neighbor.public_key()); - - let filled_db_countries = subject.init_db_countries(); - - subject - .neighborhood_database - .remove_arbitrary_half_neighbor(root_node.public_key(), first_neighbor.public_key()); - let emptied_db_countries = subject.init_db_countries(); - - assert_eq!(filled_db_countries, &["CZ".to_string()]); - assert!(emptied_db_countries.is_empty()); - } - #[test] #[should_panic( expected = "Neighbor masq://eth-ropsten:AQIDBA@1.2.3.4:1234 is not on the mainnet blockchain" @@ -3824,7 +3836,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "cz We expecting {}, country is too close to be exit", + "cz We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -3835,7 +3847,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "us We expecting {}, country is considered for exit location in fallback", + "us We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -3846,7 +3858,7 @@ mod tests { .metadata .country_undesirability, 0u32, - "sk We expecting 0, country is with Priority: 1" + "sk We expect 0, country is with Priority: 1" ); assert_eq!( neighborhood @@ -3856,7 +3868,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "de We expecting {}, country is too close to be exit", + "de We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -3867,7 +3879,7 @@ mod tests { .metadata .country_undesirability, 1 * COUNTRY_UNDESIRABILITY_FACTOR, - "at We expecting {}, country is with Priority: 2", + "at We expect {}, country is with Priority: 2", 1 * COUNTRY_UNDESIRABILITY_FACTOR ); assert_eq!( @@ -3878,7 +3890,7 @@ mod tests { .metadata .country_undesirability, 2 * COUNTRY_UNDESIRABILITY_FACTOR, - "pl We expecting {}, country is with Priority: 3", + "pl We expect {}, country is with Priority: 3", 2 * COUNTRY_UNDESIRABILITY_FACTOR ); }), @@ -4003,7 +4015,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "es We expecting {}, country is too close to be exit", + "es We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -4014,7 +4026,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "us We expecting {}, country is considered for exit location in fallback", + "us We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -4025,7 +4037,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "hu We expecting {}, country is too close to be exit", + "hu We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -4036,7 +4048,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "de We expecting {}, country is too close to be exit", + "de We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -4047,7 +4059,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "at We expecting {}, country is considered for exit location in fallback", + "at We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( @@ -4058,7 +4070,7 @@ mod tests { .metadata .country_undesirability, UNREACHABLE_COUNTRY_PENALTY, - "pl We expecting {}, country is too close to be exit", + "pl We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); }), @@ -4122,35 +4134,45 @@ mod tests { client_id: 8765, body: request.tmb(1234), }; + let request_2 = UiSetExitLocationRequest { + fallback_routing: true, + exit_locations: vec![], + show_countries: false, + }; + let clear_exit_location_message = NodeFromUiMessage { + client_id: 6543, + body: request_2.tmb(7894), + }; let mut subject = make_standard_subject(); let system = System::new(test_name); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); subject.logger = Logger::new(test_name); let cz = &mut make_node_record(3456, true); cz.inner.country_code_opt = Some("CZ".to_string()); - let r = &make_node_record(4567, true); + let standard_node_1 = &make_node_record(4567, true); let fr = &mut make_node_record(5678, true); fr.inner.country_code_opt = Some("FR".to_string()); - let t = &make_node_record(7777, true); - let db = &mut subject.neighborhood_database.clone(); + let standard_node_2 = &make_node_record(7777, true); + let root_node = subject.neighborhood_database.root().clone(); + let db = &mut subject.neighborhood_database; db.add_node(cz.clone()).unwrap(); - db.add_node(t.clone()).unwrap(); - db.add_node(r.clone()).unwrap(); + db.add_node(standard_node_2.clone()).unwrap(); + db.add_node(standard_node_1.clone()).unwrap(); db.add_node(fr.clone()).unwrap(); let mut dual_edge = |a: &NodeRecord, b: &NodeRecord| { db.add_arbitrary_full_neighbor(a.public_key(), b.public_key()); }; - dual_edge(&subject.neighborhood_database.root(), cz); - dual_edge(cz, t); - dual_edge(cz, r); - dual_edge(r, fr); + dual_edge(&root_node, cz); + dual_edge(cz, standard_node_2); + dual_edge(cz, standard_node_1); + dual_edge(standard_node_1, fr); subject.neighborhood_database = db.clone(); let subject_addr = subject.start(); let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build(); let cz_public_key = cz.inner.public_key.clone(); - let r_public_key = r.inner.public_key.clone(); + let sn_1_public_key = standard_node_1.inner.public_key.clone(); let fr_public_key = fr.inner.public_key.clone(); - let t_public_key = t.inner.public_key.clone(); + let sn_2_public_key = standard_node_2.inner.public_key.clone(); let assert_country_undesirability_populated = AssertionsMessage { assertions: Box::new(move |neighborhood: &mut Neighborhood| { assert_eq!( @@ -4161,17 +4183,17 @@ mod tests { .metadata .country_undesirability, 0u32, - "CZ - We expecting zero, country is with Priority: 1" + "CZ - We expect zero, country is with Priority: 1" ); assert_eq!( neighborhood .neighborhood_database - .node_by_key(&r_public_key) + .node_by_key(&sn_1_public_key) .unwrap() .metadata .country_undesirability, 0u32, - "We expecting 0, country is not considered for exit location, so country_undesirability doesn't matter" + "We expect 0, country is not considered for exit location, so country_undesirability doesn't matter" ); assert_eq!( neighborhood @@ -4181,23 +4203,19 @@ mod tests { .metadata .country_undesirability, 1 * COUNTRY_UNDESIRABILITY_FACTOR, - "FR - We expecting {}, country is with Priority: 2", + "FR - We expect {}, country is with Priority: 2", 1 * COUNTRY_UNDESIRABILITY_FACTOR ); assert_eq!( neighborhood .neighborhood_database - .node_by_key(&t_public_key) + .node_by_key(&sn_2_public_key) .unwrap() .metadata .country_undesirability, 0u32, - "We expecting 0, country is not considered for exit location, so country_undesirability doesn't matter" + "We expect 0, country is not considered for exit location, so country_undesirability doesn't matter" ); - }), - }; - let assert_neighborhood_exit_location = AssertionsMessage { - assertions: Box::new(move |neighborhood: &mut Neighborhood| { assert_eq!( neighborhood.user_exit_preferences.exit_countries, vec!["FR".to_string()] @@ -4208,19 +4226,10 @@ mod tests { ); }), }; - let request_2 = UiSetExitLocationRequest { - fallback_routing: true, - exit_locations: vec![], - show_countries: false, - }; - let clear_exit_location_message = NodeFromUiMessage { - client_id: 6543, - body: request_2.tmb(7894), - }; let cz_public_key_2 = cz.inner.public_key.clone(); - let r_public_key_2 = r.inner.public_key.clone(); + let r_public_key_2 = standard_node_1.inner.public_key.clone(); let fr_public_key_2 = fr.inner.public_key.clone(); - let t_public_key_2 = t.inner.public_key.clone(); + let t_public_key_2 = standard_node_2.inner.public_key.clone(); let assert_country_undesirability_and_exit_preference_cleared = AssertionsMessage { assertions: Box::new(move |neighborhood: &mut Neighborhood| { assert_eq!( @@ -4231,7 +4240,7 @@ mod tests { .metadata .country_undesirability, 0u32, - "We expecting zero, exit_location was unset" + "We expect zero, exit_location was unset" ); assert_eq!( neighborhood @@ -4241,7 +4250,7 @@ mod tests { .metadata .country_undesirability, 0u32, - "We expecting zero, exit_location was unset" + "We expect zero, exit_location was unset" ); assert_eq!( neighborhood @@ -4251,7 +4260,7 @@ mod tests { .metadata .country_undesirability, 0u32, - "We expecting zero, exit_location was unset" + "We expect zero, exit_location was unset" ); assert_eq!( neighborhood @@ -4261,7 +4270,7 @@ mod tests { .metadata .country_undesirability, 0u32, - "We expecting zero, exit_location was unset" + "We expect zero, exit_location was unset" ); assert_eq!( neighborhood.user_exit_preferences.exit_countries.is_empty(), @@ -4279,9 +4288,6 @@ mod tests { subject_addr .try_send(assert_country_undesirability_populated) .unwrap(); - subject_addr - .try_send(assert_neighborhood_exit_location) - .unwrap(); subject_addr.try_send(clear_exit_location_message).unwrap(); subject_addr .try_send(assert_country_undesirability_and_exit_preference_cleared) @@ -4292,7 +4298,6 @@ mod tests { let ui_gateway_recording = ui_gateway_recording_arc.lock().unwrap(); let record_one: &NodeToUiMessage = ui_gateway_recording.get_record(0); let record_two: &NodeToUiMessage = ui_gateway_recording.get_record(1); - assert_eq!(ui_gateway_recording.len(), 2); assert_eq!( record_one.body, @@ -6049,60 +6054,6 @@ mod tests { .exists_log_containing("INFO: Neighborhood: Changed public IP from 1.2.3.4 to 4.3.2.1"); } - #[test] - fn handle_gossip_produces_new_entry_in_db_countries() { - init_test_logging(); - let subject_node = make_global_cryptde_node_record(5555, true); // 9e7p7un06eHs6frl5A - let first_neighbor = make_node_record(1050, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&first_neighbor)); - let second_neighbor = make_node_record(1234, false); - let mut new_neighbor = make_node_record(2345, false); - new_neighbor.inner.country_code_opt = Some("FR".to_string()); - let first_key = subject - .neighborhood_database - .add_node(first_neighbor) - .unwrap(); - let second_key = subject - .neighborhood_database - .add_node(second_neighbor) - .unwrap(); - subject - .neighborhood_database - .add_arbitrary_full_neighbor(subject_node.public_key(), &first_key); - subject - .neighborhood_database - .add_arbitrary_full_neighbor(&first_key, &second_key); - subject.user_exit_preferences.db_countries = subject.init_db_countries(); - let assertion_db_countries = subject.user_exit_preferences.db_countries.clone(); - let peer_actors = peer_actors_builder().build(); - subject.handle_bind_message(BindMessage { peer_actors }); - - let mut neighbor_db = subject.neighborhood_database.clone(); - neighbor_db.add_node(new_neighbor.clone()).unwrap(); - neighbor_db.set_root_key(&first_key); - neighbor_db.add_arbitrary_full_neighbor(&second_key, new_neighbor.public_key()); - let mut new_second_neighbor = neighbor_db.node_by_key_mut(&second_key).unwrap(); - new_second_neighbor.inner.version = 2; - new_second_neighbor.resign(); - let gossip = GossipBuilder::new(&neighbor_db) - .node(&first_key, true) - .node(&second_key, false) - .node(new_neighbor.public_key(), false) - .build(); - - subject.handle_gossip( - gossip, - SocketAddr::from_str("1.0.5.0:1050").unwrap(), - make_cpm_recipient().0, - ); - - assert!(assertion_db_countries.is_empty()); - assert_eq!( - &subject.user_exit_preferences.db_countries, - &["FR".to_string()] - ) - } - #[test] fn neighborhood_sends_from_gossip_producer_when_acceptance_introductions_are_not_provided() { init_test_logging(); From 6148a71f9e99d84e0a2f01b41ea390a1204f5192 Mon Sep 17 00:00:00 2001 From: czarte Date: Thu, 3 Apr 2025 16:32:07 +0200 Subject: [PATCH 17/27] UI README updated (uncompleted), comment explaining country_undesirability in Metadata, addressing gomments from review --- USER-INTERFACE-INTERFACE.md | 54 ++++ node/src/neighborhood/mod.rs | 434 +++++++++------------------ node/src/neighborhood/node_record.rs | 3 + 3 files changed, 199 insertions(+), 292 deletions(-) diff --git a/USER-INTERFACE-INTERFACE.md b/USER-INTERFACE-INTERFACE.md index c7059929a..b7886e423 100644 --- a/USER-INTERFACE-INTERFACE.md +++ b/USER-INTERFACE-INTERFACE.md @@ -590,6 +590,60 @@ descriptor (for example, if it's still waiting on the router to get a public IP Node descriptor (for example, if its neighborhood mode is not Standard), the `nodeDescriptorOpt` field will be null or absent. +#### `exit-location` +##### Direction: Request +##### Correspondent: Node +##### Layout: +``` +"payload": { + "fallbackRouting": , + "exitLocations": [ + { + "countryCodes": [string, ..], + "priority": + }, + ], + "showCountries": +} +``` +##### Description: +This command requests information about the countries available for exit in our neighborhood and allows us to set up the +desired locations with their priority. The priority provides the node's perspective on how important a particular country +is for our preferences. + +This command can be used in two ways: +1. If we use the command with showCountries set to true, it retrieves information about the available countries in our neighborhood. In this case, other parameters are ignored. +2. If we want to set an exit location, we must set showCountries to false and then configure fallbackRouting and exitLocations with our preferences. + +The fallbackRouting parameter determines whether we want to block exit for a particular country. If this country is no longer +available, the route to exit will fail during construction. If fallbackRouting is set to true, we can exit through any available +country if none of our specified exitLocations are accessible. + +Priorities are used to determine the preferred exit countries. Priority 1 is the highest, while higher numbers indicate +lower priority. For example, if we specify DE with priority 1 and FR with priority 2, then an exit through France will +only be used if a German exit is unavailable or significantly more expensive. + +#### `exit-location` +##### Direction: Response +##### Correspondent: UI +##### Layout: + +``` +"payload": { + "fallbackRouting": , + "exitCountrySelection": <[ + { + "countryCodes": [string, ..], + "priority": + }, + ]>, + "exitCountries": + "missingCountries": <[string, ..]> +} +``` +##### Description: + + #### `financials` ##### Direction: Request ##### Correspondent: Node diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index b6bd8d138..973d5bf19 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -563,7 +563,6 @@ impl Neighborhood { } self.set_country_undesirability_and_exit_countries(&exit_locations_by_priority); } - self.search_for_a_new_route(); } ConfigChange::UpdatePassword(new_password) => { @@ -2047,11 +2046,12 @@ pub enum FallbackPreference { ExitCountryNoFallback, } +//TODO create big comment about all members and its utilization #[derive(Clone, Debug)] pub struct UserExitPreferences { exit_countries: Vec, //if we cross number of country_codes used in one workflow over 34, we want to change this member to HashSet fallback_preference: FallbackPreference, - locations_opt: Option>, //TODO remove Option from NeighborhoodMetadata and create there TODO to ptimize it in future via reference + locations_opt: Option>, //TODO remove Option from NeighborhoodMetadata and create there TODO to optimize it in future via reference db_countries: Vec, } @@ -2135,6 +2135,7 @@ mod tests { use serde_cbor; use std::any::TypeId; use std::cell::RefCell; + use std::collections::HashMap; use std::convert::TryInto; use std::net::{IpAddr, SocketAddr}; use std::path::Path; @@ -2208,6 +2209,21 @@ mod tests { use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + impl Neighborhood { + fn get_node_country_undesirability( + &self, + pubkey: &PublicKey + ) -> u32 + { + self + .neighborhood_database + .node_by_key(pubkey) + .unwrap() + .metadata + .country_undesirability + } + } + impl NeighborhoodDatabase { pub fn set_root_key(&mut self, key: &PublicKey) { self.this_node = key.clone(); @@ -2297,8 +2313,8 @@ mod tests { fn standard_gossip_results_in_exit_node_in_database() { let mut subject = make_standard_subject(); let root_node_key = subject.neighborhood_database.root_key().clone(); - let source_node = make_node_record(1111, true); - let first_node = make_node_record(2222, true); + let source_node = make_node_record(1111, true); //US + let first_node = make_node_record(2222, true); //FR let second_node = make_node_record(3333, false); subject .neighborhood_database @@ -2345,29 +2361,30 @@ mod tests { } #[test] - fn introduction_always_results_with_half_neighborship_in_gossip() { - let mut debut_subject = make_debut_subject(); + fn introduction_results_in_full_neighborship_in_debutant_db_and_enrich_db_countries_on_one_hop() { + let debut_node = make_global_cryptde_node_record(1111, true); + let mut debut_subject = neighborhood_from_nodes(&debut_node, None); + debut_subject.min_hops = Hops::OneHop; + let persistent_config = + PersistentConfigurationMock::new().set_past_neighbors_result(Ok(())); + debut_subject.persistent_config_opt = Some(Box::new(persistent_config)); let debut_root_key = debut_subject.neighborhood_database.root_key().clone(); - let introducer_node = make_node_record(3333, true); - let introducee = make_node_record(2222, true); + let introducer_node = make_node_record(3333, true); //AU + let introducee = make_node_record(2222, true); //FR let introducer_root_key = introducer_node.public_key().clone(); let mut introducer_db = debut_subject.neighborhood_database.clone(); - introducer_db.set_root_key(&introducer_root_key); - introducer_db.add_node(introducer_node).unwrap(); + introducer_db.add_node(introducer_node.clone()).unwrap(); introducer_db.add_arbitrary_half_neighbor(&introducer_root_key, &debut_root_key); introducer_db.add_node(introducee.clone()).unwrap(); introducer_db.add_arbitrary_full_neighbor(&introducer_root_key, introducee.public_key()); - let introduction_gossip = GossipBuilder::new(&introducer_db) .node(&introducer_root_key, true) .node(introducee.public_key(), true) .build(); let peer_actors = peer_actors_builder().build(); - debut_subject.min_hops = Hops::OneHop; let exit_nodes_before_gossip = debut_subject.init_db_countries(); debut_subject.handle_bind_message(BindMessage { peer_actors }); - let introduction_gossip_check = introduction_gossip.clone(); debut_subject.handle_gossip( introduction_gossip, @@ -2375,7 +2392,6 @@ mod tests { make_cpm_recipient().0, ); - println!("introduction_gossip: {:?}", introduction_gossip_check.node_records); assert!(exit_nodes_before_gossip.is_empty()); assert_eq!( debut_subject.user_exit_preferences.db_countries, @@ -3829,66 +3845,36 @@ mod tests { FallbackPreference::ExitCountryWithFallback ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&cz_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&cz_public_key), UNREACHABLE_COUNTRY_PENALTY, "cz We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&us_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&us_public_key), UNREACHABLE_COUNTRY_PENALTY, "us We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&sk_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&sk_public_key), 0u32, "sk We expect 0, country is with Priority: 1" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&de_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&de_public_key), UNREACHABLE_COUNTRY_PENALTY, "de We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&at_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&at_public_key), 1 * COUNTRY_UNDESIRABILITY_FACTOR, "at We expect {}, country is with Priority: 2", 1 * COUNTRY_UNDESIRABILITY_FACTOR ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&pl_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&pl_public_key), 2 * COUNTRY_UNDESIRABILITY_FACTOR, "pl We expect {}, country is with Priority: 3", 2 * COUNTRY_UNDESIRABILITY_FACTOR @@ -4008,67 +3994,37 @@ mod tests { FallbackPreference::ExitCountryWithFallback ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&es_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&es_public_key), UNREACHABLE_COUNTRY_PENALTY, "es We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&us_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&us_public_key), UNREACHABLE_COUNTRY_PENALTY, "us We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&hu_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&hu_public_key), UNREACHABLE_COUNTRY_PENALTY, "hu We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&de_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&de_public_key), UNREACHABLE_COUNTRY_PENALTY, "de We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&at_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&at_public_key), UNREACHABLE_COUNTRY_PENALTY, "at We expect {}, country is considered for exit location in fallback", UNREACHABLE_COUNTRY_PENALTY ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&pl_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&pl_public_key), UNREACHABLE_COUNTRY_PENALTY, "pl We expect {}, country is too close to be exit", UNREACHABLE_COUNTRY_PENALTY @@ -4176,43 +4132,23 @@ mod tests { let assert_country_undesirability_populated = AssertionsMessage { assertions: Box::new(move |neighborhood: &mut Neighborhood| { assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&cz_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&cz_public_key), 0u32, "CZ - We expect zero, country is with Priority: 1" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&sn_1_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&sn_1_public_key), 0u32, "We expect 0, country is not considered for exit location, so country_undesirability doesn't matter" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&fr_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&fr_public_key), 1 * COUNTRY_UNDESIRABILITY_FACTOR, "FR - We expect {}, country is with Priority: 2", 1 * COUNTRY_UNDESIRABILITY_FACTOR ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&sn_2_public_key) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&sn_2_public_key), 0u32, "We expect 0, country is not considered for exit location, so country_undesirability doesn't matter" ); @@ -4233,42 +4169,22 @@ mod tests { let assert_country_undesirability_and_exit_preference_cleared = AssertionsMessage { assertions: Box::new(move |neighborhood: &mut Neighborhood| { assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&cz_public_key_2) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&cz_public_key_2), 0u32, "We expect zero, exit_location was unset" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&r_public_key_2) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&r_public_key_2), 0u32, "We expect zero, exit_location was unset" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&fr_public_key_2) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&fr_public_key_2), 0u32, "We expect zero, exit_location was unset" ); assert_eq!( - neighborhood - .neighborhood_database - .node_by_key(&t_public_key_2) - .unwrap() - .metadata - .country_undesirability, + neighborhood.get_node_country_undesirability(&t_public_key_2), 0u32, "We expect zero, exit_location was unset" ); @@ -4523,50 +4439,10 @@ mod tests { fn find_exit_locations_in_packed_grid() { let mut subject = make_standard_subject(); let db = &mut subject.neighborhood_database; - let mut generator = 1000; - let mut make_node = |db: &mut NeighborhoodDatabase| { - let node = &db.add_node(make_node_record(generator, true)).unwrap(); - generator += 1; - node.clone() - }; - let mut make_row = |db: &mut NeighborhoodDatabase| { - let n1 = make_node(db); - let n2 = make_node(db); - let n3 = make_node(db); - let n4 = make_node(db); - let n5 = make_node(db); - db.add_arbitrary_full_neighbor(&n1, &n2); - db.add_arbitrary_full_neighbor(&n2, &n3); - db.add_arbitrary_full_neighbor(&n3, &n4); - db.add_arbitrary_full_neighbor(&n4, &n5); - (n1, n2, n3, n4, n5) - }; - let join_rows = |db: &mut NeighborhoodDatabase, first_row, second_row| { - let (f1, f2, f3, f4, f5) = first_row; - let (s1, s2, s3, s4, s5) = second_row; - db.add_arbitrary_full_neighbor(f1, s1); - db.add_arbitrary_full_neighbor(f2, s2); - db.add_arbitrary_full_neighbor(f3, s3); - db.add_arbitrary_full_neighbor(f4, s4); - db.add_arbitrary_full_neighbor(f5, s5); - }; - let designate_root_node = |db: &mut NeighborhoodDatabase, key: &PublicKey| { - let root_node_key = db.root_key().clone(); - db.set_root_key(key); - db.remove_node(&root_node_key); - }; - let (a, b, c, d, e) = make_row(db); - let (f, g, h, i, j) = make_row(db); - let (k, l, m, n, o) = make_row(db); - let (p, q, r, s, t) = make_row(db); - let (u, v, w, x, y) = make_row(db); - join_rows(db, (&a, &b, &c, &d, &e), (&f, &g, &h, &i, &j)); - join_rows(db, (&f, &g, &h, &i, &j), (&k, &l, &m, &n, &o)); - join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); - join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); - designate_root_node(db, &l); + let keys = make_db_with_regular_5_x_5_network(db); + designate_root_node(db, keys.get("l").unwrap()); - let mut exit_nodes = subject.find_exit_locations(&l, 3); + let mut exit_nodes = subject.find_exit_locations(keys.get("l").unwrap(), 3); let total_exit_nodes = exit_nodes.len(); exit_nodes.sort(); @@ -4605,11 +4481,6 @@ mod tests { db.add_arbitrary_full_neighbor(&f2, &f3); db.add_arbitrary_full_neighbor(&f3, &f4); db.add_arbitrary_full_neighbor(&f4, &f5); - let designate_root_node = |db: &mut NeighborhoodDatabase, key: &PublicKey| { - let root_node_key = db.root_key().clone(); - db.set_root_key(key); - db.remove_node(&root_node_key); - }; designate_root_node(db, &n1); let mut exit_nodes = subject.find_exit_locations(&n1, 3); @@ -4712,58 +4583,18 @@ mod tests { exit_locations: vec![], show_countries: false, }; - let mut generator = 1000; - let mut make_node = |db: &mut NeighborhoodDatabase| { - let node = &db.add_node(make_node_record(generator, true)).unwrap(); - generator += 1; - node.clone() - }; - let mut make_row = |db: &mut NeighborhoodDatabase| { - let n1 = make_node(db); - let n2 = make_node(db); - let n3 = make_node(db); - let n4 = make_node(db); - let n5 = make_node(db); - db.add_arbitrary_full_neighbor(&n1, &n2); - db.add_arbitrary_full_neighbor(&n2, &n3); - db.add_arbitrary_full_neighbor(&n3, &n4); - db.add_arbitrary_full_neighbor(&n4, &n5); - (n1, n2, n3, n4, n5) - }; - let join_rows = |db: &mut NeighborhoodDatabase, first_row, second_row| { - let (f1, f2, f3, f4, f5) = first_row; - let (s1, s2, s3, s4, s5) = second_row; - db.add_arbitrary_full_neighbor(f1, s1); - db.add_arbitrary_full_neighbor(f2, s2); - db.add_arbitrary_full_neighbor(f3, s3); - db.add_arbitrary_full_neighbor(f4, s4); - db.add_arbitrary_full_neighbor(f5, s5); - }; - let designate_root_node = |db: &mut NeighborhoodDatabase, key: &PublicKey| { - let root_node_key = db.root_key().clone(); - db.set_root_key(key); - db.remove_node(&root_node_key); - }; - let (a, b, c, d, e) = make_row(db); - let (f, g, h, i, j) = make_row(db); - let (k, l, m, n, o) = make_row(db); - let (p, q, r, s, t) = make_row(db); - let (u, v, w, x, y) = make_row(db); - join_rows(db, (&a, &b, &c, &d, &e), (&f, &g, &h, &i, &j)); - join_rows(db, (&f, &g, &h, &i, &j), (&k, &l, &m, &n, &o)); - join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); - join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); - designate_root_node(db, &l); + let keys = make_db_with_regular_5_x_5_network(db); + designate_root_node(db, keys.get("l").unwrap()); subject.handle_exit_location_message(message, 0, 0); let before = Instant::now(); // All the target-designated routes from L to N let route = subject - .find_best_route_segment(&l, Some(&n), 3, 10000, RouteDirection::Back, None) + .find_best_route_segment(&keys.get("l").unwrap(), Some(&keys.get("n").unwrap()), 3, 10000, RouteDirection::Back, None) .unwrap(); let after = Instant::now(); - assert_eq!(route, vec![&l, &g, &h, &i, &n]); // Cheaper than [&l, &q, &r, &s, &n] + assert_eq!(route, vec![keys.get("l").unwrap(), keys.get("g").unwrap(), keys.get("h").unwrap(), keys.get("i").unwrap(), keys.get("n").unwrap()]); // Cheaper than [&l, &q, &r, &s, &n] let interval = after.duration_since(before); assert!( interval.as_millis() <= 100, @@ -4774,24 +4605,25 @@ mod tests { /* Complex testing of country_undesirability on large network with aim to find fallback routing and non fallback routing mechanisms - Database: + Database: - A---B---C---D---E - | | | | | - F---G---H---I---J - | | | | | - K---L---M---N---O - | | | | | - P---Q---R---S---T - | | | | | - U---V---W---X---Y + A---B---C---D---E + | | | | | + F---G---H---I---J + | | | | | + K---L---M---N---O + | | | | | + P---Q---R---S---T + | | | | | + U---V---W---X---Y - All these Nodes are standard-mode. L is the root Node. C and T are "CZ" standard nodes + All these Nodes are standard-mode. L is the root Node. C and T are "CZ" standard nodes - */ + */ #[test] fn route_optimization_with_user_exit_preferences() { let mut subject = make_standard_subject(); + subject.min_hops = Hops::TwoHops; let db = &mut subject.neighborhood_database; let (recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(recipient); @@ -4803,56 +4635,16 @@ mod tests { }], show_countries: false, }; - let mut generator = 1000; - let mut make_node = |db: &mut NeighborhoodDatabase| { - let node = &db.add_node(make_node_record(generator, true)).unwrap(); - generator += 1; - node.clone() - }; - let mut make_row = |db: &mut NeighborhoodDatabase| { - let n1 = make_node(db); - let n2 = make_node(db); - let n3 = make_node(db); - let n4 = make_node(db); - let n5 = make_node(db); - db.add_arbitrary_full_neighbor(&n1, &n2); - db.add_arbitrary_full_neighbor(&n2, &n3); - db.add_arbitrary_full_neighbor(&n3, &n4); - db.add_arbitrary_full_neighbor(&n4, &n5); - (n1, n2, n3, n4, n5) - }; - let join_rows = |db: &mut NeighborhoodDatabase, first_row, second_row| { - let (f1, f2, f3, f4, f5) = first_row; - let (s1, s2, s3, s4, s5) = second_row; - db.add_arbitrary_full_neighbor(f1, s1); - db.add_arbitrary_full_neighbor(f2, s2); - db.add_arbitrary_full_neighbor(f3, s3); - db.add_arbitrary_full_neighbor(f4, s4); - db.add_arbitrary_full_neighbor(f5, s5); - }; - let designate_root_node = |db: &mut NeighborhoodDatabase, key: &PublicKey| { - let root_node_key = db.root_key().clone(); - db.set_root_key(key); - db.remove_node(&root_node_key); - }; - let (a, b, c, d, e) = make_row(db); - let (f, g, h, i, j) = make_row(db); - let (k, l, m, n, o) = make_row(db); - let (p, q, r, s, t) = make_row(db); - let (u, v, w, x, y) = make_row(db); - join_rows(db, (&a, &b, &c, &d, &e), (&f, &g, &h, &i, &j)); - join_rows(db, (&f, &g, &h, &i, &j), (&k, &l, &m, &n, &o)); - join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); - join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); - db.node_by_key_mut(&c).unwrap().inner.country_code_opt = Some("CZ".to_string()); - db.node_by_key_mut(&t).unwrap().inner.country_code_opt = Some("CZ".to_string()); + let keys = make_db_with_regular_5_x_5_network(db); + db.node_by_key_mut(&keys.get("c").unwrap()).unwrap().inner.country_code_opt = Some("CZ".to_string()); + db.node_by_key_mut(&keys.get("t").unwrap()).unwrap().inner.country_code_opt = Some("CZ".to_string()); let control_db = db.clone(); - designate_root_node(db, &l); + designate_root_node(db, &keys.get("l").unwrap()); subject.handle_exit_location_message(message, 0, 0); let before = Instant::now(); let route_cz = - subject.find_best_route_segment(&l, None, 3, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(&keys.get("l").unwrap(), None, 3, 10000, RouteDirection::Over, None); let after = Instant::now(); let exit_node = control_db.node_by_key( @@ -7624,15 +7416,6 @@ mod tests { subject } - fn make_debut_subject() -> Neighborhood { - let root_node = make_global_cryptde_node_record(9999, true); - let mut subject = neighborhood_from_nodes(&root_node, None); - let persistent_config = - PersistentConfigurationMock::new().set_past_neighbors_result(Ok(())); - subject.persistent_config_opt = Some(Box::new(persistent_config)); - subject - } - fn make_o_r_e_subject() -> (NodeRecord, NodeRecord, NodeRecord, Neighborhood) { let mut subject = make_standard_subject(); let o = &subject.neighborhood_database.root().clone(); @@ -7859,4 +7642,71 @@ mod tests { neighborhood } + + /* + Database: + + + A---B---C---D---E + | | | | | + F---G---H---I---J + | | | | | + K---L---M---N---O + | | | | | + P---Q---R---S---T + | | | | | + U---V---W---X---Y + */ + fn make_db_with_regular_5_x_5_network(db: &mut NeighborhoodDatabase) -> HashMap<&'static str, PublicKey> { + let mut generator = 1000; + let mut make_node = |db: &mut NeighborhoodDatabase| { + let node = &db.add_node(make_node_record(generator, true)).unwrap(); + generator += 1; + node.clone() + }; + let mut make_row = |db: &mut NeighborhoodDatabase| { + let n1 = make_node(db); + let n2 = make_node(db); + let n3 = make_node(db); + let n4 = make_node(db); + let n5 = make_node(db); + db.add_arbitrary_full_neighbor(&n1, &n2); + db.add_arbitrary_full_neighbor(&n2, &n3); + db.add_arbitrary_full_neighbor(&n3, &n4); + db.add_arbitrary_full_neighbor(&n4, &n5); + (n1, n2, n3, n4, n5) + }; + let join_rows = |db: &mut NeighborhoodDatabase, first_row, second_row| { + let (f1, f2, f3, f4, f5) = first_row; + let (s1, s2, s3, s4, s5) = second_row; + db.add_arbitrary_full_neighbor(f1, s1); + db.add_arbitrary_full_neighbor(f2, s2); + db.add_arbitrary_full_neighbor(f3, s3); + db.add_arbitrary_full_neighbor(f4, s4); + db.add_arbitrary_full_neighbor(f5, s5); + }; + let (a, b, c, d, e) = make_row(db); + let (f, g, h, i, j) = make_row(db); + let (k, l, m, n, o) = make_row(db); + let (p, q, r, s, t) = make_row(db); + let (u, v, w, x, y) = make_row(db); + join_rows(db, (&a, &b, &c, &d, &e), (&f, &g, &h, &i, &j)); + join_rows(db, (&f, &g, &h, &i, &j), (&k, &l, &m, &n, &o)); + join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); + join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); + let keypairs = [ + ("a", a), ("b", b), ("c", c), ("d", d), ("e", e), + ("f", f), ("g", g), ("h", h), ("i", i), ("j", j), + ("k", k), ("l", l), ("m", m), ("n", n), ("o", o), + ("p", p), ("q", q), ("r", r), ("s", s), ("t", t), + ("u", u), ("v", v), ("w", w), ("x", x), ("y", y) + ]; + HashMap::from_iter(keypairs) + } + + fn designate_root_node(db: &mut NeighborhoodDatabase, key: &PublicKey) { + let root_node_key = db.root_key().clone(); + db.set_root_key(key); + db.remove_node(&root_node_key); + } } diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs index 595e837ef..d8104bf63 100644 --- a/node/src/neighborhood/node_record.rs +++ b/node/src/neighborhood/node_record.rs @@ -348,6 +348,9 @@ pub struct NodeRecordMetadata { pub node_addr_opt: Option, pub unreachable_hosts: HashSet, pub node_location_opt: Option, + // country_undesirability is used in combination with FallbackRouting. If FallbackRouting is set + // to false, we do not consider the undesirability of countries other than those selected for exit. + // Therefore, we use a value of 0 for exit nodes in countries that are not considered for exit. pub country_undesirability: u32, //TODO introduce scores for latency #582 and reliability #583 //TODO introduce check for node_location_opt, to verify full neighbors country code (we know his IP, so we can verify it) From d50da6b0966e8b421a67905fd8843d105107a773 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 12:36:47 +0200 Subject: [PATCH 18/27] formatting for ExitLovationSet --- masq_lib/src/exit_locations.rs | 12 +++++------- node/src/neighborhood/mod.rs | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/masq_lib/src/exit_locations.rs b/masq_lib/src/exit_locations.rs index a11970456..1a7eee552 100644 --- a/masq_lib/src/exit_locations.rs +++ b/masq_lib/src/exit_locations.rs @@ -2,6 +2,7 @@ use crate::messages::ExitLocation; use std::fmt::{Display, Formatter}; +use itertools::Itertools; pub struct ExitLocationSet { pub locations: Vec, @@ -9,13 +10,10 @@ pub struct ExitLocationSet { impl Display for ExitLocationSet { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - for exit_location in self.locations.iter() { - write!( - f, - "Country Codes: {:?} - Priority: {}; ", - exit_location.country_codes, exit_location.priority - )?; - } + + let exit_location_string = self.locations.iter().map(|exit_location| + format!("Country Codes: {:?} - Priority: {}", exit_location.country_codes, exit_location.priority)).collect_vec().join("; "); + write!(f, "{}", exit_location_string)?; Ok(()) } } diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 973d5bf19..045bc1378 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -3908,7 +3908,7 @@ mod tests { "INFO: {}: Fallback Routing is set. Exit location set:", test_name ), - &"Country Codes: [\"CZ\", \"SK\"] - Priority: 1; Country Codes: [\"AT\", \"DE\"] - Priority: 2; Country Codes: [\"PL\"] - Priority: 3;" + &"Country Codes: [\"CZ\", \"SK\"] - Priority: 1; Country Codes: [\"AT\", \"DE\"] - Priority: 2; Country Codes: [\"PL\"] - Priority: 3" ]); } @@ -4059,7 +4059,7 @@ mod tests { "INFO: {}: Fallback Routing is set. Exit location set:", test_name ), - &"Country Codes: [\"CZ\", \"SK\", \"IN\"] - Priority: 1;", + &"Country Codes: [\"CZ\", \"SK\", \"IN\"] - Priority: 1", &format!( "WARN: {}: Exit Location: following desired countries are missing in Neighborhood [\"CZ\", \"SK\", \"IN\"]", test_name @@ -4249,7 +4249,7 @@ mod tests { ); TestLogHandler::new().assert_logs_contain_in_order(vec![ &format!( - "INFO: {}: Fallback Routing NOT set. Exit location set: Country Codes: [\"CZ\"] - Priority: 1; Country Codes: [\"FR\"] - Priority: 2;", + "INFO: {}: Fallback Routing NOT set. Exit location set: Country Codes: [\"CZ\"] - Priority: 1; Country Codes: [\"FR\"] - Priority: 2", test_name ), &format!( From 98a980026e98ab000a436e2d91ab8b13aba7cc5a Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 15:30:56 +0200 Subject: [PATCH 19/27] setup_reporter_shared_app and wait_for_masq_node_ends --- node/src/daemon/setup_reporter.rs | 6 +++--- node/tests/ui_gateway_test.rs | 24 ++++++++++++++++++++++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index 69970c297..1fb47be60 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -69,7 +69,7 @@ pub fn setup_cluster_from(input: Vec<(&str, &str, UiSetupResponseValueStatus)>) .collect::() } -fn daemon_shared_app() -> App<'static, 'static> { +fn setup_reporter_shared_app() -> App<'static, 'static> { shared_app(app_head()).arg(data_directory_arg(DATA_DIRECTORY_DAEMON_HELP.as_str())) } @@ -228,7 +228,7 @@ impl SetupReporterReal { } pub fn get_default_params() -> SetupCluster { - let schema = daemon_shared_app(); + let schema = setup_reporter_shared_app(); schema .p .opts @@ -498,7 +498,7 @@ impl SetupReporterReal { environment: bool, config_file: bool, ) -> Result, ConfiguratorError> { - let app = daemon_shared_app(); + let app = setup_reporter_shared_app(); let mut vcls: Vec> = vec![]; if let Some(command_line) = command_line_opt.clone() { vcls.push(Box::new(CommandLineVcl::new(command_line))); diff --git a/node/tests/ui_gateway_test.rs b/node/tests/ui_gateway_test.rs index 56893a277..e1999458e 100644 --- a/node/tests/ui_gateway_test.rs +++ b/node/tests/ui_gateway_test.rs @@ -15,7 +15,9 @@ use masq_lib::test_utils::utils::ensure_node_home_directory_exists; use masq_lib::utils::{add_chain_specific_directory, find_free_port}; use std::net::TcpStream; use std::thread; -use std::time::Duration; +use std::time::{Duration, SystemTime}; +use itertools::Itertools; +use sysinfo::{ProcessExt, System, SystemExt}; use utils::CommandConfig; #[test] @@ -57,7 +59,7 @@ fn ui_requests_something_and_gets_corresponding_response() { #[test] fn log_broadcasts_are_correctly_received_integration() { - thread::sleep(Duration::from_secs(5)); + wait_for_masq_node_ends(); fdlimit::raise_fd_limit(); let port = find_free_port(); let mut node = utils::MASQNode::start_standard( @@ -96,11 +98,28 @@ fn log_broadcasts_are_correctly_received_integration() { node.wait_for_exit(); } +fn wait_for_masq_node_ends() { + let mut system = System::new_all(); + let deadline = SystemTime::now() + Duration::from_secs(5); + loop { + if SystemTime::now() > deadline { + panic!("Previous instance of MASQNode does not stops"); + } + system.refresh_all(); + let processes = system.processes().clone(); + if processes.into_values().find(|process| process.name().contains("MASQNode")).is_none() { + break; + } + thread::sleep(Duration::from_millis(500)); + } +} + #[test] fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { //Daemon's probe to check if the Node is alive causes an unwanted new reference //for the Daemon's client, so we need to make the Daemon send a close message //breaking any reference to him immediately + wait_for_masq_node_ends(); fdlimit::raise_fd_limit(); let data_directory = ensure_node_home_directory_exists( "ui_gateway_test", @@ -182,6 +201,7 @@ fn daemon_does_not_allow_node_to_keep_his_client_alive_integration() { #[test] fn cleanup_after_deceased_clients_integration() { + wait_for_masq_node_ends(); fdlimit::raise_fd_limit(); let port = find_free_port(); let mut node = utils::MASQNode::start_standard( From 81faa511640625a527a3bc4bdc8a7943947dddb2 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 15:53:30 +0200 Subject: [PATCH 20/27] CountryCodes to CountryBlocks, fix formatting of ExitLocationSet in tests, remove reference from supporting functions, already reference comming in --- masq/src/command_factory.rs | 4 ++-- masq/src/commands/exit_location_command.rs | 22 +++++++++---------- masq_lib/src/messages.rs | 4 ++-- .../country_code_exit_location_routing.rs | 4 ++-- node/src/neighborhood/mod.rs | 6 ++--- 5 files changed, 20 insertions(+), 20 deletions(-) diff --git a/masq/src/command_factory.rs b/masq/src/command_factory.rs index 6ec658903..12515027b 100644 --- a/masq/src/command_factory.rs +++ b/masq/src/command_factory.rs @@ -108,7 +108,7 @@ impl CommandFactoryReal { mod tests { use super::*; use crate::command_factory::CommandFactoryError::UnrecognizedSubcommand; - use masq_lib::messages::CountryCodes; + use masq_lib::messages::CountryGroups; #[test] fn complains_about_unrecognized_subcommand() { @@ -282,7 +282,7 @@ mod tests { .downcast_ref::() .unwrap(), &SetExitLocationCommand { - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["CZ".to_string()], priority: 1 }], diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index 4433f7a7b..b0c40a333 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -8,7 +8,7 @@ use crate::commands::commands_common::{ use clap::{App, Arg, ArgGroup, SubCommand}; use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; use masq_lib::exit_locations::ExitLocationSet; -use masq_lib::messages::{CountryCodes, UiSetExitLocationRequest, UiSetExitLocationResponse}; +use masq_lib::messages::{CountryGroups, UiSetExitLocationRequest, UiSetExitLocationResponse}; use masq_lib::shared_schema::common_validators; use masq_lib::{as_any_ref_in_trait_impl, short_writeln}; use std::fmt::Debug; @@ -48,7 +48,7 @@ pub fn exit_location_subcommand() -> App<'static, 'static> { #[derive(Debug, PartialEq, Eq)] pub struct SetExitLocationCommand { - pub exit_locations: Vec, + pub exit_locations: Vec, pub fallback_routing: bool, pub show_countries: bool, } @@ -63,7 +63,7 @@ impl SetExitLocationCommand { .expect("Expected Country Codes") .into_iter() .enumerate() - .map(|(index, code)| CountryCodes::from((code.to_string(), index))) + .map(|(index, code)| CountryGroups::from((code.to_string(), index))) .collect(), false => vec![], }; @@ -98,7 +98,7 @@ impl Command for SetExitLocationCommand { transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS); match output { Ok(exit_location_response) => { - if let Some(exit_countries) = exit_location_response.exit_country_selection { + if let Some(exit_countries) = exit_location_response.exit_countries { match !exit_countries.is_empty() { true => short_writeln!( context.stdout(), @@ -210,7 +210,7 @@ pub mod tests { use crate::test_utils::mocks::CommandContextMock; use masq_lib::constants::EXIT_COUNTRY_MISSING_COUNTRIES_ERROR; use masq_lib::messages::{ - CountryCodes, ExitLocation, ToMessageBody, UiSetExitLocationRequest, + CountryGroups, ExitLocation, ToMessageBody, UiSetExitLocationRequest, UiSetExitLocationResponse, }; use std::sync::{Arc, Mutex}; @@ -355,15 +355,15 @@ pub mod tests { let expected_request = UiSetExitLocationRequest { fallback_routing: true, exit_locations: vec![ - CountryCodes { + CountryGroups { country_codes: vec!["CZ".to_string(), "SK".to_string()], priority: 1, }, - CountryCodes { + CountryGroups { country_codes: vec!["AT".to_string(), "DE".to_string()], priority: 2, }, - CountryCodes { + CountryGroups { country_codes: vec!["PL".to_string()], priority: 3, }, @@ -377,7 +377,7 @@ pub mod tests { &[(expected_message_body, STANDARD_COMMAND_TIMEOUT_MILLIS)] ); let stdout = stdout_arc.lock().unwrap(); - assert_eq!(&stdout.get_string(), "Fallback Routing is set.\nExit location set: Country Codes: [\"CZ\", \"SK\"] - Priority: 1; Country Codes: [\"AT\", \"DE\"] - Priority: 2; Country Codes: [\"PL\"] - Priority: 3; \n"); + assert_eq!(&stdout.get_string(), "Fallback Routing is set.\nExit location set: Country Codes: [\"CZ\", \"SK\"] - Priority: 1; Country Codes: [\"AT\", \"DE\"] - Priority: 2; Country Codes: [\"PL\"] - Priority: 3\n"); let stderr = stderr_arc.lock().unwrap(); assert_eq!(&stderr.get_string(), ""); } @@ -411,7 +411,7 @@ pub mod tests { assert_eq!(result, Ok(())); let expected_request = UiSetExitLocationRequest { fallback_routing: false, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["CZ".to_string()], priority: 1, }], @@ -425,7 +425,7 @@ pub mod tests { ); let stdout = stdout_arc.lock().unwrap(); let stderr = stderr_arc.lock().unwrap(); - assert_eq!(&stdout.get_string(), "Fallback Routing NOT set.\nExit location set: Country Codes: [\"CZ\"] - Priority: 1; \n"); + assert_eq!(&stdout.get_string(), "Fallback Routing NOT set.\nExit location set: Country Codes: [\"CZ\"] - Priority: 1\n"); assert_eq!(&stderr.get_string(), ""); } diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index bea7e2ab9..eb0b5c5ca 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -852,7 +852,7 @@ conversation_message!(UiWalletAddressesResponse, "walletAddresses"); // in the future according to future user interface needs of more specification #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct CountryGroups { - #[serde(rename = "countryCodes")] + #[serde(rename = "CountryGroups")] pub country_codes: Vec, pub priority: usize, } @@ -883,7 +883,7 @@ conversation_message!(UiSetExitLocationRequest, "exitLocation"); #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] pub struct ExitLocation { - #[serde(rename = "countryCodes")] + #[serde(rename = "CountryGroups")] pub country_codes: Vec, pub priority: usize, } diff --git a/multinode_integration_tests/tests/country_code_exit_location_routing.rs b/multinode_integration_tests/tests/country_code_exit_location_routing.rs index 8f2db0374..79b043d52 100644 --- a/multinode_integration_tests/tests/country_code_exit_location_routing.rs +++ b/multinode_integration_tests/tests/country_code_exit_location_routing.rs @@ -4,7 +4,7 @@ use std::thread; use std::time::Duration; use masq_lib::messages::{ - CountryCodes, ToMessageBody, UiSetConfigurationRequest, UiSetExitLocationRequest, + CountryGroups, ToMessageBody, UiSetConfigurationRequest, UiSetExitLocationRequest, }; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use multinode_integration_tests_lib::masq_node::{MASQNode, PortSelector}; @@ -71,7 +71,7 @@ fn http_end_to_end_routing_test_with_exit_location() { ui.send_request( UiSetExitLocationRequest { fallback_routing: false, - exit_locations: vec![CountryCodes { + exit_locations: vec![CountryGroups { country_codes: vec!["FR".to_string()], priority: 1, }], diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 045bc1378..bc81a4b43 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -2090,13 +2090,13 @@ impl UserExitPreferences { } fn should_set_country_undesirability(country_code: &String, exit_location: &ExitLocation) -> bool { - exit_location.country_codes.contains(&country_code) + exit_location.country_codes.contains(country_code) && country_code != ZZ_COUNTRY_CODE_STRING } fn is_unreachable_country_penalty(&self, country_code: &String) -> bool { (self.fallback_preference == FallbackPreference::ExitCountryWithFallback - && !self.exit_countries.contains(&country_code)) + && !self.exit_countries.contains(country_code)) || country_code == ZZ_COUNTRY_CODE_STRING } @@ -3895,7 +3895,7 @@ mod tests { MessageBody { opcode: "exitLocation".to_string(), path: MessagePath::Conversation(234), - payload: Ok("{\"fallbackRouting\":true,\"exitCountrySelection\":[{\"countryCodes\":[\"CZ\",\"SK\"],\"priority\":1},{\"countryCodes\":[\"AT\",\"DE\"],\"priority\":2},{\"countryCodes\":[\"PL\"],\"priority\":3}],\"exitCountries\":null,\"missingCountries\":[\"CZ\",\"DE\"]}".to_string()) + payload: Ok("{\"fallbackRouting\":true,\"exitCountrySelection\":[{\"CountryGroups\":[\"CZ\",\"SK\"],\"priority\":1},{\"CountryGroups\":[\"AT\",\"DE\"],\"priority\":2},{\"CountryGroups\":[\"PL\"],\"priority\":3}],\"exitCountries\":null,\"missingCountries\":[\"CZ\",\"DE\"]}".to_string()) } ); assert_eq!( From be250eb2be08c495da9fe76cbafbd0bd93caa4dd Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 16:18:54 +0200 Subject: [PATCH 21/27] resolving unimplemented Copy trait in Process --- node/tests/ui_gateway_test.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/node/tests/ui_gateway_test.rs b/node/tests/ui_gateway_test.rs index e1999458e..6e55643d6 100644 --- a/node/tests/ui_gateway_test.rs +++ b/node/tests/ui_gateway_test.rs @@ -16,7 +16,6 @@ use masq_lib::utils::{add_chain_specific_directory, find_free_port}; use std::net::TcpStream; use std::thread; use std::time::{Duration, SystemTime}; -use itertools::Itertools; use sysinfo::{ProcessExt, System, SystemExt}; use utils::CommandConfig; @@ -106,8 +105,7 @@ fn wait_for_masq_node_ends() { panic!("Previous instance of MASQNode does not stops"); } system.refresh_all(); - let processes = system.processes().clone(); - if processes.into_values().find(|process| process.name().contains("MASQNode")).is_none() { + if system.processes().into_iter().find(|(_, process)| process.name().contains("MASQNode")).is_none() { break; } thread::sleep(Duration::from_millis(500)); From 6d01238b574c6afafb0416c21eb84ab19b0a27b5 Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 17:16:34 +0200 Subject: [PATCH 22/27] new clap test providing_show_countries_with_other_argument_fails --- masq/src/commands/exit_location_command.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index b0c40a333..fef8bc613 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -485,4 +485,26 @@ pub mod tests { "Countries available for exit-location: [\"CZ\"]\n" ); } + + #[test] + fn providing_show_countries_with_other_argument_fails() { + let result_expected_one = "SetExitLocationCommand error: The argument '--country-codes ' cannot be used with one or more of the other specified arguments\n\nUSAGE:\n"; + let result_expected_two = "SetExitLocationCommand error: The argument '--show-countries' cannot be used with one or more of the other specified arguments\n\nUSAGE:\n"; + + let result = SetExitLocationCommand::new(&[ + "exit-location".to_string(), + "--show-countries".to_string(), + "--country-codes".to_string(), + "CZ".to_string(), + ]) + .unwrap_err(); + + assert!( + result.contains(result_expected_one) || result.contains(result_expected_two), + "result was {:?}, but expected {:?} or {:?}", + result, + result_expected_one, + result_expected_two + ); + } } From 7fe599882aefb83099e28f65b3f15927878a9cdf Mon Sep 17 00:00:00 2001 From: czarte Date: Tue, 8 Apr 2025 17:46:56 +0200 Subject: [PATCH 23/27] format --- masq/src/commands/exit_location_command.rs | 15 +- masq_lib/src/constants.rs | 10 +- masq_lib/src/exit_locations.rs | 16 +- masq_lib/src/messages.rs | 2 +- node/src/neighborhood/mod.rs | 262 ++++++++++++--------- node/src/neighborhood/node_record.rs | 4 +- node/tests/ui_gateway_test.rs | 7 +- 7 files changed, 182 insertions(+), 134 deletions(-) diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index fef8bc613..5b949ac3c 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -425,7 +425,10 @@ pub mod tests { ); let stdout = stdout_arc.lock().unwrap(); let stderr = stderr_arc.lock().unwrap(); - assert_eq!(&stdout.get_string(), "Fallback Routing NOT set.\nExit location set: Country Codes: [\"CZ\"] - Priority: 1\n"); + assert_eq!( + &stdout.get_string(), + "Fallback Routing NOT set.\nExit location set: Country Codes: [\"CZ\"] - Priority: 1\n" + ); assert_eq!(&stderr.get_string(), ""); } @@ -485,26 +488,26 @@ pub mod tests { "Countries available for exit-location: [\"CZ\"]\n" ); } - + #[test] fn providing_show_countries_with_other_argument_fails() { let result_expected_one = "SetExitLocationCommand error: The argument '--country-codes ' cannot be used with one or more of the other specified arguments\n\nUSAGE:\n"; let result_expected_two = "SetExitLocationCommand error: The argument '--show-countries' cannot be used with one or more of the other specified arguments\n\nUSAGE:\n"; - + let result = SetExitLocationCommand::new(&[ "exit-location".to_string(), "--show-countries".to_string(), "--country-codes".to_string(), "CZ".to_string(), ]) - .unwrap_err(); - + .unwrap_err(); + assert!( result.contains(result_expected_one) || result.contains(result_expected_two), "result was {:?}, but expected {:?} or {:?}", result, result_expected_one, result_expected_two - ); + ); } } diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index 4962dd83e..732968ec1 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -204,14 +204,8 @@ mod tests { NODE_RECORD_INNER_CURRENT_VERSION, DataVersion { major: 0, minor: 1 } ); - assert_eq!( - PAYLOAD_ZERO_SIZE, - 0usize - ); - assert_eq!( - DEFAULT_PREALLOCATION_VEC, - 10 - ) + assert_eq!(PAYLOAD_ZERO_SIZE, 0usize); + assert_eq!(DEFAULT_PREALLOCATION_VEC, 10) } #[test] diff --git a/masq_lib/src/exit_locations.rs b/masq_lib/src/exit_locations.rs index 1a7eee552..c5884be54 100644 --- a/masq_lib/src/exit_locations.rs +++ b/masq_lib/src/exit_locations.rs @@ -1,8 +1,8 @@ // Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::messages::ExitLocation; -use std::fmt::{Display, Formatter}; use itertools::Itertools; +use std::fmt::{Display, Formatter}; pub struct ExitLocationSet { pub locations: Vec, @@ -10,9 +10,17 @@ pub struct ExitLocationSet { impl Display for ExitLocationSet { fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { - - let exit_location_string = self.locations.iter().map(|exit_location| - format!("Country Codes: {:?} - Priority: {}", exit_location.country_codes, exit_location.priority)).collect_vec().join("; "); + let exit_location_string = self + .locations + .iter() + .map(|exit_location| { + format!( + "Country Codes: {:?} - Priority: {}", + exit_location.country_codes, exit_location.priority + ) + }) + .collect_vec() + .join("; "); write!(f, "{}", exit_location_string)?; Ok(()) } diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs index eb0b5c5ca..f8c6effdc 100644 --- a/masq_lib/src/messages.rs +++ b/masq_lib/src/messages.rs @@ -848,7 +848,7 @@ pub struct UiWalletAddressesResponse { } conversation_message!(UiWalletAddressesResponse, "walletAddresses"); -// CountryGroups are inbound data for ExitLocations from UI. These data structures could be enriched +// CountryGroups are inbound data for ExitLocations from UI. These data structures could be enriched // in the future according to future user interface needs of more specification #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)] pub struct CountryGroups { diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index bc81a4b43..cb324da49 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -60,7 +60,9 @@ use gossip_producer::GossipProducer; use gossip_producer::GossipProducerReal; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::{DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, PAYLOAD_ZERO_SIZE}; +use masq_lib::constants::{ + DEFAULT_PREALLOCATION_VEC, EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, PAYLOAD_ZERO_SIZE, +}; use masq_lib::crash_point::CrashPoint; use masq_lib::exit_locations::ExitLocationSet; use masq_lib::logger::Logger; @@ -1197,7 +1199,11 @@ impl Neighborhood { fn validate_country_code_when_fallback_routing(&self, last_node: &PublicKey) -> bool { let last_cc = match self.neighborhood_database.node_by_key(last_node) { - Some(nr) => nr.inner.country_code_opt.clone().unwrap_or_else(|| "ZZ".to_string()), + Some(nr) => nr + .inner + .country_code_opt + .clone() + .unwrap_or_else(|| "ZZ".to_string()), None => "ZZ".to_string(), }; if self.user_exit_preferences.exit_countries.contains(&last_cc) { @@ -1220,8 +1226,7 @@ impl Neighborhood { research_neighborhood: bool, direction: RouteDirection, ) -> bool { - if self.is_always_true(last_node_key, research_neighborhood, direction) - { + if self.is_always_true(last_node_key, research_neighborhood, direction) { true // Zero- and single-hop routes are not subject to exit-too-close restrictions } else { if let Some(node_record) = self.neighborhood_database.node_by_key(last_node_key) { @@ -1236,11 +1241,16 @@ impl Neighborhood { } } - fn is_always_true(&self, last_node_key: &PublicKey, research_neighborhood: bool, direction: RouteDirection) -> bool { + fn is_always_true( + &self, + last_node_key: &PublicKey, + research_neighborhood: bool, + direction: RouteDirection, + ) -> bool { self.user_exit_preferences.fallback_preference == FallbackPreference::Nothing || (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback - && self.validate_country_code_when_fallback_routing(last_node_key)) + == FallbackPreference::ExitCountryWithFallback + && self.validate_country_code_when_fallback_routing(last_node_key)) || research_neighborhood || direction == RouteDirection::Back } @@ -1252,9 +1262,13 @@ impl Neighborhood { logger: &Logger, ) -> i64 { match undesirability_type { - UndesirabilityType::Relay => node_record.inner.rate_pack.routing_charge(payload_size) as i64, - UndesirabilityType::ExitRequest(None) => node_record.inner.rate_pack.exit_charge(payload_size) as i64 - + node_record.metadata.country_undesirability as i64, + UndesirabilityType::Relay => { + node_record.inner.rate_pack.routing_charge(payload_size) as i64 + } + UndesirabilityType::ExitRequest(None) => { + node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.metadata.country_undesirability as i64 + } UndesirabilityType::ExitRequest(Some(hostname)) => { let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; @@ -1709,7 +1723,10 @@ impl Neighborhood { body: MessageBody { opcode: "exitLocation".to_string(), path: Conversation(context_id), - payload: Err((EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, missing_countries_message)), + payload: Err(( + EXIT_COUNTRY_MISSING_COUNTRIES_ERROR, + missing_countries_message, + )), }, } } else { @@ -1721,7 +1738,7 @@ impl Neighborhood { exit_countries: countries_to_show, missing_countries, } - .tmb(context_id), + .tmb(context_id), } } } @@ -1839,15 +1856,13 @@ impl Neighborhood { fn code_in_db_countries_or_fallback_active(&mut self, code: &String) -> bool { self.user_exit_preferences.db_countries.contains(code) || (self.user_exit_preferences.fallback_preference - == FallbackPreference::ExitCountryWithFallback) + == FallbackPreference::ExitCountryWithFallback) } fn init_db_countries(&mut self) -> Vec { let root_key = self.neighborhood_database.root_key(); let min_hops = self.min_hops as usize; - let exit_nodes = self - .find_exit_locations(root_key, min_hops) - .to_owned(); + let exit_nodes = self.find_exit_locations(root_key, min_hops).to_owned(); let mut db_countries = vec![]; if !exit_nodes.is_empty() { for pub_key in exit_nodes { @@ -2074,13 +2089,11 @@ impl UserExitPreferences { match &self.locations_opt { Some(exit_locations_by_priority) => { for exit_location in exit_locations_by_priority { - if Self::should_set_country_undesirability(&country_code, exit_location) - { + if Self::should_set_country_undesirability(&country_code, exit_location) { node_record.metadata.country_undesirability = Self::calculate_country_undesirability(exit_location.priority as u32); } - if self.is_unreachable_country_penalty(&country_code) - { + if self.is_unreachable_country_penalty(&country_code) { node_record.metadata.country_undesirability = UNREACHABLE_COUNTRY_PENALTY; } } @@ -2089,9 +2102,11 @@ impl UserExitPreferences { } } - fn should_set_country_undesirability(country_code: &String, exit_location: &ExitLocation) -> bool { - exit_location.country_codes.contains(country_code) - && country_code != ZZ_COUNTRY_CODE_STRING + fn should_set_country_undesirability( + country_code: &String, + exit_location: &ExitLocation, + ) -> bool { + exit_location.country_codes.contains(country_code) && country_code != ZZ_COUNTRY_CODE_STRING } fn is_unreachable_country_penalty(&self, country_code: &String) -> bool { @@ -2151,14 +2166,14 @@ mod tests { CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, }; use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::ui_gateway::{MessageBody, MessagePath}; use masq_lib::ui_gateway::MessagePath::Conversation; use masq_lib::ui_gateway::MessageTarget; + use masq_lib::ui_gateway::{MessageBody, MessagePath}; use masq_lib::utils::running_test; use crate::db_config::persistent_configuration::PersistentConfigError; - use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord}; use crate::neighborhood::gossip::Gossip_0v1; + use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord}; use crate::neighborhood::node_record::{NodeRecordInner_0v1, NodeRecordInputs}; use crate::stream_messages::{NonClandestineAttributes, RemovedStreamType}; use crate::sub_lib::cryptde::{decodex, encodex, CryptData, PlainData}; @@ -2210,13 +2225,8 @@ mod tests { use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; impl Neighborhood { - fn get_node_country_undesirability( - &self, - pubkey: &PublicKey - ) -> u32 - { - self - .neighborhood_database + fn get_node_country_undesirability(&self, pubkey: &PublicKey) -> u32 { + self.neighborhood_database .node_by_key(pubkey) .unwrap() .metadata @@ -2286,7 +2296,8 @@ mod tests { .neighborhood_database .add_node(first_neighbor.clone()) .unwrap(); - subject.neighborhood_database + subject + .neighborhood_database .add_node(second_neighbor.clone()) .unwrap(); subject @@ -2320,7 +2331,8 @@ mod tests { .neighborhood_database .add_node(source_node.clone()) .unwrap(); - subject.neighborhood_database + subject + .neighborhood_database .add_node(second_node.clone()) .unwrap(); subject @@ -2330,12 +2342,9 @@ mod tests { source_db.set_root_key(source_node.public_key()); source_db.add_arbitrary_full_neighbor(source_node.public_key(), &root_node_key); source_db.add_node(first_node.clone()).unwrap(); - source_db - .add_arbitrary_full_neighbor(source_node.public_key(), first_node.public_key()); + source_db.add_arbitrary_full_neighbor(source_node.public_key(), first_node.public_key()); source_db.root_mut().inner.version = 1; - let resigner = source_db - .node_by_key_mut(source_node.public_key()) - .unwrap(); + let resigner = source_db.node_by_key_mut(source_node.public_key()).unwrap(); resigner.resign(); let standard_gossip = GossipBuilder::new(&source_db) .node(source_node.public_key(), true) @@ -2361,7 +2370,8 @@ mod tests { } #[test] - fn introduction_results_in_full_neighborship_in_debutant_db_and_enrich_db_countries_on_one_hop() { + fn introduction_results_in_full_neighborship_in_debutant_db_and_enrich_db_countries_on_one_hop() + { let debut_node = make_global_cryptde_node_record(1111, true); let mut debut_subject = neighborhood_from_nodes(&debut_node, None); debut_subject.min_hops = Hops::OneHop; @@ -3888,7 +3898,7 @@ mod tests { System::current().stop(); system.run(); - + let recorder_result = arc_recorder.lock().unwrap(); assert_eq!( recorder_result.get_record::(0).body, @@ -3899,8 +3909,7 @@ mod tests { } ); assert_eq!( - recorder_result - .get_record::(0).target, + recorder_result.get_record::(0).target, MessageTarget::ClientId(123) ); TestLogHandler::new().assert_logs_contain_in_order(vec![ @@ -3913,7 +3922,8 @@ mod tests { } #[test] - fn no_exit_location_is_set_if_desired_country_codes_not_present_in_neighborhood_with_fallback_routing_set() { + fn no_exit_location_is_set_if_desired_country_codes_not_present_in_neighborhood_with_fallback_routing_set( + ) { init_test_logging(); let test_name = "exit_location_with_multiple_countries_and_priorities_can_be_changed_using_exit_location_msg"; let request = UiSetExitLocationRequest { @@ -4042,7 +4052,8 @@ mod tests { let log_handler = TestLogHandler::new(); assert_eq!( exit_location_recording - .get_record::(0).body, + .get_record::(0) + .body, MessageBody { opcode: "exitLocation".to_string(), path: MessagePath::Conversation(123), @@ -4051,7 +4062,8 @@ mod tests { ); assert_eq!( exit_location_recording - .get_record::(0).target, + .get_record::(0) + .target, MessageTarget::ClientId(234) ); log_handler.assert_logs_contain_in_order(vec![ @@ -4590,11 +4602,27 @@ mod tests { // All the target-designated routes from L to N let route = subject - .find_best_route_segment(&keys.get("l").unwrap(), Some(&keys.get("n").unwrap()), 3, 10000, RouteDirection::Back, None) + .find_best_route_segment( + &keys.get("l").unwrap(), + Some(&keys.get("n").unwrap()), + 3, + 10000, + RouteDirection::Back, + None, + ) .unwrap(); let after = Instant::now(); - assert_eq!(route, vec![keys.get("l").unwrap(), keys.get("g").unwrap(), keys.get("h").unwrap(), keys.get("i").unwrap(), keys.get("n").unwrap()]); // Cheaper than [&l, &q, &r, &s, &n] + assert_eq!( + route, + vec![ + keys.get("l").unwrap(), + keys.get("g").unwrap(), + keys.get("h").unwrap(), + keys.get("i").unwrap(), + keys.get("n").unwrap() + ] + ); // Cheaper than [&l, &q, &r, &s, &n] let interval = after.duration_since(before); assert!( interval.as_millis() <= 100, @@ -4605,21 +4633,21 @@ mod tests { /* Complex testing of country_undesirability on large network with aim to find fallback routing and non fallback routing mechanisms - Database: + Database: - A---B---C---D---E - | | | | | - F---G---H---I---J - | | | | | - K---L---M---N---O - | | | | | - P---Q---R---S---T - | | | | | - U---V---W---X---Y + A---B---C---D---E + | | | | | + F---G---H---I---J + | | | | | + K---L---M---N---O + | | | | | + P---Q---R---S---T + | | | | | + U---V---W---X---Y - All these Nodes are standard-mode. L is the root Node. C and T are "CZ" standard nodes + All these Nodes are standard-mode. L is the root Node. C and T are "CZ" standard nodes - */ + */ #[test] fn route_optimization_with_user_exit_preferences() { let mut subject = make_standard_subject(); @@ -4636,24 +4664,30 @@ mod tests { show_countries: false, }; let keys = make_db_with_regular_5_x_5_network(db); - db.node_by_key_mut(&keys.get("c").unwrap()).unwrap().inner.country_code_opt = Some("CZ".to_string()); - db.node_by_key_mut(&keys.get("t").unwrap()).unwrap().inner.country_code_opt = Some("CZ".to_string()); + db.node_by_key_mut(&keys.get("c").unwrap()) + .unwrap() + .inner + .country_code_opt = Some("CZ".to_string()); + db.node_by_key_mut(&keys.get("t").unwrap()) + .unwrap() + .inner + .country_code_opt = Some("CZ".to_string()); let control_db = db.clone(); designate_root_node(db, &keys.get("l").unwrap()); subject.handle_exit_location_message(message, 0, 0); let before = Instant::now(); - let route_cz = - subject.find_best_route_segment(&keys.get("l").unwrap(), None, 3, 10000, RouteDirection::Over, None); + let route_cz = subject.find_best_route_segment( + &keys.get("l").unwrap(), + None, + 3, + 10000, + RouteDirection::Over, + None, + ); let after = Instant::now(); - let exit_node = control_db.node_by_key( - &route_cz - .as_ref() - .unwrap() - .last() - .unwrap(), - ); + let exit_node = control_db.node_by_key(&route_cz.as_ref().unwrap().last().unwrap()); assert_eq!( exit_node.unwrap().inner.country_code_opt, Some("CZ".to_string()) @@ -4725,18 +4759,12 @@ mod tests { let mut c_au_node = make_node_record(1234, true); c_au_node.inner.rate_pack.exit_byte_rate = 10; c_au_node.inner.rate_pack.exit_service_rate = 10; - let a_fr_key = &subject - .neighborhood_database - .add_node(a_fr_node) - .unwrap(); + let a_fr_key = &subject.neighborhood_database.add_node(a_fr_node).unwrap(); let b_fr_key = &subject .neighborhood_database .add_node(make_node_record(5678, true)) .unwrap(); - let c_au_key = &subject - .neighborhood_database - .add_node(c_au_node) - .unwrap(); + let c_au_key = &subject.neighborhood_database.add_node(c_au_node).unwrap(); subject .neighborhood_database .add_arbitrary_full_neighbor(root_key, b_fr_key); @@ -4765,13 +4793,7 @@ mod tests { let route_au = subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); - let exit_node = cdb.node_by_key( - &route_au - .as_ref() - .unwrap() - .last() - .unwrap(), - ); + let exit_node = cdb.node_by_key(&route_au.as_ref().unwrap().last().unwrap()); assert_eq!( exit_node.unwrap().inner.country_code_opt, Some("AU".to_string()) @@ -4826,13 +4848,7 @@ mod tests { let route_fr = subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); - let exit_node = cdb.node_by_key( - &route_fr - .as_ref() - .unwrap() - .last() - .unwrap(), - ); + let exit_node = cdb.node_by_key(&route_fr.as_ref().unwrap().last().unwrap()); assert_eq!( exit_node.unwrap().inner.country_code_opt, Some("FR".to_string()) @@ -7644,20 +7660,22 @@ mod tests { } /* - Database: - - - A---B---C---D---E - | | | | | - F---G---H---I---J - | | | | | - K---L---M---N---O - | | | | | - P---Q---R---S---T - | | | | | - U---V---W---X---Y - */ - fn make_db_with_regular_5_x_5_network(db: &mut NeighborhoodDatabase) -> HashMap<&'static str, PublicKey> { + Database: + + + A---B---C---D---E + | | | | | + F---G---H---I---J + | | | | | + K---L---M---N---O + | | | | | + P---Q---R---S---T + | | | | | + U---V---W---X---Y + */ + fn make_db_with_regular_5_x_5_network( + db: &mut NeighborhoodDatabase, + ) -> HashMap<&'static str, PublicKey> { let mut generator = 1000; let mut make_node = |db: &mut NeighborhoodDatabase| { let node = &db.add_node(make_node_record(generator, true)).unwrap(); @@ -7695,11 +7713,31 @@ mod tests { join_rows(db, (&k, &l, &m, &n, &o), (&p, &q, &r, &s, &t)); join_rows(db, (&p, &q, &r, &s, &t), (&u, &v, &w, &x, &y)); let keypairs = [ - ("a", a), ("b", b), ("c", c), ("d", d), ("e", e), - ("f", f), ("g", g), ("h", h), ("i", i), ("j", j), - ("k", k), ("l", l), ("m", m), ("n", n), ("o", o), - ("p", p), ("q", q), ("r", r), ("s", s), ("t", t), - ("u", u), ("v", v), ("w", w), ("x", x), ("y", y) + ("a", a), + ("b", b), + ("c", c), + ("d", d), + ("e", e), + ("f", f), + ("g", g), + ("h", h), + ("i", i), + ("j", j), + ("k", k), + ("l", l), + ("m", m), + ("n", n), + ("o", o), + ("p", p), + ("q", q), + ("r", r), + ("s", s), + ("t", t), + ("u", u), + ("v", v), + ("w", w), + ("x", x), + ("y", y), ]; HashMap::from_iter(keypairs) } diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs index d8104bf63..8502d444f 100644 --- a/node/src/neighborhood/node_record.rs +++ b/node/src/neighborhood/node_record.rs @@ -348,8 +348,8 @@ pub struct NodeRecordMetadata { pub node_addr_opt: Option, pub unreachable_hosts: HashSet, pub node_location_opt: Option, - // country_undesirability is used in combination with FallbackRouting. If FallbackRouting is set - // to false, we do not consider the undesirability of countries other than those selected for exit. + // country_undesirability is used in combination with FallbackRouting. If FallbackRouting is set + // to false, we do not consider the undesirability of countries other than those selected for exit. // Therefore, we use a value of 0 for exit nodes in countries that are not considered for exit. pub country_undesirability: u32, //TODO introduce scores for latency #582 and reliability #583 diff --git a/node/tests/ui_gateway_test.rs b/node/tests/ui_gateway_test.rs index 6e55643d6..eb3658f9c 100644 --- a/node/tests/ui_gateway_test.rs +++ b/node/tests/ui_gateway_test.rs @@ -105,7 +105,12 @@ fn wait_for_masq_node_ends() { panic!("Previous instance of MASQNode does not stops"); } system.refresh_all(); - if system.processes().into_iter().find(|(_, process)| process.name().contains("MASQNode")).is_none() { + if system + .processes() + .into_iter() + .find(|(_, process)| process.name().contains("MASQNode")) + .is_none() + { break; } thread::sleep(Duration::from_millis(500)); From 7adb418764a9fd29f30c25efb977de1ec0fc77c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vojte=CC=8Cch=20Parka=CC=81n?= Date: Wed, 9 Apr 2025 11:01:54 +0200 Subject: [PATCH 24/27] fallback routing test change --- node/src/neighborhood/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index cb324da49..1d722c77a 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -4789,9 +4789,10 @@ mod tests { show_countries: false, }; subject.handle_exit_location_message(message, 0, 0); + let subject_min_hops = 2; let route_au = - subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(root_key, None, subject_min_hops, 10000, RouteDirection::Over, None); let exit_node = cdb.node_by_key(&route_au.as_ref().unwrap().last().unwrap()); assert_eq!( From 9d97e23e23b8030be904e1588cc046c92a50d9af Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 9 Apr 2025 12:18:43 +0200 Subject: [PATCH 25/27] country codes to nodes names in tests --- node/src/neighborhood/mod.rs | 49 ++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 1d722c77a..c302ed400 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -3611,39 +3611,39 @@ mod tests { #[test] fn min_hops_change_affects_db_countries_and_exit_location_settings() { let mut subject = make_standard_subject(); - let root_node = subject.neighborhood_database.root().clone(); //CH - let neighbor_one = make_node_record(1234, true); //AU - let neighbor_two = make_node_record(2345, true); //FR - let neighbor_three = make_node_record(3456, true); //CN - let neighbor_four = make_node_record(4567, true); //US + let root_node_ch = subject.neighborhood_database.root().clone(); + let neighbor_one_au = make_node_record(1234, true); + let neighbor_two_fr = make_node_record(2345, true); + let neighbor_three_cn = make_node_record(3456, true); + let neighbor_four_us = make_node_record(4567, true); subject .neighborhood_database - .add_node(neighbor_one.clone()) + .add_node(neighbor_one_au.clone()) .unwrap(); subject .neighborhood_database - .add_node(neighbor_two.clone()) + .add_node(neighbor_two_fr.clone()) .unwrap(); subject .neighborhood_database - .add_node(neighbor_three.clone()) + .add_node(neighbor_three_cn.clone()) .unwrap(); subject .neighborhood_database - .add_node(neighbor_four.clone()) + .add_node(neighbor_four_us.clone()) .unwrap(); subject .neighborhood_database - .add_arbitrary_full_neighbor(root_node.public_key(), neighbor_one.public_key()); + .add_arbitrary_full_neighbor(root_node_ch.public_key(), neighbor_one_au.public_key()); subject .neighborhood_database - .add_arbitrary_full_neighbor(neighbor_one.public_key(), neighbor_two.public_key()); + .add_arbitrary_full_neighbor(neighbor_one_au.public_key(), neighbor_two_fr.public_key()); subject .neighborhood_database - .add_arbitrary_full_neighbor(neighbor_two.public_key(), neighbor_three.public_key()); + .add_arbitrary_full_neighbor(neighbor_two_fr.public_key(), neighbor_three_cn.public_key()); subject .neighborhood_database - .add_arbitrary_full_neighbor(neighbor_three.public_key(), neighbor_four.public_key()); + .add_arbitrary_full_neighbor(neighbor_three_cn.public_key(), neighbor_four_us.public_key()); subject.user_exit_preferences.db_countries = subject.init_db_countries(); let exit_locations_by_priority = vec![ExitLocation { country_codes: vec!["FR".to_string(), "US".to_string()], @@ -4707,11 +4707,6 @@ mod tests { | a_fr Test is written from the standpoint of root_key. - println!("a {}, b {}, c {}", - make_node_record(2345, true).inner.country_code_opt.unwrap(), - make_node_record(5678, true).inner.country_code_opt.unwrap(), - make_node_record(1234, true).inner.country_code_opt.unwrap() - ); */ #[test] @@ -4804,35 +4799,35 @@ mod tests { #[test] fn route_for_fr_country_code_is_constructed_without_fallback_routing() { let mut subject = make_standard_subject(); - let p = &subject + let root_key = &subject .neighborhood_database .root_mut() .public_key() .clone(); - let a = &subject + let a_fr = &subject .neighborhood_database .add_node(make_node_record(2345, true)) .unwrap(); - let b = &subject + let b_fr = &subject .neighborhood_database .add_node(make_node_record(5678, true)) .unwrap(); - let c = &subject + let c_au = &subject .neighborhood_database .add_node(make_node_record(1234, true)) .unwrap(); subject .neighborhood_database - .add_arbitrary_full_neighbor(p, b); + .add_arbitrary_full_neighbor(root_key, b_fr); subject .neighborhood_database - .add_arbitrary_full_neighbor(b, c); + .add_arbitrary_full_neighbor(b_fr, c_au); subject .neighborhood_database - .add_arbitrary_full_neighbor(b, a); + .add_arbitrary_full_neighbor(b_fr, a_fr); subject .neighborhood_database - .add_arbitrary_full_neighbor(a, c); + .add_arbitrary_full_neighbor(a_fr, c_au); let cdb = subject.neighborhood_database.clone(); let (recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(recipient); @@ -4847,7 +4842,7 @@ mod tests { subject.handle_exit_location_message(message, 0, 0); let route_fr = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); let exit_node = cdb.node_by_key(&route_fr.as_ref().unwrap().last().unwrap()); assert_eq!( From 2573729c941d28ad4b409ecdfebc084bd8dbc82d Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 9 Apr 2025 16:57:47 +0200 Subject: [PATCH 26/27] description to exit-location response --- USER-INTERFACE-INTERFACE.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/USER-INTERFACE-INTERFACE.md b/USER-INTERFACE-INTERFACE.md index b7886e423..134c6ae64 100644 --- a/USER-INTERFACE-INTERFACE.md +++ b/USER-INTERFACE-INTERFACE.md @@ -611,7 +611,7 @@ This command requests information about the countries available for exit in our desired locations with their priority. The priority provides the node's perspective on how important a particular country is for our preferences. -This command can be used in two ways: +This command can be used in two ways which can't be combined: 1. If we use the command with showCountries set to true, it retrieves information about the available countries in our neighborhood. In this case, other parameters are ignored. 2. If we want to set an exit location, we must set showCountries to false and then configure fallbackRouting and exitLocations with our preferences. @@ -637,12 +637,19 @@ only be used if a German exit is unavailable or significantly more expensive. "priority": }, ]>, - "exitCountries": "missingCountries": <[string, ..]> + "exitCountries": } ``` ##### Description: +In response, the Node sends a payload to the UI that contains either the Exit Location settings (which may include missing countries) or a list of exit countries. + +Exit Location settings consist of fallbackRouting, exitCountrySelection, and missingCountries, where: +1. fallbackRouting is a boolean representing the user's choice to enable or disable fallback routing within the neighborhood. +2. exitCountrySelection is an array of objects, where each object represents a set of country codes along with their assigned priority. +3. missingCountries is an array of strings representing a list of countries that are currently unavailable in the Node's Neighborhood Database. +Exit Countries (or exitCountries) is an optional array containing ISO country code strings. These represent the countries currently available in the Node's Neighborhood Database. The user can select from these countries to configure the Exit Location settings. #### `financials` ##### Direction: Request From 526908419bea9ffb6527b3d25f7219c9f8b12049 Mon Sep 17 00:00:00 2001 From: czarte Date: Wed, 9 Apr 2025 18:47:00 +0200 Subject: [PATCH 27/27] removing comments, adding country codes to node names in tests --- masq/src/commands/exit_location_command.rs | 4 +-- node/src/neighborhood/gossip_acceptor.rs | 32 ++++++++++------------ 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs index 5b949ac3c..ed4687fd4 100644 --- a/masq/src/commands/exit_location_command.rs +++ b/masq/src/commands/exit_location_command.rs @@ -289,8 +289,8 @@ pub mod tests { let message_body = Ok(UiSetExitLocationResponse { fallback_routing: true, exit_country_selection: vec![], - exit_countries: None, //Some(vec!["FR".to_string()]), - missing_countries: vec![], // vec!["CZ".to_string()], + exit_countries: None, + missing_countries: vec![], } .tmb(1234)); diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index a3e131554..02399be1b 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -2446,25 +2446,21 @@ mod tests { The source node(S) will gossip about Node A and B to the destination node(D). - - Country Codes - node_a: "FR" - node_b: "US" */ let src_root = make_node_record(1234, true); let dest_root = make_node_record(2345, true); let mut src_db = db_from_node(&src_root); - let node_a = make_node_record(5678, true); - let node_b = make_node_record(4567, true); + let node_a_fr = make_node_record(5678, true); + let node_b_us = make_node_record(4567, true); let mut dest_db = db_from_node(&dest_root); dest_db.add_node(src_root.clone()).unwrap(); dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), src_root.public_key()); src_db.add_node(dest_db.root().clone()).unwrap(); - src_db.add_node(node_a.clone()).unwrap(); - src_db.add_node(node_b.clone()).unwrap(); + src_db.add_node(node_a_fr.clone()).unwrap(); + src_db.add_node(node_b_us.clone()).unwrap(); src_db.add_arbitrary_full_neighbor(src_root.public_key(), dest_root.public_key()); - src_db.add_arbitrary_half_neighbor(src_root.public_key(), &node_a.public_key()); - src_db.add_arbitrary_full_neighbor(src_root.public_key(), &node_b.public_key()); + src_db.add_arbitrary_half_neighbor(src_root.public_key(), &node_a_fr.public_key()); + src_db.add_arbitrary_full_neighbor(src_root.public_key(), &node_b_us.public_key()); src_db .node_by_key_mut(src_root.public_key()) .unwrap() @@ -2472,8 +2468,8 @@ mod tests { src_db.resign_node(src_root.public_key()); let gossip = GossipBuilder::new(&src_db) .node(src_root.public_key(), true) - .node(node_a.public_key(), false) - .node(node_b.public_key(), false) + .node(node_a_fr.public_key(), false) + .node(node_b_us.public_key(), false) .build(); let subject = StandardGossipHandler::new(Logger::new("test")); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); @@ -2504,7 +2500,7 @@ mod tests { assert_eq!( dest_db - .node_by_key(node_a.public_key()) + .node_by_key(node_a_fr.public_key()) .unwrap() .metadata .country_undesirability, @@ -2512,7 +2508,7 @@ mod tests { ); assert_eq!( dest_db - .node_by_key(node_b.public_key()) + .node_by_key(node_b_us.public_key()) .unwrap() .metadata .country_undesirability, @@ -2526,12 +2522,12 @@ mod tests { ); assert!(dest_db.has_full_neighbor(dest_db.root().public_key(), src_db.root().public_key())); assert_eq!( - &src_db.node_by_key(node_a.public_key()).unwrap().inner, - &dest_db.node_by_key(node_a.public_key()).unwrap().inner + &src_db.node_by_key(node_a_fr.public_key()).unwrap().inner, + &dest_db.node_by_key(node_a_fr.public_key()).unwrap().inner ); assert_eq!( - &src_db.node_by_key(node_b.public_key()).unwrap().inner, - &dest_db.node_by_key(node_b.public_key()).unwrap().inner + &src_db.node_by_key(node_b_us.public_key()).unwrap().inner, + &dest_db.node_by_key(node_b_us.public_key()).unwrap().inner ); System::current().stop(); assert_eq!(system.run(), 0);