diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml
index 3b7c44c7a..3d7e47af5 100644
--- a/.github/workflows/ci-matrix.yml
+++ b/.github/workflows/ci-matrix.yml
@@ -4,7 +4,7 @@ on:
pull_request:
types: [opened, reopened, synchronize]
branches:
- - master
+ - GH-784
jobs:
build:
diff --git a/.idea/modules.xml b/.idea/modules.xml
index c32b128d9..861973a36 100644
--- a/.idea/modules.xml
+++ b/.idea/modules.xml
@@ -10,6 +10,7 @@
+
\ No newline at end of file
diff --git a/automap/Cargo.lock b/automap/Cargo.lock
index c970dc200..b2aaa5d2a 100644
--- a/automap/Cargo.lock
+++ b/automap/Cargo.lock
@@ -464,6 +464,27 @@ dependencies = [
"subtle 1.0.0",
]
+[[package]]
+name = "csv"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "cxx"
version = "1.0.94"
@@ -901,6 +922,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "ip_country"
+version = "0.1.0"
+dependencies = [
+ "csv",
+ "itertools 0.13.0",
+ "lazy_static",
+]
+
[[package]]
name = "ipconfig"
version = "0.1.9"
@@ -923,6 +953,15 @@ dependencies = [
"either",
]
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.6"
@@ -1059,7 +1098,8 @@ dependencies = [
"crossbeam-channel 0.5.8",
"dirs",
"ethereum-types",
- "itertools",
+ "ip_country",
+ "itertools 0.10.5",
"lazy_static",
"log 0.4.17",
"nix",
@@ -1067,6 +1107,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
+ "test_utilities",
"time 0.3.20",
"tiny-hderive",
"toml",
@@ -1927,6 +1968,10 @@ dependencies = [
"winapi-util",
]
+[[package]]
+name = "test_utilities"
+version = "0.1.0"
+
[[package]]
name = "textwrap"
version = "0.11.0"
diff --git a/ci/all.sh b/ci/all.sh
index 2f381c45c..6591cd392 100755
--- a/ci/all.sh
+++ b/ci/all.sh
@@ -8,7 +8,7 @@ ci/format.sh
export RUST_BACKTRACE=1
# Remove these two lines to slow down the build
-which sccache || cargo install sccache || echo "Skipping sccache installation" # Should do significant work only once
+which sccache || cargo install --version 0.4.1 sccache || echo "Skipping sccache installation" # Should do significant work only once
#export CARGO_TARGET_DIR="$CI_DIR/../cargo-cache"
export SCCACHE_DIR="$HOME/.cargo/sccache"
#export RUSTC_WRAPPER="$HOME/.cargo/bin/sccache"
diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock
index 52a3ec6b3..f1701c7d9 100644
--- a/dns_utility/Cargo.lock
+++ b/dns_utility/Cargo.lock
@@ -399,6 +399,27 @@ dependencies = [
"subtle 1.0.0",
]
+[[package]]
+name = "csv"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe"
+dependencies = [
+ "csv-core",
+ "itoa",
+ "ryu",
+ "serde",
+]
+
+[[package]]
+name = "csv-core"
+version = "0.1.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "digest"
version = "0.8.1"
@@ -716,6 +737,15 @@ dependencies = [
"libc",
]
+[[package]]
+name = "ip_country"
+version = "0.1.0"
+dependencies = [
+ "csv",
+ "itertools 0.13.0",
+ "lazy_static",
+]
+
[[package]]
name = "ipconfig"
version = "0.1.9"
@@ -750,6 +780,15 @@ dependencies = [
"either",
]
+[[package]]
+name = "itertools"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
+dependencies = [
+ "either",
+]
+
[[package]]
name = "itoa"
version = "1.0.6"
@@ -862,7 +901,8 @@ dependencies = [
"crossbeam-channel 0.5.8",
"dirs",
"ethereum-types",
- "itertools",
+ "ip_country",
+ "itertools 0.10.5",
"lazy_static",
"log 0.4.17",
"nix",
@@ -870,6 +910,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
+ "test_utilities",
"time 0.3.20",
"tiny-hderive",
"toml",
@@ -1653,6 +1694,10 @@ dependencies = [
"libc",
]
+[[package]]
+name = "test_utilities"
+version = "0.1.0"
+
[[package]]
name = "textwrap"
version = "0.11.0"
diff --git a/ip_country/Cargo.toml b/ip_country/Cargo.toml
index 7ba43fb60..0e57d7cac 100644
--- a/ip_country/Cargo.toml
+++ b/ip_country/Cargo.toml
@@ -13,8 +13,9 @@ workspace = "../node"
csv = "1.3.0"
itertools = "0.13.0"
lazy_static = "1.4.0"
-masq_lib = { path = "../masq_lib" }
+[dev-dependencies]
+test_utilities = { path = "../test_utilities"}
[[bin]]
name = "ip_country"
diff --git a/ip_country/src/country_finder.rs b/ip_country/src/country_finder.rs
index 270ecd76c..114c89421 100644
--- a/ip_country/src/country_finder.rs
+++ b/ip_country/src/country_finder.rs
@@ -54,9 +54,7 @@ impl CountryCodeFinder {
#[cfg(test)]
mod tests {
use super::*;
- use crate::country_block_serde::{
- CountryBlockDeserializer, Ipv4CountryBlockDeserializer, Ipv6CountryBlockDeserializer,
- };
+ use crate::country_block_serde::{Ipv4CountryBlockDeserializer, Ipv6CountryBlockDeserializer};
use crate::dbip_country;
use std::str::FromStr;
use std::time::SystemTime;
diff --git a/ip_country/src/ip_country.rs b/ip_country/src/ip_country.rs
index abf461aba..f4a7cbe13 100644
--- a/ip_country/src/ip_country.rs
+++ b/ip_country/src/ip_country.rs
@@ -133,8 +133,8 @@ fn write_value(
#[cfg(test)]
mod tests {
use super::*;
- use masq_lib::test_utils::fake_stream_holder::{ByteArrayReader, ByteArrayWriter};
use std::io::{Error, ErrorKind};
+ use test_utilities::byte_array_reader_writer::{ByteArrayReader, ByteArrayWriter};
static PROPER_TEST_DATA: &str = "0.0.0.0,0.255.255.255,ZZ
1.0.0.0,1.0.0.255,AU
diff --git a/masq/Cargo.toml b/masq/Cargo.toml
index 87a20ad67..dd9863021 100644
--- a/masq/Cargo.toml
+++ b/masq/Cargo.toml
@@ -28,6 +28,7 @@ nix = "0.23.0"
[dev-dependencies]
atty = "0.2.14"
+test_utilities = { path = "../test_utilities" }
[lib]
name = "masq_cli_lib"
diff --git a/masq/src/command_context.rs b/masq/src/command_context.rs
index 41308e5bb..e648da218 100644
--- a/masq/src/command_context.rs
+++ b/masq/src/command_context.rs
@@ -156,12 +156,12 @@ mod tests {
use crate::test_utils::mocks::TRANSACT_TIMEOUT_MILLIS_FOR_TESTS;
use masq_lib::messages::{FromMessageBody, UiCrashRequest, UiSetupRequest};
use masq_lib::messages::{ToMessageBody, UiShutdownRequest, UiShutdownResponse};
- use masq_lib::test_utils::fake_stream_holder::{ByteArrayReader, ByteArrayWriter};
use masq_lib::test_utils::mock_websockets_server::MockWebSocketsServer;
use masq_lib::ui_gateway::MessageBody;
use masq_lib::ui_gateway::MessagePath::Conversation;
use masq_lib::ui_traffic_converter::{TrafficConversionError, UnmarshalError};
use masq_lib::utils::{find_free_port, running_test};
+ use test_utilities::byte_array_reader_writer::{ByteArrayReader, ByteArrayWriter};
#[test]
fn constant_has_correct_values() {
diff --git a/masq/src/command_factory.rs b/masq/src/command_factory.rs
index 21222c454..6ec658903 100644
--- a/masq/src/command_factory.rs
+++ b/masq/src/command_factory.rs
@@ -8,6 +8,7 @@ use crate::commands::configuration_command::ConfigurationCommand;
use crate::commands::connection_status_command::ConnectionStatusCommand;
use crate::commands::crash_command::CrashCommand;
use crate::commands::descriptor_command::DescriptorCommand;
+use crate::commands::exit_location_command::SetExitLocationCommand;
use crate::commands::financials_command::FinancialsCommand;
use crate::commands::generate_wallets_command::GenerateWalletsCommand;
use crate::commands::recover_wallets_command::RecoverWalletsCommand;
@@ -52,6 +53,10 @@ impl CommandFactory for CommandFactoryReal {
Err(msg) => return Err(CommandSyntax(msg)),
},
"descriptor" => Box::new(DescriptorCommand::new()),
+ "exit-location" => match SetExitLocationCommand::new(pieces) {
+ Ok(command) => Box::new(command),
+ Err(msg) => return Err(CommandSyntax(msg)),
+ },
"financials" => match FinancialsCommand::new(pieces) {
Ok(command) => Box::new(command),
Err(msg) => return Err(CommandSyntax(msg)),
@@ -103,6 +108,7 @@ impl CommandFactoryReal {
mod tests {
use super::*;
use crate::command_factory::CommandFactoryError::UnrecognizedSubcommand;
+ use masq_lib::messages::CountryCodes;
#[test]
fn complains_about_unrecognized_subcommand() {
@@ -258,6 +264,34 @@ mod tests {
);
}
+ #[test]
+ fn factory_produces_exit_location() {
+ let subject = CommandFactoryReal::new();
+
+ let command = subject
+ .make(&[
+ "exit-location".to_string(),
+ "--country-codes".to_string(),
+ "CZ".to_string(),
+ ])
+ .unwrap();
+
+ assert_eq!(
+ command
+ .as_any()
+ .downcast_ref::()
+ .unwrap(),
+ &SetExitLocationCommand {
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1
+ }],
+ fallback_routing: false,
+ show_countries: false,
+ }
+ );
+ }
+
#[test]
fn complains_about_set_configuration_command_with_no_parameters() {
let subject = CommandFactoryReal::new();
diff --git a/masq/src/commands/exit_location_command.rs b/masq/src/commands/exit_location_command.rs
new file mode 100644
index 000000000..552bab768
--- /dev/null
+++ b/masq/src/commands/exit_location_command.rs
@@ -0,0 +1,274 @@
+use crate::command_context::CommandContext;
+use crate::commands::commands_common::{
+ transaction, Command, CommandError, STANDARD_COMMAND_TIMEOUT_MILLIS,
+};
+use clap::{App, Arg, SubCommand};
+use masq_lib::as_any_ref_in_trait_impl;
+use masq_lib::messages::{CountryCodes, UiSetExitLocationRequest, UiSetExitLocationResponse};
+use masq_lib::shared_schema::common_validators;
+
+const EXIT_LOCATION_ABOUT: &str =
+ "If you activate exit-location preferences, all exit Nodes in countries you don't specify will be prohibited: \n\
+ that is, if there is no exit Node available in any of your preferred countries, you'll get an error. However, \
+ if you just want to make a suggestion, and you don't mind Nodes in other countries being used if nothing is available \
+ in your preferred countries, you can specify --fallback-routing, and you'll get no error unless there are no exit Nodes \
+ available anywhere.\n\n\
+ Here are some example commands:\n\
+ masq> exit-location // disable exit-location preferences\n\
+ masq> exit-location --fallback-routing // disable exit-location preferences\n\
+ masq> exit-location --country-codes \"CZ,PL|SK\" --fallback-routing \n\t// fallback-routing is ON, \"CZ\" and \"PL\" countries have same priority \"1\", \"SK\" has priority \"2\"\n\
+ masq> exit-location --country-codes \"CZ|SK\" \n\t// fallback-routing is OFF, \"CZ\" and \"SK\" countries have different priority\n";
+
+// TODO update following help when GH-469 is done with `To obtain codes, you can use the 'country-codes-list' (469 card command) command.`
+const COUNTRY_CODES_HELP: &str = "Establish a set of countries that your Node should try to use for exit Nodes. You should choose from the countries that host the \
+ Nodes in your Neighborhood. List the countries in order of preference, separated by vertical pipes (|). If your level of preference \
+ for a group of countries is the same, separate those countries by commas (,).\n\
+ You can specify country codes as follows:\n\n\
+ masq> exit-location --country-codes \"CZ,PL|SK\" \n\t// \"CZ\" and \"PL\" countries have same priority \"1\", \"SK\" has priority \"2\" \n\
+ masq> exit-location --country-codes \"CZ|SK\" \n\t// \"CZ\" and \"SK\" countries have different priority\n\n";
+
+const FALLBACK_ROUTING_HELP: &str = "If you just want to make a suggestion, and you don't mind Nodes in other countries being used if nothing is available \
+ in your preferred countries, you can specify --fallback-routing, and you'll get no error unless there are no exit Nodes \
+ available anywhere. \n Here are some examples: \n\n\
+ 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";
+
+pub fn exit_location_subcommand() -> App<'static, 'static> {
+ SubCommand::with_name("exit-location").about(EXIT_LOCATION_ABOUT)
+}
+
+#[derive(Debug, PartialEq, Eq)]
+pub struct SetExitLocationCommand {
+ pub exit_locations: Vec,
+ pub fallback_routing: bool,
+ pub show_countries: bool,
+}
+
+impl SetExitLocationCommand {
+ pub fn new(pieces: &[String]) -> Result {
+ match set_exit_location_subcommand().get_matches_from_safe(pieces) {
+ Ok(matches) => {
+ let exit_locations = match matches.is_present("country-codes") {
+ true => matches
+ .values_of("country-codes")
+ .expect("Expected Country Codes")
+ .into_iter()
+ .enumerate()
+ .map(|(index, code)| CountryCodes::from((code.to_string(), index)))
+ .collect(),
+ false => vec![],
+ };
+ let fallback_routing = !matches!(
+ (
+ matches.is_present("fallback-routing"),
+ matches.is_present("country-codes")
+ ),
+ (false, true)
+ );
+ let show_countries = matches.is_present("show-countries");
+ Ok(SetExitLocationCommand {
+ exit_locations,
+ fallback_routing,
+ show_countries,
+ })
+ }
+
+ Err(e) => Err(format!("SetExitLocationCommand {}", e)),
+ }
+ }
+}
+
+impl Command for SetExitLocationCommand {
+ fn execute(&self, context: &mut dyn CommandContext) -> Result<(), CommandError> {
+ let input = UiSetExitLocationRequest {
+ exit_locations: self.exit_locations.clone(),
+ fallback_routing: self.fallback_routing,
+ show_countries: self.show_countries,
+ };
+
+ let _: UiSetExitLocationResponse =
+ transaction(input, context, STANDARD_COMMAND_TIMEOUT_MILLIS)?;
+ Ok(())
+ }
+
+ as_any_ref_in_trait_impl!();
+}
+
+pub fn set_exit_location_subcommand() -> App<'static, 'static> {
+ SubCommand::with_name("exit-location")
+ .about(EXIT_LOCATION_ABOUT)
+ .arg(
+ Arg::with_name("country-codes")
+ .long("country-codes")
+ .value_name("COUNTRY-CODES")
+ .value_delimiter("|")
+ .validator(common_validators::validate_exit_locations)
+ .help(COUNTRY_CODES_HELP)
+ .required(false),
+ )
+ .arg(
+ Arg::with_name("fallback-routing")
+ .long("fallback-routing")
+ .value_name("FALLBACK-ROUTING")
+ .help(FALLBACK_ROUTING_HELP)
+ .takes_value(false)
+ .required(false),
+ )
+ .arg(
+ Arg::with_name("show-countries")
+ .long("show-countries")
+ .value_name("SHOW-COUNTRIES")
+ .takes_value(false)
+ .required(false),
+ )
+}
+
+#[cfg(test)]
+pub mod tests {
+ use crate::commands::commands_common::{Command, STANDARD_COMMAND_TIMEOUT_MILLIS};
+ use crate::commands::exit_location_command::SetExitLocationCommand;
+ use crate::test_utils::mocks::CommandContextMock;
+ use masq_lib::messages::{
+ CountryCodes, ToMessageBody, UiSetExitLocationRequest, UiSetExitLocationResponse,
+ };
+ use std::sync::{Arc, Mutex};
+
+ #[test]
+ fn can_deserialize_ui_set_exit_location() {
+ let transact_params_arc = Arc::new(Mutex::new(vec![]));
+ let mut context = CommandContextMock::new()
+ .transact_params(&transact_params_arc)
+ .transact_result(Ok(UiSetExitLocationResponse {}.tmb(0)));
+ let stderr_arc = context.stderr_arc();
+ let subject = SetExitLocationCommand::new(&[
+ "exit-location".to_string(),
+ "--country-codes".to_string(),
+ "CZ,SK|AT,DE|PL".to_string(),
+ "--fallback-routing".to_string(),
+ ])
+ .unwrap();
+
+ let result = subject.execute(&mut context);
+
+ assert_eq!(result, Ok(()));
+ let expected_request = UiSetExitLocationRequest {
+ fallback_routing: true,
+ exit_locations: vec![
+ CountryCodes {
+ country_codes: vec!["CZ".to_string(), "SK".to_string()],
+ priority: 1,
+ },
+ CountryCodes {
+ country_codes: vec!["AT".to_string(), "DE".to_string()],
+ priority: 2,
+ },
+ CountryCodes {
+ country_codes: vec!["PL".to_string()],
+ priority: 3,
+ },
+ ],
+ 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(), "");
+ }
+
+ #[test]
+ fn absence_of_fallback_routing_produces_fallback_routing_false() {
+ let transact_params_arc = Arc::new(Mutex::new(vec![]));
+ let mut context = CommandContextMock::new()
+ .transact_params(&transact_params_arc)
+ .transact_result(Ok(UiSetExitLocationResponse {}.tmb(0)));
+ let stderr_arc = context.stderr_arc();
+ let subject = SetExitLocationCommand::new(&[
+ "exit-location".to_string(),
+ "--country-codes".to_string(),
+ "CZ".to_string(),
+ ])
+ .unwrap();
+
+ let result = subject.execute(&mut context);
+
+ assert_eq!(result, Ok(()));
+ let expected_request = UiSetExitLocationRequest {
+ fallback_routing: false,
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1,
+ }],
+ 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(), "");
+ }
+
+ #[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 {}.tmb(0)));
+ let stderr_arc = context.stderr_arc();
+ let subject = SetExitLocationCommand::new(&["exit-location".to_string()]).unwrap();
+
+ let result = subject.execute(&mut context);
+
+ 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(), "");
+ }
+
+ #[test]
+ fn providing_show_countries_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 {}.tmb(0)));
+ let stderr_arc = context.stderr_arc();
+ let subject = SetExitLocationCommand::new(&[
+ "exit-location".to_string(),
+ "--show-countries".to_string(),
+ ])
+ .unwrap();
+
+ let result = subject.execute(&mut context);
+
+ assert_eq!(result, Ok(()));
+ let expected_request = UiSetExitLocationRequest {
+ fallback_routing: true,
+ exit_locations: vec![],
+ show_countries: true,
+ };
+ 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(), "");
+ }
+}
diff --git a/masq/src/commands/mod.rs b/masq/src/commands/mod.rs
index 44026ebf8..143dff8e1 100644
--- a/masq/src/commands/mod.rs
+++ b/masq/src/commands/mod.rs
@@ -7,6 +7,7 @@ pub mod configuration_command;
pub mod connection_status_command;
pub mod crash_command;
pub mod descriptor_command;
+pub mod exit_location_command;
pub mod financials_command;
pub mod generate_wallets_command;
pub mod recover_wallets_command;
diff --git a/masq/src/commands/set_configuration_command.rs b/masq/src/commands/set_configuration_command.rs
index 99d979f07..dbe17e753 100644
--- a/masq/src/commands/set_configuration_command.rs
+++ b/masq/src/commands/set_configuration_command.rs
@@ -84,6 +84,8 @@ pub fn set_configuration_subcommand() -> App<'static, 'static> {
.args(&["gas-price", "min-hops", "start-block"])
.required(true),
)
+ //TODO here is the place we want to place function to set country_code for ExitService,
+ // this function will be used in shared_app to setup the country_code for ExitService on startup
}
#[cfg(test)]
diff --git a/masq/src/interactive_mode.rs b/masq/src/interactive_mode.rs
index a9141ffa5..026ed4aef 100644
--- a/masq/src/interactive_mode.rs
+++ b/masq/src/interactive_mode.rs
@@ -168,10 +168,11 @@ mod tests {
CommandFactoryMock, CommandProcessorMock, TerminalActiveMock, TerminalPassiveMock,
};
use crossbeam_channel::bounded;
- use masq_lib::test_utils::fake_stream_holder::{ByteArrayWriter, FakeStreamHolder};
+ use masq_lib::test_utils::fake_stream_holder::FakeStreamHolder;
use std::sync::{Arc, Mutex};
use std::thread;
use std::time::{Duration, Instant};
+ use test_utilities::byte_array_reader_writer::ByteArrayWriter;
#[test]
fn interactive_mode_works_for_unrecognized_command() {
diff --git a/masq/src/notifications/connection_change_notification.rs b/masq/src/notifications/connection_change_notification.rs
index 9ea7d5911..0480660fb 100644
--- a/masq/src/notifications/connection_change_notification.rs
+++ b/masq/src/notifications/connection_change_notification.rs
@@ -34,9 +34,9 @@ impl ConnectionChangeNotification {
mod tests {
use super::*;
use crate::test_utils::mocks::TerminalPassiveMock;
- use masq_lib::test_utils::fake_stream_holder::ByteArrayWriter;
use masq_lib::utils::running_test;
use std::sync::Arc;
+ use test_utilities::byte_array_reader_writer::ByteArrayWriter;
#[test]
fn broadcasts_connected_to_neighbor() {
diff --git a/masq/src/notifications/crashed_notification.rs b/masq/src/notifications/crashed_notification.rs
index 36e0ff4f4..66dfc773c 100644
--- a/masq/src/notifications/crashed_notification.rs
+++ b/masq/src/notifications/crashed_notification.rs
@@ -63,9 +63,9 @@ impl CrashNotifier {
mod tests {
use super::*;
use crate::test_utils::mocks::TerminalPassiveMock;
- use masq_lib::test_utils::fake_stream_holder::ByteArrayWriter;
use masq_lib::utils::running_test;
use std::sync::Arc;
+ use test_utilities::byte_array_reader_writer::ByteArrayWriter;
#[test]
pub fn handles_child_wait_failure() {
diff --git a/masq/src/schema.rs b/masq/src/schema.rs
index c03a3ea39..287fd9468 100644
--- a/masq/src/schema.rs
+++ b/masq/src/schema.rs
@@ -8,6 +8,7 @@ use crate::commands::configuration_command::configuration_subcommand;
use crate::commands::connection_status_command::connection_status_subcommand;
use crate::commands::crash_command::crash_subcommand;
use crate::commands::descriptor_command::descriptor_subcommand;
+use crate::commands::exit_location_command::exit_location_subcommand;
use crate::commands::financials_command::args_validation::financials_subcommand;
use crate::commands::generate_wallets_command::generate_wallets_subcommand;
use crate::commands::recover_wallets_command::recover_wallets_subcommand;
@@ -67,6 +68,7 @@ pub fn app() -> App<'static, 'static> {
.subcommand(configuration_subcommand())
.subcommand(connection_status_subcommand())
.subcommand(descriptor_subcommand())
+ .subcommand(exit_location_subcommand())
.subcommand(financials_subcommand())
.subcommand(generate_wallets_subcommand())
.subcommand(recover_wallets_subcommand())
diff --git a/masq/src/terminal/integration_test_utils.rs b/masq/src/terminal/integration_test_utils.rs
index 7f42f72f5..526e19a66 100644
--- a/masq/src/terminal/integration_test_utils.rs
+++ b/masq/src/terminal/integration_test_utils.rs
@@ -187,9 +187,9 @@ mod tests {
use crate::terminal::terminal_interface::TerminalWrapper;
use crate::test_utils::mocks::StdoutBlender;
use crossbeam_channel::{bounded, unbounded};
- use masq_lib::test_utils::fake_stream_holder::ByteArrayReader;
use std::thread;
use std::time::Duration;
+ use test_utilities::byte_array_reader_writer::ByteArrayReader;
#[test]
fn constants_have_correct_values() {
diff --git a/masq/src/test_utils/mocks.rs b/masq/src/test_utils/mocks.rs
index 955fa578a..9f70b7e5b 100644
--- a/masq/src/test_utils/mocks.rs
+++ b/masq/src/test_utils/mocks.rs
@@ -15,7 +15,6 @@ use linefeed::memory::MemoryTerminal;
use linefeed::{Interface, ReadResult, Signal};
use masq_lib::command::StdStreams;
use masq_lib::constants::DEFAULT_UI_PORT;
-use masq_lib::test_utils::fake_stream_holder::{ByteArrayWriter, ByteArrayWriterInner};
use masq_lib::ui_gateway::MessageBody;
use std::cell::RefCell;
use std::fmt::Arguments;
@@ -23,6 +22,7 @@ use std::io::{Read, Write};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use std::{io, thread};
+use test_utilities::byte_array_reader_writer::{ByteArrayWriter, ByteArrayWriterInner};
pub const TRANSACT_TIMEOUT_MILLIS_FOR_TESTS: u64 = DEFAULT_TRANSACT_TIMEOUT_MILLIS;
diff --git a/masq_lib/Cargo.toml b/masq_lib/Cargo.toml
index f87de81a1..0f36bf8a9 100644
--- a/masq_lib/Cargo.toml
+++ b/masq_lib/Cargo.toml
@@ -15,12 +15,14 @@ crossbeam-channel = "0.5.1"
dirs = "4.0.0"
ethereum-types = "0.9.0"
itertools = "0.10.1"
+ip_country = { path = "../ip_country"}
lazy_static = "1.4.0"
log = "0.4.8"
regex = "1.5.4"
serde = "1.0.133"
serde_derive = "1.0.133"
serde_json = "1.0.74"
+test_utilities = { path = "../test_utilities"}
time = {version = "0.3.11", features = [ "formatting" ]}
tiny-hderive = "0.3.0"
toml = "0.5.8"
diff --git a/masq_lib/src/blockchains/blockchain_records.rs b/masq_lib/src/blockchains/blockchain_records.rs
index f9b4bcab0..5227195f7 100644
--- a/masq_lib/src/blockchains/blockchain_records.rs
+++ b/masq_lib/src/blockchains/blockchain_records.rs
@@ -2,15 +2,14 @@
use crate::blockchains::chains::Chain;
use crate::constants::{
- DEV_CHAIN_FULL_IDENTIFIER, ETH_MAINNET_CONTRACT_CREATION_BLOCK, ETH_MAINNET_FULL_IDENTIFIER,
- ETH_ROPSTEN_FULL_IDENTIFIER, MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK,
- MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK, POLYGON_MAINNET_CONTRACT_CREATION_BLOCK,
- POLYGON_MAINNET_FULL_IDENTIFIER, POLYGON_MUMBAI_FULL_IDENTIFIER,
+ AMOY_TESTNET_CONTRACT_CREATION_BLOCK, DEV_CHAIN_FULL_IDENTIFIER,
+ ETH_MAINNET_CONTRACT_CREATION_BLOCK, ETH_MAINNET_FULL_IDENTIFIER, ETH_ROPSTEN_FULL_IDENTIFIER,
+ MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK, POLYGON_AMOY_FULL_IDENTIFIER,
+ POLYGON_MAINNET_CONTRACT_CREATION_BLOCK, POLYGON_MAINNET_FULL_IDENTIFIER,
ROPSTEN_TESTNET_CONTRACT_CREATION_BLOCK,
};
use ethereum_types::{Address, H160};
-//chains are ordered by their significance for the community of users (the order reflects in some error or help messages)
pub const CHAINS: [BlockchainRecord; 5] = [
BlockchainRecord {
self_id: Chain::PolyMainnet,
@@ -27,11 +26,11 @@ pub const CHAINS: [BlockchainRecord; 5] = [
contract_creation_block: ETH_MAINNET_CONTRACT_CREATION_BLOCK,
},
BlockchainRecord {
- self_id: Chain::PolyMumbai,
- num_chain_id: 80001,
- literal_identifier: POLYGON_MUMBAI_FULL_IDENTIFIER,
- contract: MUMBAI_TESTNET_CONTRACT_ADDRESS,
- contract_creation_block: MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK,
+ self_id: Chain::PolyAmoy,
+ num_chain_id: 80002,
+ literal_identifier: POLYGON_AMOY_FULL_IDENTIFIER,
+ contract: AMOY_TESTNET_CONTRACT_ADDRESS,
+ contract_creation_block: AMOY_TESTNET_CONTRACT_CREATION_BLOCK,
},
BlockchainRecord {
self_id: Chain::EthRopsten,
@@ -58,6 +57,22 @@ pub struct BlockchainRecord {
pub contract_creation_block: u64,
}
+const POLYGON_MAINNET_CONTRACT_ADDRESS: Address = H160([
+ 0xee, 0x9a, 0x35, 0x2f, 0x6a, 0xac, 0x4a, 0xf1, 0xa5, 0xb9, 0xf4, 0x67, 0xf6, 0xa9, 0x3e, 0x0f,
+ 0xfb, 0xe9, 0xdd, 0x35,
+]);
+
+const ETH_MAINNET_CONTRACT_ADDRESS: Address = H160([
+ 0x06, 0xf3, 0xc3, 0x23, 0xf0, 0x23, 0x8c, 0x72, 0xbf, 0x35, 0x01, 0x10, 0x71, 0xf2, 0xb5, 0xb7,
+ 0xf4, 0x3a, 0x05, 0x4c,
+]);
+
+// $tMASQ (Amoy)
+const AMOY_TESTNET_CONTRACT_ADDRESS: Address = H160([
+ 0xd9, 0x8c, 0x3e, 0xbd, 0x6b, 0x7f, 0x9b, 0x7c, 0xda, 0x24, 0x49, 0xec, 0xac, 0x00, 0xd1, 0xe5,
+ 0xf4, 0x7a, 0x81, 0x93,
+]);
+
// SHRD (Ropsten)
const ROPSTEN_TESTNET_CONTRACT_ADDRESS: Address = H160([
0x38, 0x4d, 0xec, 0x25, 0xe0, 0x3f, 0x94, 0x93, 0x17, 0x67, 0xce, 0x4c, 0x35, 0x56, 0x16, 0x84,
@@ -69,30 +84,12 @@ const MULTINODE_TESTNET_CONTRACT_ADDRESS: Address = H160([
0xf1, 0xb3, 0xe6, 0x64,
]);
-const ETH_MAINNET_CONTRACT_ADDRESS: Address = H160([
- 0x06, 0xF3, 0xC3, 0x23, 0xf0, 0x23, 0x8c, 0x72, 0xBF, 0x35, 0x01, 0x10, 0x71, 0xf2, 0xb5, 0xB7,
- 0xF4, 0x3A, 0x05, 0x4c,
-]);
-
-#[allow(clippy::mixed_case_hex_literals)]
-const POLYGON_MAINNET_CONTRACT_ADDRESS: Address = H160([
- 0xEe, 0x9A, 0x35, 0x2F, 0x6a, 0xAc, 0x4a, 0xF1, 0xA5, 0xB9, 0xf4, 0x67, 0xF6, 0xa9, 0x3E, 0x0f,
- 0xfB, 0xe9, 0xDd, 0x35,
-]);
-
-// $tMASQ (Mumbai)
-#[allow(clippy::mixed_case_hex_literals)]
-const MUMBAI_TESTNET_CONTRACT_ADDRESS: Address = H160([
- 0x9B, 0x27, 0x03, 0x4a, 0xca, 0xBd, 0x44, 0x22, 0x3f, 0xB2, 0x3d, 0x62, 0x8B, 0xa4, 0x84, 0x98,
- 0x67, 0xcE, 0x1D, 0xB2,
-]);
-
#[cfg(test)]
mod tests {
use super::*;
use crate::blockchains::chains::chain_from_chain_identifier_opt;
use crate::constants::{
- MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK, POLYGON_MAINNET_CONTRACT_CREATION_BLOCK,
+ AMOY_TESTNET_CONTRACT_CREATION_BLOCK, POLYGON_MAINNET_CONTRACT_CREATION_BLOCK,
};
use std::collections::HashSet;
use std::iter::FromIterator;
@@ -104,7 +101,7 @@ mod tests {
assert_returns_correct_record(Chain::Dev, 2),
assert_returns_correct_record(Chain::EthRopsten, 3),
assert_returns_correct_record(Chain::PolyMainnet, 137),
- assert_returns_correct_record(Chain::PolyMumbai, 80001),
+ assert_returns_correct_record(Chain::PolyAmoy, 80002),
];
assert_exhaustive(&test_array)
}
@@ -118,7 +115,7 @@ mod tests {
fn from_str_works() {
let test_array = [
assert_from_str(Chain::PolyMainnet),
- assert_from_str(Chain::PolyMumbai),
+ assert_from_str(Chain::PolyAmoy),
assert_from_str(Chain::EthMainnet),
assert_from_str(Chain::EthRopsten),
assert_from_str(Chain::Dev),
@@ -142,7 +139,7 @@ mod tests {
let test_array = [
assert_chain_significance(0, Chain::PolyMainnet),
assert_chain_significance(1, Chain::EthMainnet),
- assert_chain_significance(2, Chain::PolyMumbai),
+ assert_chain_significance(2, Chain::PolyAmoy),
assert_chain_significance(3, Chain::EthRopsten),
assert_chain_significance(4, Chain::Dev),
];
@@ -203,17 +200,17 @@ mod tests {
}
#[test]
- fn mumbai_record_is_properly_declared() {
- let examined_chain = Chain::PolyMumbai;
+ fn amoy_record_is_properly_declared() {
+ let examined_chain = Chain::PolyAmoy;
let chain_record = return_examined(examined_chain);
assert_eq!(
chain_record,
&BlockchainRecord {
- num_chain_id: 80001,
+ num_chain_id: 80002,
self_id: examined_chain,
- literal_identifier: "polygon-mumbai",
- contract: MUMBAI_TESTNET_CONTRACT_ADDRESS,
- contract_creation_block: MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK,
+ literal_identifier: "polygon-amoy",
+ contract: AMOY_TESTNET_CONTRACT_ADDRESS,
+ contract_creation_block: AMOY_TESTNET_CONTRACT_CREATION_BLOCK,
}
);
}
@@ -245,7 +242,7 @@ mod tests {
assert_chain_from_chain_identifier_opt("eth-ropsten", Some(Chain::EthRopsten)),
assert_chain_from_chain_identifier_opt("dev", Some(Chain::Dev)),
assert_chain_from_chain_identifier_opt("polygon-mainnet", Some(Chain::PolyMainnet)),
- assert_chain_from_chain_identifier_opt("polygon-mumbai", Some(Chain::PolyMumbai)),
+ assert_chain_from_chain_identifier_opt("polygon-amoy", Some(Chain::PolyAmoy)),
];
assert_exhaustive(&test_array)
}
diff --git a/masq_lib/src/blockchains/chains.rs b/masq_lib/src/blockchains/chains.rs
index ac3fbbac0..433d5524c 100644
--- a/masq_lib/src/blockchains/chains.rs
+++ b/masq_lib/src/blockchains/chains.rs
@@ -3,7 +3,7 @@
use crate::blockchains::blockchain_records::{BlockchainRecord, CHAINS};
use crate::constants::{
DEFAULT_CHAIN, DEV_CHAIN_FULL_IDENTIFIER, ETH_MAINNET_FULL_IDENTIFIER,
- ETH_ROPSTEN_FULL_IDENTIFIER, POLYGON_MAINNET_FULL_IDENTIFIER, POLYGON_MUMBAI_FULL_IDENTIFIER,
+ ETH_ROPSTEN_FULL_IDENTIFIER, POLYGON_AMOY_FULL_IDENTIFIER, POLYGON_MAINNET_FULL_IDENTIFIER,
};
use serde_derive::{Deserialize, Serialize};
@@ -12,7 +12,7 @@ pub enum Chain {
EthMainnet,
EthRopsten,
PolyMainnet,
- PolyMumbai,
+ PolyAmoy,
Dev,
}
@@ -28,8 +28,8 @@ impl From<&str> for Chain {
Chain::PolyMainnet
} else if str == ETH_MAINNET_FULL_IDENTIFIER {
Chain::EthMainnet
- } else if str == POLYGON_MUMBAI_FULL_IDENTIFIER {
- Chain::PolyMumbai
+ } else if str == POLYGON_AMOY_FULL_IDENTIFIER {
+ Chain::PolyAmoy
} else if str == ETH_ROPSTEN_FULL_IDENTIFIER {
Chain::EthRopsten
} else if str == DEV_CHAIN_FULL_IDENTIFIER {
diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs
index 7bdcbe1e1..6d758a4c2 100644
--- a/masq_lib/src/constants.rs
+++ b/masq_lib/src/constants.rs
@@ -26,9 +26,9 @@ pub const WEIS_IN_GWEI: i128 = 1_000_000_000;
pub const ETH_MAINNET_CONTRACT_CREATION_BLOCK: u64 = 11_170_708;
pub const ROPSTEN_TESTNET_CONTRACT_CREATION_BLOCK: u64 = 8_688_171;
-pub const MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK: u64 = 0;
pub const POLYGON_MAINNET_CONTRACT_CREATION_BLOCK: u64 = 14_863_650;
-pub const MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK: u64 = 24_638_838;
+pub const AMOY_TESTNET_CONTRACT_CREATION_BLOCK: u64 = 5_323_366;
+pub const MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK: u64 = 0;
//Migration versions
////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -71,6 +71,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;
//accountant
pub const ACCOUNTANT_PREFIX: u64 = 0x0040_0000_0000_0000;
@@ -87,15 +88,15 @@ pub const CENTRAL_DELIMITER: char = '@';
pub const CHAIN_IDENTIFIER_DELIMITER: char = ':';
//chains
-const MAINNET: &str = "mainnet";
const POLYGON_FAMILY: &str = "polygon";
const ETH_FAMILY: &str = "eth";
+const MAINNET: &str = "mainnet";
const LINK: char = '-';
pub const POLYGON_MAINNET_FULL_IDENTIFIER: &str = concatcp!(POLYGON_FAMILY, LINK, MAINNET);
-pub const POLYGON_MUMBAI_FULL_IDENTIFIER: &str = concatcp!(POLYGON_FAMILY, LINK, "mumbai");
-pub const DEV_CHAIN_FULL_IDENTIFIER: &str = "dev";
+pub const POLYGON_AMOY_FULL_IDENTIFIER: &str = concatcp!(POLYGON_FAMILY, LINK, "amoy");
pub const ETH_MAINNET_FULL_IDENTIFIER: &str = concatcp!(ETH_FAMILY, LINK, MAINNET);
pub const ETH_ROPSTEN_FULL_IDENTIFIER: &str = concatcp!(ETH_FAMILY, LINK, "ropsten");
+pub const DEV_CHAIN_FULL_IDENTIFIER: &str = "dev";
#[cfg(test)]
mod tests {
@@ -119,9 +120,9 @@ mod tests {
assert_eq!(WEIS_IN_GWEI, 1_000_000_000);
assert_eq!(ETH_MAINNET_CONTRACT_CREATION_BLOCK, 11_170_708);
assert_eq!(ROPSTEN_TESTNET_CONTRACT_CREATION_BLOCK, 8_688_171);
- assert_eq!(MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK, 0);
assert_eq!(POLYGON_MAINNET_CONTRACT_CREATION_BLOCK, 14_863_650);
- assert_eq!(MUMBAI_TESTNET_CONTRACT_CREATION_BLOCK, 24_638_838);
+ assert_eq!(AMOY_TESTNET_CONTRACT_CREATION_BLOCK, 5_323_366);
+ assert_eq!(MULTINODE_TESTNET_CONTRACT_CREATION_BLOCK, 0);
assert_eq!(CONFIGURATOR_PREFIX, 0x0001_0000_0000_0000);
assert_eq!(CONFIGURATOR_READ_ERROR, CONFIGURATOR_PREFIX | 1);
assert_eq!(CONFIGURATOR_WRITE_ERROR, CONFIGURATOR_PREFIX | 2);
@@ -157,15 +158,15 @@ mod tests {
assert_eq!(VALUE_EXCEEDS_ALLOWED_LIMIT, ACCOUNTANT_PREFIX | 3);
assert_eq!(CENTRAL_DELIMITER, '@');
assert_eq!(CHAIN_IDENTIFIER_DELIMITER, ':');
- assert_eq!(MAINNET, "mainnet");
assert_eq!(POLYGON_FAMILY, "polygon");
assert_eq!(ETH_FAMILY, "eth");
+ assert_eq!(MAINNET, "mainnet");
assert_eq!(LINK, '-');
assert_eq!(POLYGON_MAINNET_FULL_IDENTIFIER, "polygon-mainnet");
- assert_eq!(POLYGON_MUMBAI_FULL_IDENTIFIER, "polygon-mumbai");
- assert_eq!(DEV_CHAIN_FULL_IDENTIFIER, "dev");
+ assert_eq!(POLYGON_AMOY_FULL_IDENTIFIER, "polygon-amoy");
assert_eq!(ETH_MAINNET_FULL_IDENTIFIER, "eth-mainnet");
assert_eq!(ETH_ROPSTEN_FULL_IDENTIFIER, "eth-ropsten");
+ assert_eq!(DEV_CHAIN_FULL_IDENTIFIER, "dev");
assert_eq!(
CLIENT_REQUEST_PAYLOAD_CURRENT_VERSION,
DataVersion { major: 0, minor: 1 }
diff --git a/masq_lib/src/messages.rs b/masq_lib/src/messages.rs
index 59522171e..1366db4d6 100644
--- a/masq_lib/src/messages.rs
+++ b/masq_lib/src/messages.rs
@@ -846,6 +846,44 @@ pub struct UiWalletAddressesResponse {
}
conversation_message!(UiWalletAddressesResponse, "walletAddresses");
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+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 {
+ CountryCodes {
+ country_codes: item
+ .split(',')
+ .into_iter()
+ .map(|x| x.to_string())
+ .collect::>(),
+ priority: priority + 1,
+ }
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+pub struct UiSetExitLocationRequest {
+ #[serde(rename = "fallbackRouting")]
+ pub fallback_routing: bool,
+ #[serde(rename = "exitLocations")]
+ pub exit_locations: Vec,
+ #[serde(rename = "showCountries")]
+ pub show_countries: bool,
+}
+
+conversation_message!(UiSetExitLocationRequest, "exitLocation");
+
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
+pub struct UiSetExitLocationResponse {}
+
+conversation_message!(UiSetExitLocationResponse, "exitLocation");
+
#[cfg(test)]
mod tests {
use super::*;
diff --git a/masq_lib/src/shared_schema.rs b/masq_lib/src/shared_schema.rs
index fc7e8ced2..3c619e429 100644
--- a/masq_lib/src/shared_schema.rs
+++ b/masq_lib/src/shared_schema.rs
@@ -3,7 +3,7 @@
use crate::constants::{
DEFAULT_GAS_PRICE, DEFAULT_UI_PORT, DEV_CHAIN_FULL_IDENTIFIER, ETH_MAINNET_FULL_IDENTIFIER,
ETH_ROPSTEN_FULL_IDENTIFIER, HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT,
- POLYGON_MAINNET_FULL_IDENTIFIER, POLYGON_MUMBAI_FULL_IDENTIFIER,
+ POLYGON_AMOY_FULL_IDENTIFIER, POLYGON_MAINNET_FULL_IDENTIFIER,
};
use crate::crash_point::CrashPoint;
use clap::{App, Arg};
@@ -13,6 +13,7 @@ pub const BLOCKCHAIN_SERVICE_HELP: &str =
"The Ethereum client you wish to use to provide Blockchain \
exit services from your MASQ Node (e.g. http://localhost:8545, \
https://ropsten.infura.io/v3/YOUR-PROJECT-ID, https://mainnet.infura.io/v3/YOUR-PROJECT-ID), \
+ https://base-mainnet.g.alchemy.com/v2/d66UL0lPrltmweEqVsv3opBSVI3wkL8I, \
https://polygon-mainnet.infura.io/v3/YOUR-PROJECT-ID";
pub const CHAIN_HELP: &str =
"The blockchain network MASQ Node will configure itself to use. You must ensure the \
@@ -64,8 +65,9 @@ pub const NEIGHBORS_HELP: &str = "One or more Node descriptors for running Nodes
on startup. A Node descriptor looks similar to one of these:\n\n\
masq://polygon-mainnet:d2U3Dv1BqtS5t_Zz3mt9_sCl7AgxUlnkB4jOMElylrU@172.50.48.6:9342\n\
masq://eth-mainnet:gBviQbjOS3e5ReFQCvIhUM3i02d1zPleo1iXg_EN6zQ@86.75.30.9:5542\n\
- masq://polygon-mumbai:A6PGHT3rRjaeFpD_rFi3qGEXAVPq7bJDfEUZpZaIyq8@14.10.50.6:10504\n\
- masq://eth-ropsten:OHsC2CAm4rmfCkaFfiynwxflUgVTJRb2oY5mWxNCQkY@150.60.42.72:6642/4789/5254\n\n\
+ masq://base-mainnet:ZjPLnb9RrgsRM1D9edqH8jx9DkbPZSWqqFqLnmdKhsk@112.55.78.0:7878\n\
+ masq://polygon-amoy:A6PGHT3rRjaeFpD_rFi3qGEXAVPq7bJDfEUZpZaIyq8@14.10.50.6:10504\n\
+ masq://base-sepolia:OHsC2CAm4rmfCkaFfiynwxflUgVTJRb2oY5mWxNCQkY@150.60.42.72:6642/4789/5254\n\n\
Notice each of the different chain identifiers in the masq protocol prefix - they determine a family of chains \
and also the network the descriptor belongs to (mainnet or a testnet). See also the last descriptor which shows \
a configuration with multiple clandestine ports.\n\n\
@@ -256,7 +258,7 @@ pub fn official_chain_names() -> &'static [&'static str] {
&[
POLYGON_MAINNET_FULL_IDENTIFIER,
ETH_MAINNET_FULL_IDENTIFIER,
- POLYGON_MUMBAI_FULL_IDENTIFIER,
+ POLYGON_AMOY_FULL_IDENTIFIER,
ETH_ROPSTEN_FULL_IDENTIFIER,
DEV_CHAIN_FULL_IDENTIFIER,
]
@@ -388,7 +390,6 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> {
.case_insensitive(true)
.hidden(true),
)
- .arg(data_directory_arg(DATA_DIRECTORY_HELP))
.arg(db_password_arg(DB_PASSWORD_HELP))
.arg(
Arg::with_name("dns-servers")
@@ -484,6 +485,7 @@ pub fn shared_app(head: App<'static, 'static>) -> App<'static, 'static> {
pub mod common_validators {
use crate::constants::LOWEST_USABLE_INSECURE_PORT;
+ use ip_country_lib::countries::INDEX_BY_ISO3166;
use regex::Regex;
use std::net::IpAddr;
use std::str::FromStr;
@@ -520,6 +522,44 @@ pub mod common_validators {
}
}
+ pub fn validate_country_code(country_code: &str) -> Result<(), String> {
+ match INDEX_BY_ISO3166.contains_key(country_code) {
+ true => Ok(()),
+ false => Err(format!(
+ "'{}' is not a valid ISO3166 country code",
+ country_code
+ )),
+ }
+ }
+
+ pub fn validate_exit_locations(exit_location: String) -> Result<(), String> {
+ validate_pipe_separated_values(exit_location, |country: String| {
+ let mut collect_fails = "".to_string();
+ country.split(',').into_iter().for_each(|country_code| {
+ match validate_country_code(country_code) {
+ Ok(_) => (),
+ Err(e) => collect_fails.push_str(&e),
+ }
+ });
+ match collect_fails.is_empty() {
+ true => Ok(()),
+ false => Err(collect_fails),
+ }
+ })
+ }
+
+ pub fn validate_separate_u64_values(values: String) -> Result<(), String> {
+ validate_pipe_separated_values(values, |segment: String| {
+ segment
+ .parse::()
+ .map_err(|_| {
+ "Supply nonnegative numeric values separated by vertical bars like 111|222|333|..."
+ .to_string()
+ })
+ .map(|_| ())
+ })
+ }
+
pub fn validate_private_key(key: String) -> Result<(), String> {
if Regex::new("^[0-9a-fA-F]{64}$")
.expect("Failed to compile regular expression")
@@ -606,16 +646,24 @@ pub mod common_validators {
}
}
- pub fn validate_separate_u64_values(values_with_delimiters: String) -> Result<(), String> {
- values_with_delimiters.split('|').try_for_each(|segment| {
- segment
- .parse::()
- .map_err(|_| {
- "Supply positive numeric values separated by vertical bars like 111|222|333|..."
- .to_string()
- })
- .map(|_| ())
- })
+ fn validate_pipe_separated_values(
+ values_with_delimiters: String,
+ closure: 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()) {
+ Ok(_) => (),
+ Err(msg) => error_collection.push(msg),
+ };
+ });
+ match error_collection.is_empty() {
+ true => Ok(()),
+ false => Err(error_collection.into_iter().collect::()),
+ }
}
}
@@ -683,6 +731,7 @@ mod tests {
"The Ethereum client you wish to use to provide Blockchain \
exit services from your MASQ Node (e.g. http://localhost:8545, \
https://ropsten.infura.io/v3/YOUR-PROJECT-ID, https://mainnet.infura.io/v3/YOUR-PROJECT-ID), \
+ https://base-mainnet.g.alchemy.com/v2/d66UL0lPrltmweEqVsv3opBSVI3wkL8I, \
https://polygon-mainnet.infura.io/v3/YOUR-PROJECT-ID"
);
assert_eq!(
@@ -757,8 +806,9 @@ mod tests {
on startup. A Node descriptor looks similar to one of these:\n\n\
masq://polygon-mainnet:d2U3Dv1BqtS5t_Zz3mt9_sCl7AgxUlnkB4jOMElylrU@172.50.48.6:9342\n\
masq://eth-mainnet:gBviQbjOS3e5ReFQCvIhUM3i02d1zPleo1iXg_EN6zQ@86.75.30.9:5542\n\
- masq://polygon-mumbai:A6PGHT3rRjaeFpD_rFi3qGEXAVPq7bJDfEUZpZaIyq8@14.10.50.6:10504\n\
- masq://eth-ropsten:OHsC2CAm4rmfCkaFfiynwxflUgVTJRb2oY5mWxNCQkY@150.60.42.72:6642/4789/5254\n\n\
+ masq://base-mainnet:ZjPLnb9RrgsRM1D9edqH8jx9DkbPZSWqqFqLnmdKhsk@112.55.78.0:7878\n\
+ masq://polygon-amoy:A6PGHT3rRjaeFpD_rFi3qGEXAVPq7bJDfEUZpZaIyq8@14.10.50.6:10504\n\
+ masq://base-sepolia:OHsC2CAm4rmfCkaFfiynwxflUgVTJRb2oY5mWxNCQkY@150.60.42.72:6642/4789/5254\n\n\
Notice each of the different chain identifiers in the masq protocol prefix - they determine a family of chains \
and also the network the descriptor belongs to (mainnet or a testnet). See also the last descriptor which shows \
a configuration with multiple clandestine ports.\n\n\
@@ -925,6 +975,23 @@ 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"));
+
+ assert_eq!(
+ result,
+ Err("'RR' is not a valid ISO3166 country code".to_string())
+ );
+ }
+
+ #[test]
+ fn validate_exit_key_success() {
+ let result = common_validators::validate_exit_locations(String::from("CZ|SK"));
+
+ assert_eq!(result, Ok(()));
+ }
+
#[test]
fn validate_private_key_requires_a_key_that_is_64_characters_long() {
let result = common_validators::validate_private_key(String::from("42"));
@@ -1075,7 +1142,7 @@ mod tests {
assert_eq!(
result,
Err(String::from(
- "Supply positive numeric values separated by vertical bars like 111|222|333|..."
+ "Supply nonnegative numeric values separated by vertical bars like 111|222|333|..."
))
)
}
@@ -1087,7 +1154,7 @@ mod tests {
assert_eq!(
result,
Err(String::from(
- "Supply positive numeric values separated by vertical bars like 111|222|333|..."
+ "Supply nonnegative numeric values separated by vertical bars like 111|222|333|..."
))
)
}
@@ -1099,7 +1166,7 @@ mod tests {
assert_eq!(
result,
Err(String::from(
- "Supply positive numeric values separated by vertical bars like 111|222|333|..."
+ "Supply nonnegative numeric values separated by vertical bars like 111|222|333|..."
))
)
}
@@ -1144,7 +1211,7 @@ mod tests {
let mut iterator = official_chain_names().iter();
assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::PolyMainnet);
assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::EthMainnet);
- assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::PolyMumbai);
+ assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::PolyAmoy);
assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::EthRopsten);
assert_eq!(Chain::from(*iterator.next().unwrap()), Chain::Dev);
assert_eq!(iterator.next(), None)
diff --git a/masq_lib/src/test_utils/fake_stream_holder.rs b/masq_lib/src/test_utils/fake_stream_holder.rs
index d971ffa7a..d57ed3c3c 100644
--- a/masq_lib/src/test_utils/fake_stream_holder.rs
+++ b/masq_lib/src/test_utils/fake_stream_holder.rs
@@ -1,136 +1,7 @@
// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
use crate::command::StdStreams;
-use std::cmp::min;
-use std::io;
-use std::io::Read;
-use std::io::Write;
-use std::io::{BufRead, Error};
-use std::sync::{Arc, Mutex};
-
-pub struct ByteArrayWriter {
- inner_arc: Arc>,
-}
-
-pub struct ByteArrayWriterInner {
- byte_array: Vec,
- next_error: Option,
-}
-
-impl ByteArrayWriterInner {
- pub fn get_bytes(&self) -> Vec {
- self.byte_array.clone()
- }
- pub fn get_string(&self) -> String {
- String::from_utf8(self.get_bytes()).unwrap()
- }
-}
-
-impl Default for ByteArrayWriter {
- fn default() -> Self {
- ByteArrayWriter {
- inner_arc: Arc::new(Mutex::new(ByteArrayWriterInner {
- byte_array: vec![],
- next_error: None,
- })),
- }
- }
-}
-
-impl ByteArrayWriter {
- pub fn new() -> ByteArrayWriter {
- Self::default()
- }
-
- pub fn inner_arc(&self) -> Arc> {
- self.inner_arc.clone()
- }
-
- pub fn get_bytes(&self) -> Vec {
- self.inner_arc.lock().unwrap().byte_array.clone()
- }
- pub fn get_string(&self) -> String {
- String::from_utf8(self.get_bytes()).unwrap()
- }
-
- pub fn reject_next_write(&mut self, error: Error) {
- self.inner_arc().lock().unwrap().next_error = Some(error);
- }
-}
-
-impl Write for ByteArrayWriter {
- fn write(&mut self, buf: &[u8]) -> io::Result {
- let mut inner = self.inner_arc.lock().unwrap();
- if let Some(next_error) = inner.next_error.take() {
- Err(next_error)
- } else {
- for byte in buf {
- inner.byte_array.push(*byte)
- }
- Ok(buf.len())
- }
- }
-
- fn flush(&mut self) -> io::Result<()> {
- Ok(())
- }
-}
-
-pub struct ByteArrayReader {
- byte_array: Vec,
- position: usize,
- next_error: Option,
-}
-
-impl ByteArrayReader {
- pub fn new(byte_array: &[u8]) -> ByteArrayReader {
- ByteArrayReader {
- byte_array: byte_array.to_vec(),
- position: 0,
- next_error: None,
- }
- }
-
- pub fn reject_next_read(mut self, error: Error) -> ByteArrayReader {
- self.next_error = Some(error);
- self
- }
-}
-
-impl Read for ByteArrayReader {
- fn read(&mut self, buf: &mut [u8]) -> io::Result {
- match self.next_error.take() {
- Some(error) => Err(error),
- None => {
- let to_copy = min(buf.len(), self.byte_array.len() - self.position);
- #[allow(clippy::needless_range_loop)]
- for idx in 0..to_copy {
- buf[idx] = self.byte_array[self.position + idx]
- }
- self.position += to_copy;
- Ok(to_copy)
- }
- }
- }
-}
-
-impl BufRead for ByteArrayReader {
- fn fill_buf(&mut self) -> io::Result<&[u8]> {
- match self.next_error.take() {
- Some(error) => Err(error),
- None => Ok(&self.byte_array[self.position..]),
- }
- }
-
- fn consume(&mut self, amt: usize) {
- let result = self.position + amt;
- self.position = if result < self.byte_array.len() {
- result
- } else {
- self.byte_array.len()
- }
- }
-}
+use test_utilities::byte_array_reader_writer::{ByteArrayReader, ByteArrayWriter};
pub struct FakeStreamHolder {
pub stdin: ByteArrayReader,
diff --git a/masq_lib/src/test_utils/logging.rs b/masq_lib/src/test_utils/logging.rs
index ec60ea72f..92566a753 100644
--- a/masq_lib/src/test_utils/logging.rs
+++ b/masq_lib/src/test_utils/logging.rs
@@ -1,6 +1,5 @@
// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
use crate::logger::real_format_function;
-use crate::test_utils::fake_stream_holder::ByteArrayWriter;
use crate::test_utils::utils::to_millis;
use lazy_static::lazy_static;
use log::set_logger;
@@ -13,6 +12,7 @@ use std::sync::{Arc, Mutex, MutexGuard};
use std::thread;
use std::time::Duration;
use std::time::Instant;
+use test_utilities::byte_array_reader_writer::ByteArrayWriter;
use time::OffsetDateTime;
lazy_static! {
diff --git a/masq_lib/src/utils.rs b/masq_lib/src/utils.rs
index 1083af2b9..8d563ef37 100644
--- a/masq_lib/src/utils.rs
+++ b/masq_lib/src/utils.rs
@@ -49,14 +49,14 @@ fn compute_data_directory_help() -> String {
let polygon_mainnet_dir = Path::new(&data_dir.to_str().unwrap())
.join("MASQ")
.join("polygon-mainnet");
- let polygon_mumbai_dir = Path::new(&data_dir.to_str().unwrap())
+ let polygon_amoy_dir = Path::new(&data_dir.to_str().unwrap())
.join("MASQ")
- .join("polygon-mumbai");
+ .join("polygon-amoy");
format!("Directory in which the Node will store its persistent state, including at least its database \
and by default its configuration file as well. By default, your data-directory is located in \
your application directory, under your home directory e.g.: '{}'.\n\n\
In case you change your chain to a different one, the data-directory path is automatically changed \
- to end with the name of your chain: e.g.: if you choose polygon-mumbai, then data-directory is \
+ to end with the name of your chain: e.g.: if you choose polygon-amoy, then data-directory is \
automatically changed to: '{}'.\n\n\
You can specify your own data-directory to the Daemon in two different ways: \n\n\
1. If you provide a path without the chain name on the end, the Daemon will automatically change \
@@ -65,7 +65,7 @@ fn compute_data_directory_help() -> String {
2. If you provide your data directory with the corresponding chain name on the end, eg: {}/masq_home/polygon-mainnet, \
there will be no change until you set the chain parameter to a different value.",
polygon_mainnet_dir.to_string_lossy().to_string().as_str(),
- polygon_mumbai_dir.to_string_lossy().to_string().as_str(),
+ polygon_amoy_dir.to_string_lossy().to_string().as_str(),
&home_dir.to_string_lossy().to_string().as_str(),
&home_dir.to_string_lossy().to_string().as_str(),
home_dir.to_string_lossy().to_string().as_str()
diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs
index 86a94af54..67e7c372f 100644
--- a/multinode_integration_tests/src/masq_node_cluster.rs
+++ b/multinode_integration_tests/src/masq_node_cluster.rs
@@ -337,6 +337,25 @@ impl MASQNodeCluster {
)),
}
}
+
+ fn create_world_network(ipv4addr: Ipv4Addr, name: &str) -> Result<(), String> {
+ let mut command = Command::new(
+ "docker",
+ Command::strings(vec![
+ "network",
+ "create",
+ format!("--subnet={}/16", ipv4addr).as_str(),
+ name,
+ ]),
+ );
+ match command.wait_for_exit() {
+ 0 => Ok(()),
+ _ => Err(format!(
+ "Could not create network integration_net: {}",
+ command.stderr_as_string()
+ )),
+ }
+ }
}
pub struct DockerHostSocketAddr {
diff --git a/multinode_integration_tests/src/neighborhood_constructor.rs b/multinode_integration_tests/src/neighborhood_constructor.rs
index ba3fb650b..53a9d611e 100644
--- a/multinode_integration_tests/src/neighborhood_constructor.rs
+++ b/multinode_integration_tests/src/neighborhood_constructor.rs
@@ -259,8 +259,8 @@ fn from_masq_node_to_node_record(masq_node: &dyn MASQNode) -> NodeRecord {
last_update: time_t_timestamp(),
node_addr_opt: agr.node_addr_opt.clone(),
unreachable_hosts: Default::default(),
- node_distrust_score: 0,
node_location_opt: None,
+ country_undesirability: 0u32,
},
signed_gossip: agr.signed_gossip.clone(),
signature: agr.signature,
diff --git a/multinode_integration_tests/src/utils.rs b/multinode_integration_tests/src/utils.rs
index 6c4868e89..5c522b364 100644
--- a/multinode_integration_tests/src/utils.rs
+++ b/multinode_integration_tests/src/utils.rs
@@ -3,7 +3,6 @@
use crate::command::Command;
use crate::masq_node::{MASQNode, MASQNodeUtils};
use crate::masq_real_node::MASQRealNode;
-use ip_country_lib::country_finder::COUNTRY_CODE_FINDER;
use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN;
use masq_lib::utils::NeighborhoodModeLight;
use node_lib::accountant::db_access_objects::payable_dao::{PayableDao, PayableDaoReal};
@@ -147,7 +146,7 @@ impl From<&dyn MASQNode> for AccessibleGossipRecord {
signature: CryptData::new(b""),
};
let ip_addr = masq_node.node_addr().ip_addr();
- let country_code = get_node_location(Some(ip_addr), &COUNTRY_CODE_FINDER);
+ let country_code = get_node_location(Some(ip_addr));
if let Some(cc) = country_code {
agr.inner.country_code_opt = Some(cc.country_code)
};
diff --git a/multinode_integration_tests/tests/data_routing_test.rs b/multinode_integration_tests/tests/data_routing_test.rs
index 0b3fa9d21..0c4cef279 100644
--- a/multinode_integration_tests/tests/data_routing_test.rs
+++ b/multinode_integration_tests/tests/data_routing_test.rs
@@ -316,7 +316,7 @@ fn multiple_stream_zero_hop_test() {
let mut another_client = zero_hop_node.make_client(8080, STANDARD_CLIENT_TIMEOUT_MILLIS);
one_client.send_chunk(b"GET / HTTP/1.1\r\nHost: www.example.com\r\n\r\n");
- another_client.send_chunk(b"GET /online/ HTTP/1.1\r\nHost: whatever.neverssl.com\r\n\r\n");
+ another_client.send_chunk(b"GET /online/ HTTP/1.1\r\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\r\nAccept-Language: cs-CZ,cs;q=0.9,en;q=0.8,sk;q=0.7\r\nCache-Control: max-age=0\r\nConnection: keep-alive\r\nHost: whatever.neverssl.com\r\nUpgrade-Insecure-Requests: 1\r\nUser-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36\r\n\r\n");
let one_response = one_client.wait_for_chunk();
let another_response = another_client.wait_for_chunk();
diff --git a/node/Cargo.lock b/node/Cargo.lock
index 5d4b6605d..fabda2b8a 100644
--- a/node/Cargo.lock
+++ b/node/Cargo.lock
@@ -1565,7 +1565,7 @@ dependencies = [
"csv",
"itertools 0.13.0",
"lazy_static",
- "masq_lib",
+ "test_utilities",
]
[[package]]
@@ -1855,6 +1855,7 @@ dependencies = [
"nix 0.23.1",
"num",
"regex",
+ "test_utilities",
"thousands",
"time 0.3.11",
"websocket",
@@ -1870,6 +1871,7 @@ dependencies = [
"crossbeam-channel 0.5.1",
"dirs 4.0.0",
"ethereum-types",
+ "ip_country",
"itertools 0.10.3",
"lazy_static",
"log 0.4.18",
@@ -1878,6 +1880,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
+ "test_utilities",
"time 0.3.11",
"tiny-hderive",
"toml",
@@ -2188,6 +2191,7 @@ dependencies = [
"sodiumoxide",
"sysinfo",
"system-configuration",
+ "test_utilities",
"thousands",
"time 0.3.11",
"tiny-bip39",
@@ -3654,6 +3658,10 @@ dependencies = [
"phf_codegen",
]
+[[package]]
+name = "test_utilities"
+version = "0.1.0"
+
[[package]]
name = "textwrap"
version = "0.11.0"
diff --git a/node/Cargo.toml b/node/Cargo.toml
index 345daa234..46f12ef88 100644
--- a/node/Cargo.toml
+++ b/node/Cargo.toml
@@ -86,6 +86,7 @@ native-tls = {version = "0.2.8", features = ["vendored"]}
simple-server = "0.4.0"
serial_test_derive = "0.5.1"
serial_test = "0.5.1"
+test_utilities = { path = "../test_utilities"}
trust-dns-proto = "0.8.0"
[[bin]]
diff --git a/node/src/actor_system_factory.rs b/node/src/actor_system_factory.rs
index 0b175a797..32b284237 100644
--- a/node/src/actor_system_factory.rs
+++ b/node/src/actor_system_factory.rs
@@ -1179,7 +1179,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
payment_thresholds_opt: Some(PaymentThresholds::default()),
when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC,
@@ -1255,7 +1254,7 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
+
},
payment_thresholds_opt: Default::default(),
when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC
@@ -1401,7 +1400,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let make_params_arc = Arc::new(Mutex::new(vec![]));
let mut subject = make_subject_with_null_setter();
@@ -1556,7 +1554,7 @@ mod tests {
neighborhood_config: NeighborhoodConfig {
mode: NeighborhoodMode::ConsumeOnly(vec![]),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
+
},
payment_thresholds_opt: Default::default(),
when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC
@@ -1746,7 +1744,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
node_descriptor: Default::default(),
payment_thresholds_opt: Default::default(),
diff --git a/node/src/apps.rs b/node/src/apps.rs
index 9aed6369b..2a38c7e47 100644
--- a/node/src/apps.rs
+++ b/node/src/apps.rs
@@ -6,7 +6,7 @@ use lazy_static::lazy_static;
use masq_lib::constants::{HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT};
use masq_lib::shared_schema::{
chain_arg, data_directory_arg, db_password_arg, real_user_arg, shared_app, ui_port_arg,
- DB_PASSWORD_HELP,
+ DATA_DIRECTORY_HELP, DB_PASSWORD_HELP,
};
use masq_lib::utils::DATA_DIRECTORY_DAEMON_HELP;
@@ -36,7 +36,9 @@ pub fn app_daemon() -> App<'static, 'static> {
}
pub fn app_node() -> App<'static, 'static> {
- shared_app(app_head().after_help(NODE_HELP_TEXT)).arg(ui_port_arg(&DAEMON_UI_PORT_HELP))
+ shared_app(app_head().after_help(NODE_HELP_TEXT))
+ .arg(data_directory_arg(DATA_DIRECTORY_HELP))
+ .arg(ui_port_arg(&DAEMON_UI_PORT_HELP))
}
pub fn app_config_dumper() -> App<'static, 'static> {
@@ -107,9 +109,9 @@ mod tests {
let polygon_mainnet_dir = Path::new(&data_dir.to_str().unwrap())
.join("MASQ")
.join("polygon-mainnet");
- let polygon_mumbai_dir = Path::new(&data_dir.to_str().unwrap())
+ let polygon_amoy_dir = Path::new(&data_dir.to_str().unwrap())
.join("MASQ")
- .join("polygon-mumbai");
+ .join("polygon-amoy");
assert_eq!(
DATA_DIRECTORY_DAEMON_HELP.as_str(),
@@ -117,7 +119,7 @@ mod tests {
and by default its configuration file as well. By default, your data-directory is located in \
your application directory, under your home directory e.g.: '{}'.\n\n\
In case you change your chain to a different one, the data-directory path is automatically changed \
- to end with the name of your chain: e.g.: if you choose polygon-mumbai, then data-directory is \
+ to end with the name of your chain: e.g.: if you choose polygon-amoy, then data-directory is \
automatically changed to: '{}'.\n\n\
You can specify your own data-directory to the Daemon in two different ways: \n\n\
1. If you provide a path without the chain name on the end, the Daemon will automatically change \
@@ -126,7 +128,7 @@ mod tests {
2. If you provide your data directory with the corresponding chain name on the end, eg: {}/masq_home/polygon-mainnet, \
there will be no change until you set the chain parameter to a different value.",
polygon_mainnet_dir.to_string_lossy().to_string().as_str(),
- polygon_mumbai_dir.to_string_lossy().to_string().as_str(),
+ polygon_amoy_dir.to_string_lossy().to_string().as_str(),
&home_dir.to_string_lossy().to_string().as_str(),
&home_dir.to_string_lossy().to_string().as_str(),
home_dir.to_string_lossy().to_string().as_str()
diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs
index 93e4d9bb1..1f6e4d4be 100644
--- a/node/src/blockchain/blockchain_bridge.rs
+++ b/node/src/blockchain/blockchain_bridge.rs
@@ -1828,7 +1828,7 @@ mod tests {
assert_eq!(Some(100000u64), max_block_count);
}
/*
- POKT (Polygon mainnet and mumbai)
+ POKT (Polygon mainnet and amoy)
{"jsonrpc":"2.0","id":7,"error":{"message":"You cannot query logs for more than 100000 blocks at once.","code":-32064}}
*/
/*
diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs
index 083f75822..c3f41f054 100644
--- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs
+++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs
@@ -579,7 +579,7 @@ where
fn web3_gas_limit_const_part(chain: Chain) -> u64 {
match chain {
Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => 55_000,
- Chain::PolyMainnet | Chain::PolyMumbai => 70_000,
+ Chain::PolyMainnet | Chain::PolyAmoy => 70_000,
}
}
@@ -1107,7 +1107,7 @@ mod tests {
#[test]
fn build_of_the_blockchain_agent_fails_on_fetching_gas_price() {
- let chain = Chain::PolyMumbai;
+ let chain = Chain::PolyAmoy;
let wallet = make_wallet("abc");
let persistent_config = PersistentConfigurationMock::new().gas_price_result(Err(
PersistentConfigError::UninterpretableValue("booga".to_string()),
@@ -1613,10 +1613,7 @@ mod tests {
Subject::web3_gas_limit_const_part(Chain::PolyMainnet),
70_000
);
- assert_eq!(
- Subject::web3_gas_limit_const_part(Chain::PolyMumbai),
- 70_000
- );
+ assert_eq!(Subject::web3_gas_limit_const_part(Chain::PolyAmoy), 70_000);
assert_eq!(Subject::web3_gas_limit_const_part(Chain::Dev), 55_000);
}
@@ -1683,7 +1680,7 @@ mod tests {
#[test]
fn signing_error_terminates_iteration_over_accounts_and_propagates_it_all_way_up_and_out() {
let transport = TestTransport::default();
- let chain = Chain::PolyMumbai;
+ let chain = Chain::PolyAmoy;
let batch_payable_tools = BatchPayableToolsMock::::default()
.sign_transaction_result(Err(Web3Error::Signing(
secp256k1secrets::Error::InvalidSecretKey,
@@ -1749,7 +1746,7 @@ mod tests {
.batch_wide_timestamp_result(SystemTime::now())
.submit_batch_result(Err(Web3Error::Transport("Transaction crashed".to_string())));
let consuming_wallet_secret_raw_bytes = b"okay-wallet";
- let chain = Chain::PolyMumbai;
+ let chain = Chain::PolyAmoy;
let mut subject =
BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain);
subject.batch_payable_tools = Box::new(batch_payable_tools);
@@ -1781,7 +1778,7 @@ mod tests {
secp256k1secrets::Error::InvalidSecretKey,
)));
let consuming_wallet_secret_raw_bytes = b"okay-wallet";
- let chain = Chain::PolyMumbai;
+ let chain = Chain::PolyAmoy;
let mut subject =
BlockchainInterfaceWeb3::new(transport, make_fake_event_loop_handle(), chain);
subject.batch_payable_tools = Box::new(batch_payable_tools);
@@ -1801,10 +1798,6 @@ mod tests {
);
}
- const TEST_PAYMENT_AMOUNT: u128 = 1_000_000_000_000;
- const TEST_GAS_PRICE_ETH: u64 = 110;
- const TEST_GAS_PRICE_POLYGON: u64 = 50;
-
fn test_consuming_wallet_with_secret() -> Wallet {
let key_pair = Bip32EncryptionKeyProvider::from_raw_secret(
&decode_hex("97923d8fd8de4a00f912bfb77ef483141dec551bd73ea59343ef5c4aac965d04")
@@ -1832,12 +1825,13 @@ mod tests {
let recipient_wallet = test_recipient_wallet();
let nonce_correct_type = U256::from(nonce);
let gas_price = match chain {
- Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => TEST_GAS_PRICE_ETH,
- Chain::PolyMainnet | Chain::PolyMumbai => TEST_GAS_PRICE_POLYGON,
+ Chain::EthMainnet | Chain::EthRopsten | Chain::Dev => 110,
+ Chain::PolyMainnet | Chain::PolyAmoy => 55,
};
+ let payment_size_wei = 1_000_000_000_000;
let payable_account = make_payable_account_with_wallet_and_balance_and_timestamp_opt(
recipient_wallet,
- TEST_PAYMENT_AMOUNT,
+ payment_size_wei,
None,
);
@@ -1855,57 +1849,63 @@ mod tests {
assert_eq!(byte_set_to_compare.as_slice(), template)
}
- //with a real confirmation through a transaction sent with this data to the network
+ // Transaction with this input was verified on the test network
#[test]
- fn web3_interface_signing_a_transaction_works_for_polygon_mumbai() {
- let chain = Chain::PolyMumbai;
- let nonce = 5;
- // signed_transaction_data changed after we changed the contract address of polygon matic
- let signed_transaction_data = "f8ad05850ba43b740083011980949b27034acabd44223fb23d628ba4849867ce1db280b844a9059cbb0000000000000000000000007788df76bbd9a0c7c3e5bf0f77bb28c60a167a7b000000000000000000000000000000000000000000000000000000e8d4a5100083027126a09fdbbd7064d3b7240f5422b2164aaa13d62f0946a683d82ee26f97f242570d90a077b49dbb408c20d73e0666ba0a77ac888bf7a9cb14824a5f35c97217b9bc0a5a";
-
+ fn web3_interface_signing_a_transaction_works_for_polygon_amoy() {
+ let chain = Chain::PolyAmoy;
+ let nonce = 4;
+ let signed_transaction_data = "\
+ f8ad04850cce4166008301198094d98c3ebd6b7f9b7cda2449ecac00d1e5f47a819380b844a9059cbb000000000\
+ 0000000000000007788df76bbd9a0c7c3e5bf0f77bb28c60a167a7b000000000000000000000000000000000000\
+ 000000000000000000e8d4a5100083027127a0ddd78a41c42b7a409c281292f7c6aedefab8b461d87371fe402b4\
+ b0804a092f2a04b1b599ac2c1ff07bb3d40d3698c454691c3b70d99f1e5d840c852e968c96a10";
let in_bytes = decode_hex(signed_transaction_data).unwrap();
assert_that_signed_transactions_agrees_with_template(chain, nonce, &in_bytes)
}
- //with a real confirmation through a transaction sent with this data to the network
+ // Transaction with this input was verified on the test network
#[test]
fn web3_interface_signing_a_transaction_works_for_eth_ropsten() {
let chain = Chain::EthRopsten;
- let nonce = 1; //must stay like this!
- let signed_transaction_data = "f8a90185199c82cc0082dee894384dec25e03f94931767ce4c3556168468ba24c380b844a9059cbb0000000000000000000000007788df76bbd9a0c7c3e5bf0f77bb28c60a167a7b000000000000000000000000000000000000000000000000000000e8d4a510002aa0635fbb3652e1c3063afac6ffdf47220e0431825015aef7daff9251694e449bfca00b2ed6d556bd030ac75291bf58817da15a891cd027a4c261bb80b51f33b78adf";
+ let nonce = 1;
+ let signed_transaction_data = "\
+ f8a90185199c82cc0082dee894384dec25e03f94931767ce4c3556168468ba24c380b844a9059cbb00000000000\
+ 00000000000007788df76bbd9a0c7c3e5bf0f77bb28c60a167a7b00000000000000000000000000000000000000\
+ 0000000000000000e8d4a510002aa0635fbb3652e1c3063afac6ffdf47220e0431825015aef7daff9251694e449\
+ bfca00b2ed6d556bd030ac75291bf58817da15a891cd027a4c261bb80b51f33b78adf";
let in_bytes = decode_hex(signed_transaction_data).unwrap();
assert_that_signed_transactions_agrees_with_template(chain, nonce, &in_bytes)
}
- //not confirmed on the real network
+ // Unconfirmed on the real network
#[test]
fn web3_interface_signing_a_transaction_for_polygon_mainnet() {
let chain = Chain::PolyMainnet;
let nonce = 10;
- //generated locally
+ // Generated locally
let signed_transaction_data = [
- 248, 172, 10, 133, 11, 164, 59, 116, 0, 131, 1, 25, 128, 148, 238, 154, 53, 47, 106,
+ 248, 172, 10, 133, 12, 206, 65, 102, 0, 131, 1, 25, 128, 148, 238, 154, 53, 47, 106,
172, 74, 241, 165, 185, 244, 103, 246, 169, 62, 15, 251, 233, 221, 53, 128, 184, 68,
169, 5, 156, 187, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 119, 136, 223, 118, 187, 217,
160, 199, 195, 229, 191, 15, 119, 187, 40, 198, 10, 22, 122, 123, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 232, 212, 165, 16, 0, 130,
- 1, 53, 160, 7, 203, 40, 44, 202, 233, 15, 5, 64, 218, 199, 239, 94, 126, 152, 2, 108,
- 30, 157, 75, 124, 129, 117, 27, 109, 163, 132, 27, 11, 123, 137, 10, 160, 18, 170, 130,
- 198, 73, 190, 158, 235, 0, 77, 118, 213, 244, 229, 225, 143, 156, 214, 219, 204, 193,
- 155, 199, 164, 162, 31, 134, 51, 139, 130, 152, 104,
+ 1, 53, 160, 200, 159, 77, 202, 128, 195, 67, 122, 35, 204, 26, 65, 171, 89, 253, 82, 6,
+ 176, 192, 225, 41, 61, 151, 82, 66, 232, 72, 44, 68, 131, 140, 117, 160, 117, 66, 154,
+ 132, 183, 97, 219, 131, 214, 72, 220, 66, 152, 72, 15, 107, 44, 237, 193, 16, 193, 52,
+ 6, 94, 216, 149, 94, 102, 199, 80, 68, 105,
];
assert_that_signed_transactions_agrees_with_template(chain, nonce, &signed_transaction_data)
}
- //not confirmed on the real network
+ // Unconfirmed on the real network
#[test]
fn web3_interface_signing_a_transaction_for_eth_mainnet() {
let chain = Chain::EthMainnet;
let nonce = 10;
- //generated locally
+ // Generated locally
let signed_transaction_data = [
248, 169, 10, 133, 25, 156, 130, 204, 0, 130, 222, 232, 148, 6, 243, 195, 35, 240, 35,
140, 114, 191, 53, 1, 16, 113, 242, 181, 183, 244, 58, 5, 76, 128, 184, 68, 169, 5,
@@ -1921,8 +1921,8 @@ mod tests {
assert_that_signed_transactions_agrees_with_template(chain, nonce, &signed_transaction_data)
}
- //an adapted test from old times when we had our own signing method
- //I don't have data for the new chains so I omit them in this kind of tests
+ // Adapted test from old times when we had our own signing method.
+ // Don't have data for new chains, so I omit them in this kind of tests
#[test]
fn signs_various_transactions_for_eth_mainnet() {
let signatures = &[
@@ -1955,8 +1955,8 @@ mod tests {
assert_signature(Chain::EthMainnet, signatures)
}
- //an adapted test from old times when we had our own signing method
- //I don't have data for the new chains so I omit them in this kind of tests
+ // Adapted test from old times when we had our own signing method.
+ // Don't have data for new chains, so I omit them in this kind of tests
#[test]
fn signs_various_transactions_for_ropsten() {
let signatures = &[
diff --git a/node/src/blockchain/test_utils.rs b/node/src/blockchain/test_utils.rs
index 4a8f4ecd6..16f7d21da 100644
--- a/node/src/blockchain/test_utils.rs
+++ b/node/src/blockchain/test_utils.rs
@@ -331,7 +331,7 @@ pub fn all_chains() -> [Chain; 4] {
[
Chain::EthMainnet,
Chain::PolyMainnet,
- Chain::PolyMumbai,
+ Chain::PolyAmoy,
Chain::Dev,
]
}
diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs
index f1ece6d54..fccdf83c4 100644
--- a/node/src/bootstrapper.rs
+++ b/node/src/bootstrapper.rs
@@ -400,7 +400,6 @@ impl BootstrapperConfig {
neighborhood_config: NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: DEFAULT_MIN_HOPS,
- country: "ZZ".to_string(),
},
when_pending_too_long_sec: DEFAULT_PENDING_TOO_LONG_SEC,
}
@@ -1156,7 +1155,7 @@ mod tests {
"--real-user",
"123:456:/home/booga",
"--chain",
- "polygon-mumbai",
+ "polygon-amoy",
]))
.unwrap();
@@ -1240,7 +1239,6 @@ mod tests {
let neighborhood_config = NeighborhoodConfig {
mode: NeighborhoodMode::OriginateOnly(vec![], rate_pack(9)),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let earning_wallet = make_wallet("earning wallet");
let consuming_wallet_opt = Some(make_wallet("consuming wallet"));
@@ -1861,7 +1859,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
config.data_directory = data_dir.clone();
config.clandestine_port_opt = Some(port);
@@ -1932,7 +1929,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
config.data_directory = data_dir.clone();
config.clandestine_port_opt = None;
@@ -1982,7 +1978,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let listener_handler = ListenerHandlerNull::new(vec![]);
let mut subject = BootstrapperBuilder::new()
@@ -2020,7 +2015,6 @@ mod tests {
cryptde,
))]),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let listener_handler = ListenerHandlerNull::new(vec![]);
let mut subject = BootstrapperBuilder::new()
@@ -2051,7 +2045,6 @@ mod tests {
config.neighborhood_config = NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let listener_handler = ListenerHandlerNull::new(vec![]);
let mut subject = BootstrapperBuilder::new()
@@ -2083,7 +2076,6 @@ mod tests {
config.neighborhood_config = NeighborhoodConfig {
mode: NeighborhoodMode::Standard(NodeAddr::default(), vec![], DEFAULT_RATE_PACK),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let mut subject = BootstrapperBuilder::new().config(config).build();
subject.set_up_clandestine_port();
diff --git a/node/src/daemon/mod.rs b/node/src/daemon/mod.rs
index 78b8491e9..f16bcbc7f 100644
--- a/node/src/daemon/mod.rs
+++ b/node/src/daemon/mod.rs
@@ -161,7 +161,7 @@ impl Daemon {
node_process_id: None,
node_ui_port: None,
verifier_tools: Box::new(VerifierToolsReal::new()),
- setup_reporter: Box::new(SetupReporterReal::new(Box::new(DirsWrapperReal {}))),
+ setup_reporter: Box::new(SetupReporterReal::new(Box::new(DirsWrapperReal::default()))),
logger: Logger::new("Daemon"),
}
}
diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs
index d882ee773..a8da581d5 100644
--- a/node/src/daemon/setup_reporter.rs
+++ b/node/src/daemon/setup_reporter.rs
@@ -26,7 +26,7 @@ use crate::sub_lib::neighborhood::NodeDescriptor;
use crate::sub_lib::neighborhood::{NeighborhoodMode as NeighborhoodModeEnum, DEFAULT_RATE_PACK};
use crate::sub_lib::utils::make_new_multi_config;
use crate::test_utils::main_cryptde;
-use clap::value_t;
+use clap::{value_t, App};
use itertools::Itertools;
use masq_lib::blockchains::chains::Chain as BlockChain;
use masq_lib::constants::DEFAULT_CHAIN;
@@ -36,8 +36,10 @@ use masq_lib::messages::{UiSetupRequestValue, UiSetupResponseValue, UiSetupRespo
use masq_lib::multi_config::{
CommandLineVcl, ConfigFileVcl, EnvironmentVcl, MultiConfig, VirtualCommandLine,
};
-use masq_lib::shared_schema::{shared_app, ConfiguratorError};
-use masq_lib::utils::{add_chain_specific_directory, to_string, ExpectValue};
+use masq_lib::shared_schema::{data_directory_arg, shared_app, ConfiguratorError};
+use masq_lib::utils::{
+ add_chain_specific_directory, to_string, ExpectValue, DATA_DIRECTORY_DAEMON_HELP,
+};
use std::collections::HashMap;
use std::fmt::Display;
use std::net::{IpAddr, Ipv4Addr};
@@ -67,6 +69,10 @@ pub fn setup_cluster_from(input: Vec<(&str, &str, UiSetupResponseValueStatus)>)
.collect::()
}
+fn daemon_shared_app() -> App<'static, 'static> {
+ shared_app(app_head()).arg(data_directory_arg(DATA_DIRECTORY_DAEMON_HELP.as_str()))
+}
+
pub trait SetupReporter {
fn get_modified_setup(
&self,
@@ -222,7 +228,7 @@ impl SetupReporterReal {
}
pub fn get_default_params() -> SetupCluster {
- let schema = shared_app(app_head());
+ let schema = daemon_shared_app();
schema
.p
.opts
@@ -492,7 +498,7 @@ impl SetupReporterReal {
environment: bool,
config_file: bool,
) -> Result, ConfiguratorError> {
- let app = shared_app(app_head());
+ let app = daemon_shared_app();
let mut vcls: Vec> = vec![];
if let Some(command_line) = command_line_opt.clone() {
vcls.push(Box::new(CommandLineVcl::new(command_line)));
@@ -742,7 +748,7 @@ impl ValueRetriever for DataDirectory {
}
impl std::default::Default for DataDirectory {
fn default() -> Self {
- Self::new(&DirsWrapperReal)
+ Self::new(&DirsWrapperReal::default())
}
}
impl DataDirectory {
@@ -1136,7 +1142,7 @@ impl ValueRetriever for RealUser {
}
impl std::default::Default for RealUser {
fn default() -> Self {
- Self::new(&DirsWrapperReal {})
+ Self::new(&DirsWrapperReal::default())
}
}
impl RealUser {
@@ -1224,11 +1230,13 @@ mod tests {
make_persistent_config_real_with_config_dao_null,
make_pre_populated_mocked_directory_wrapper, make_simplified_multi_config,
};
- use crate::test_utils::{assert_string_contains, rate_pack};
+ use crate::test_utils::{
+ assert_string_contains,
+ make_node_base_dir_and_return_its_absolute_and_relative_path_to_os_home_dir, rate_pack,
+ };
use core::option::Option;
- use dirs::home_dir;
use masq_lib::blockchains::chains::Chain as Blockchain;
- use masq_lib::blockchains::chains::Chain::PolyMumbai;
+ use masq_lib::blockchains::chains::Chain::PolyAmoy;
use masq_lib::constants::{DEFAULT_CHAIN, DEFAULT_GAS_PRICE};
use masq_lib::messages::UiSetupResponseValueStatus::{Blank, Configured, Required, Set};
use masq_lib::test_utils::environment_guard::{ClapGuard, EnvironmentGuard};
@@ -1378,7 +1386,7 @@ mod tests {
.into_iter()
.map(|(name, value)| UiSetupRequestValue::new(name, value))
.collect_vec();
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject
@@ -1436,7 +1444,7 @@ mod tests {
(
"real-user",
&RealUser::new(None, None, None)
- .populate(&DirsWrapperReal {})
+ .populate(&DirsWrapperReal::default())
.to_string(),
Default,
),
@@ -1496,7 +1504,7 @@ mod tests {
("scan-intervals","150|150|150",Set),
("scans", "off", Set),
]);
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject.get_modified_setup(existing_setup, vec![]).unwrap();
@@ -1568,7 +1576,7 @@ mod tests {
].into_iter()
.map (|(name, value)| UiSetupRequestValue::new(name, value))
.collect_vec();
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject
@@ -1643,7 +1651,7 @@ mod tests {
("MASQ_SCAN_INTERVALS","133|133|111")
].into_iter()
.for_each (|(name, value)| std::env::set_var (name, value));
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let params = vec![];
let subject = SetupReporterReal::new(dirs_wrapper);
@@ -1956,7 +1964,7 @@ mod tests {
("scan-intervals", "111|111|111", Set),
("scans", "off", Set),
]);
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject.get_modified_setup(existing_setup, params).unwrap();
@@ -2048,19 +2056,16 @@ mod tests {
#[test]
fn get_modified_setup_tilde_in_config_file_path() {
let _guard = EnvironmentGuard::new();
- let base_dir = ensure_node_home_directory_exists(
- "setup_reporter",
- "get_modified_setup_tilde_in_data_directory",
- );
- let data_dir = base_dir.join("data_dir");
- std::fs::create_dir_all(home_dir().expect("expect home dir").join("masqhome")).unwrap();
- let mut config_file = File::create(
- home_dir()
- .expect("expect home dir")
- .join("masqhome")
- .join("config.toml"),
- )
- .unwrap();
+ let (node_base_dir, node_base_dir_relative_to_os_home_dir) =
+ make_node_base_dir_and_return_its_absolute_and_relative_path_to_os_home_dir(
+ "setup_reporter",
+ "get_modified_setup_tilde_in_config_file_path",
+ );
+ let existing_data_dir = node_base_dir.join("obsolete_data_dir");
+ let new_dir_levels = PathBuf::new().join("whatever_dir").join("new_data_dir");
+ let new_data_dir = node_base_dir.join(new_dir_levels.as_path());
+ create_dir_all(new_data_dir.as_path()).unwrap();
+ let mut config_file = File::create(new_data_dir.join("config.toml")).unwrap();
config_file
.write_all(b"blockchain-service-url = \"https://www.mainnet.com\"\n")
.unwrap();
@@ -2069,24 +2074,30 @@ mod tests {
("chain", DEFAULT_CHAIN.rec().literal_identifier, Default),
(
"data-directory",
- &data_dir.to_string_lossy().to_string(),
+ &existing_data_dir.to_string_lossy().to_string(),
Default,
),
]);
+ let data_dir_referenced_from_the_home_dir = node_base_dir_relative_to_os_home_dir
+ .join(new_dir_levels)
+ .as_os_str()
+ .to_str()
+ .unwrap()
+ .to_string();
let incoming_setup = vec![
- ("data-directory", "~/masqhome"),
- ("config-file", "~/masqhome/config.toml"),
+ (
+ "data-directory",
+ &format!("~/{}", data_dir_referenced_from_the_home_dir),
+ ),
+ (
+ "config-file",
+ &format!("~/{}/config.toml", data_dir_referenced_from_the_home_dir),
+ ),
]
.into_iter()
.map(|(name, value)| UiSetupRequestValue::new(name, value))
.collect_vec();
-
- let expected_config_file_data = "https://www.mainnet.com";
- let dirs_wrapper = Box::new(
- DirsWrapperMock::new()
- .data_dir_result(Some(data_dir))
- .home_dir_result(Some(base_dir)),
- );
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject
@@ -2094,7 +2105,7 @@ mod tests {
.unwrap();
let actual_config_file_data = result.get("blockchain-service-url").unwrap().value.as_str();
- assert_eq!(actual_config_file_data, expected_config_file_data);
+ assert_eq!(actual_config_file_data, "https://www.mainnet.com");
}
#[test]
@@ -2264,14 +2275,10 @@ mod tests {
let current_data_dir = base_dir
.join("data_dir")
.join("MASQ")
- .join(BlockChain::PolyMumbai.rec().literal_identifier); //not a default
+ .join(BlockChain::PolyAmoy.rec().literal_identifier); //not a default
let existing_setup = setup_cluster_from(vec![
("blockchain-service-url", "", Required),
- (
- "chain",
- BlockChain::PolyMumbai.rec().literal_identifier,
- Set,
- ),
+ ("chain", BlockChain::PolyAmoy.rec().literal_identifier, Set),
("clandestine-port", "7788", Default),
("config-file", "config.toml", Default),
("consuming-private-key", "", Blank),
@@ -2310,7 +2317,7 @@ mod tests {
.get_modified_setup(existing_setup, incoming_setup)
.unwrap_err();
- let expected_chain = PolyMumbai.rec().literal_identifier;
+ let expected_chain = PolyAmoy.rec().literal_identifier;
let actual_chain = &resulting_setup_cluster.get("chain").unwrap().value;
assert_eq!(actual_chain, expected_chain);
let actual_data_directory =
@@ -2338,7 +2345,7 @@ mod tests {
(
"real-user",
&crate::bootstrapper::RealUser::new(None, None, None)
- .populate(&DirsWrapperReal {})
+ .populate(&DirsWrapperReal::default())
.to_string(),
Default,
),
@@ -2347,7 +2354,7 @@ mod tests {
.into_iter()
.map(|(name, value)| UiSetupRequestValue::new(name, value))
.collect_vec();
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let _ = subject
@@ -2375,7 +2382,7 @@ mod tests {
),
]);
let incoming_setup = vec![UiSetupRequestValue::clear("neighbors")];
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject
@@ -2487,7 +2494,7 @@ mod tests {
let setup = setup_cluster_from(vec![]);
let (real_user_opt, data_directory_opt, chain) =
- SetupReporterReal::calculate_fundamentals(&DirsWrapperReal {}, &setup).unwrap();
+ SetupReporterReal::calculate_fundamentals(&DirsWrapperReal::default(), &setup).unwrap();
assert_eq!(
real_user_opt,
@@ -2518,7 +2525,7 @@ mod tests {
]);
let (real_user_opt, data_directory_opt, chain) =
- SetupReporterReal::calculate_fundamentals(&DirsWrapperReal {}, &setup).unwrap();
+ SetupReporterReal::calculate_fundamentals(&DirsWrapperReal::default(), &setup).unwrap();
assert_eq!(
real_user_opt,
@@ -2549,7 +2556,7 @@ mod tests {
]);
let (real_user_opt, data_directory_opt, chain) =
- SetupReporterReal::calculate_fundamentals(&DirsWrapperReal {}, &setup).unwrap();
+ SetupReporterReal::calculate_fundamentals(&DirsWrapperReal::default(), &setup).unwrap();
assert_eq!(
real_user_opt,
@@ -2576,7 +2583,7 @@ mod tests {
]);
let (real_user_opt, data_directory_opt, chain) =
- SetupReporterReal::calculate_fundamentals(&DirsWrapperReal {}, &setup).unwrap();
+ SetupReporterReal::calculate_fundamentals(&DirsWrapperReal::default(), &setup).unwrap();
assert_eq!(
real_user_opt,
@@ -2599,12 +2606,13 @@ mod tests {
let setup = setup_cluster_from(vec![]);
let (real_user_opt, data_directory_opt, chain) =
- SetupReporterReal::calculate_fundamentals(&DirsWrapperReal {}, &setup).unwrap();
+ SetupReporterReal::calculate_fundamentals(&DirsWrapperReal::default(), &setup).unwrap();
assert_eq!(
real_user_opt,
Some(
- crate::bootstrapper::RealUser::new(None, None, None).populate(&DirsWrapperReal {})
+ crate::bootstrapper::RealUser::new(None, None, None)
+ .populate(&DirsWrapperReal::default())
)
);
assert_eq!(data_directory_opt, None);
@@ -2618,7 +2626,7 @@ mod tests {
"setup_reporter",
"blanking_a_parameter_with_a_default_produces_that_default",
);
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject
@@ -2690,7 +2698,7 @@ mod tests {
.into_iter()
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let subject = SetupReporterReal::new(Box::new(DirsWrapperReal {}));
+ let subject = SetupReporterReal::new(Box::new(DirsWrapperReal::default()));
let result = subject
.calculate_configured_setup(&setup, &data_directory)
@@ -2736,7 +2744,7 @@ mod tests {
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let (result, _) = SetupReporterReal::new(Box::new(DirsWrapperReal {}))
+ let (result, _) = SetupReporterReal::new(Box::new(DirsWrapperReal::default()))
.calculate_configured_setup(&setup, &*data_directory);
assert_eq!(result.get("gas-price").unwrap().value, "10".to_string());
@@ -2757,7 +2765,7 @@ mod tests {
}
let setup = vec![
//no config-file setting
- UiSetupResponseValue::new("chain", "polygon-mumbai", Set),
+ UiSetupResponseValue::new("chain", "polygon-amoy", Set),
UiSetupResponseValue::new("neighborhood-mode", "zero-hop", Set),
UiSetupResponseValue::new("config-file", "booga/special.toml", Set),
UiSetupResponseValue::new(
@@ -2769,7 +2777,7 @@ mod tests {
.into_iter()
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let subject = SetupReporterReal::new(Box::new(DirsWrapperReal {}));
+ let subject = SetupReporterReal::new(Box::new(DirsWrapperReal::default()));
let result = subject
.calculate_configured_setup(&setup, &data_directory)
.0;
@@ -2795,7 +2803,7 @@ mod tests {
.into_iter()
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let subject = SetupReporterReal::new(Box::new(DirsWrapperReal {}));
+ let subject = SetupReporterReal::new(Box::new(DirsWrapperReal::default()));
let result = subject
.calculate_configured_setup(&setup, &data_directory)
@@ -2833,7 +2841,7 @@ mod tests {
.into_iter()
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let subject = SetupReporterReal::new(Box::new(DirsWrapperReal {}));
+ let subject = SetupReporterReal::new(Box::new(DirsWrapperReal::default()));
let result = subject.calculate_configured_setup(&setup, &data_dir).0;
@@ -2848,7 +2856,7 @@ mod tests {
);
let config_file_dir = config_file_dir.canonicalize().unwrap();
let config_file_path = config_file_dir.join("nonexistent.toml");
- let wrapper = DirsWrapperReal {};
+ let wrapper = DirsWrapperReal::default();
let data_directory = wrapper
.data_dir()
.unwrap()
@@ -2866,7 +2874,7 @@ mod tests {
.into_iter()
.map(|uisrv| (uisrv.name.clone(), uisrv))
.collect();
- let subject = SetupReporterReal::new(Box::new(DirsWrapperReal {}));
+ let subject = SetupReporterReal::new(Box::new(DirsWrapperReal::default()));
let result = subject
.calculate_configured_setup(&setup, &data_directory)
@@ -2919,11 +2927,14 @@ mod tests {
#[test]
fn data_directory_computed_default() {
- let real_user = RealUser::new(None, None, None).populate(&DirsWrapperReal {});
- let expected =
- data_directory_from_context(&DirsWrapperReal {}, &real_user, Blockchain::EthMainnet)
- .to_string_lossy()
- .to_string();
+ let real_user = RealUser::new(None, None, None).populate(&DirsWrapperReal::default());
+ let expected = data_directory_from_context(
+ &DirsWrapperReal::default(),
+ &real_user,
+ Blockchain::EthMainnet,
+ )
+ .to_string_lossy()
+ .to_string();
let mut config = BootstrapperConfig::new();
config.real_user = real_user;
config.blockchain_bridge_config.chain = Blockchain::from("eth-mainnet");
@@ -3317,7 +3328,7 @@ mod tests {
result,
Some((
RealUser::new(None, None, None)
- .populate(&DirsWrapperReal {})
+ .populate(&DirsWrapperReal::default())
.to_string(),
Default
))
@@ -3699,7 +3710,7 @@ mod tests {
"data-directory",
&masqhome.to_str().unwrap(),
)];
- let dirs_wrapper = Box::new(DirsWrapperReal);
+ let dirs_wrapper = Box::new(DirsWrapperReal::default());
let subject = SetupReporterReal::new(dirs_wrapper);
let result = subject.get_modified_setup(existing_setup, incoming_setup);
@@ -3716,10 +3727,10 @@ mod tests {
let _guard = EnvironmentGuard::new();
let existing_setup =
setup_cluster_from(vec![("real-user", "1111:1111:/home/booga", Default)]);
- let incoming_setup = vec![UiSetupRequestValue::new("chain", "polygon-mumbai")];
+ let incoming_setup = vec![UiSetupRequestValue::new("chain", "polygon-amoy")];
let home_directory = Path::new("/home/booga");
let data_directory = home_directory.join("data");
- let expected = data_directory.join("MASQ").join("polygon-mumbai");
+ let expected = data_directory.join("MASQ").join("polygon-amoy");
let dirs_wrapper = Box::new(
DirsWrapperMock::new()
.data_dir_result(Some(data_directory))
diff --git a/node/src/database/config_dumper.rs b/node/src/database/config_dumper.rs
index 891eac1ad..78f23ade7 100644
--- a/node/src/database/config_dumper.rs
+++ b/node/src/database/config_dumper.rs
@@ -198,7 +198,7 @@ mod tests {
.opt("--dump-config")
.into();
let subject = DumpConfigRunnerReal {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
};
let caught_panic = catch_unwind(AssertUnwindSafe(|| {
@@ -239,7 +239,7 @@ mod tests {
.opt("--dump-config")
.into();
let subject = DumpConfigRunnerReal {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
};
let result = subject.go(&mut holder.streams(), args_vec.as_slice());
@@ -471,7 +471,7 @@ mod tests {
.opt("--dump-config")
.into();
let subject = DumpConfigRunnerReal {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
};
let result = subject.go(&mut holder.streams(), args_vec.as_slice());
@@ -579,7 +579,7 @@ mod tests {
.opt("--dump-config")
.into();
let subject = DumpConfigRunnerReal {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
};
let result = subject.go(&mut holder.streams(), args_vec.as_slice());
diff --git a/node/src/neighborhood/dot_graph.rs b/node/src/neighborhood/dot_graph.rs
index 345afe077..09e30329c 100644
--- a/node/src/neighborhood/dot_graph.rs
+++ b/node/src/neighborhood/dot_graph.rs
@@ -12,6 +12,7 @@ pub struct NodeRenderableInner {
pub version: u32,
pub accepts_connections: bool,
pub routes_data: bool,
+ pub country_code: String
}
pub struct NodeRenderable {
@@ -45,10 +46,11 @@ impl NodeRenderable {
fn render_label(&self) -> String {
let inner_string = match &self.inner {
Some(inner) => format!(
- "{}{} v{}\\n",
+ "{}{} v{} {}\\n",
if inner.accepts_connections { "A" } else { "a" },
if inner.routes_data { "R" } else { "r" },
inner.version,
+ inner.country_code
),
None => String::new(),
};
@@ -107,6 +109,7 @@ mod tests {
version: 1,
accepts_connections: true,
routes_data: true,
+ country_code: "ZZ".to_string(),
}),
public_key: public_key.clone(),
node_addr: None,
@@ -120,7 +123,7 @@ mod tests {
assert_string_contains(
&result,
&format!(
- "\"{}\" [label=\"AR v1\\n{}\"];",
+ "\"{}\" [label=\"AR v1 ZZ\\n{}\"];",
public_key_64, public_key_trunc
),
);
@@ -135,6 +138,7 @@ mod tests {
version: 1,
accepts_connections: false,
routes_data: false,
+ country_code: "ZZ".to_string(),
}),
public_key: public_key.clone(),
node_addr: None,
@@ -148,7 +152,7 @@ mod tests {
assert_string_contains(
&result,
&format!(
- "\"{}\" [label=\"ar v1\\n{}\"];",
+ "\"{}\" [label=\"ar v1 ZZ\\n{}\"];",
public_key_64, public_key_64
),
);
diff --git a/node/src/neighborhood/gossip.rs b/node/src/neighborhood/gossip.rs
index 1c156b4b6..389d2e933 100644
--- a/node/src/neighborhood/gossip.rs
+++ b/node/src/neighborhood/gossip.rs
@@ -378,11 +378,16 @@ impl Gossip_0v1 {
to: k.clone(),
})
});
+ let country_code = match &nri.country_code_opt {
+ Some(cc) => cc.clone(),
+ None => "ZZ".to_string(),
+ };
node_renderables.push(NodeRenderable {
inner: Some(NodeRenderableInner {
version: nri.version,
accepts_connections: nri.accepts_connections,
routes_data: nri.routes_data,
+ country_code
}),
public_key: nri.public_key.clone(),
node_addr: addr.clone(),
diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs
index fa5009ae7..a7d4574db 100644
--- a/node/src/neighborhood/gossip_acceptor.rs
+++ b/node/src/neighborhood/gossip_acceptor.rs
@@ -3,7 +3,7 @@
use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord, Gossip_0v1};
use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, NeighborhoodDatabaseError};
use crate::neighborhood::node_record::NodeRecord;
-use crate::neighborhood::AccessibleGossipRecord;
+use crate::neighborhood::{AccessibleGossipRecord, UserExitPreferences};
use crate::sub_lib::cryptde::{CryptDE, PublicKey};
use crate::sub_lib::neighborhood::{
ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata,
@@ -130,7 +130,7 @@ impl GossipHandler for DebutHandler {
database: &mut NeighborhoodDatabase,
mut agrs: Vec,
gossip_source: SocketAddr,
- _neighborhood_metadata: NeighborhoodMetadata,
+ neighborhood_metadata: NeighborhoodMetadata,
) -> GossipAcceptanceResult {
let source_agr = {
let mut agr = agrs.remove(0); // empty Gossip shouldn't get here
@@ -165,7 +165,13 @@ impl GossipHandler for DebutHandler {
source_node_addr,
);
}
- if let Ok(result) = self.try_accept_debut(cryptde, database, &source_agr, gossip_source) {
+ if let Ok(result) = self.try_accept_debut(
+ cryptde,
+ database,
+ &source_agr,
+ gossip_source,
+ neighborhood_metadata.user_exit_preferences_opt,
+ ) {
return result;
}
debug!(self.logger, "Seeking neighbor for Pass");
@@ -266,13 +272,22 @@ impl DebutHandler {
database: &mut NeighborhoodDatabase,
debuting_agr: &AccessibleGossipRecord,
gossip_source: SocketAddr,
+ user_exit_preferences_opt: Option,
) -> Result {
if database.gossip_target_degree(database.root().public_key()) >= MAX_DEGREE {
debug!(self.logger, "Neighbor count already at maximum");
return Err(());
}
let debut_node_addr_opt = debuting_agr.node_addr_opt.clone();
- let debuting_node = NodeRecord::from(debuting_agr);
+ let mut debuting_node = NodeRecord::from(debuting_agr);
+ match user_exit_preferences_opt {
+ Some(user_exit_preferences) => {
+ //TODO 788 check if country code is present in Neighborhood DB and if yes, perform assign country code to exit_countries without duplication
+ user_exit_preferences.assign_nodes_country_undesirability(&mut debuting_node);
+ }
+ None => (),
+ }
+ // TODO 468 make debuting_node mut and add country_undesirability to its metadata
let debut_node_key = database
.add_node(debuting_node)
.expect("Debuting Node suddenly appeared in database");
@@ -685,7 +700,13 @@ impl GossipHandler for IntroductionHandler {
.as_ref()
.expect("IP Address not found for the Node Addr.")
.ip_addr();
- match self.update_database(database, cryptde, introducer) {
+ // TODO 468 pass the NeighborhoodMetadata into update_database
+ match self.update_database(
+ database,
+ cryptde,
+ introducer,
+ neighborhood_metadata.user_exit_preferences_opt,
+ ) {
Ok(_) => (),
Err(e) => {
return GossipAcceptanceResult::Ban(format!(
@@ -845,6 +866,7 @@ impl IntroductionHandler {
database: &mut NeighborhoodDatabase,
cryptde: &dyn CryptDE,
introducer: AccessibleGossipRecord,
+ user_exit_preferences_opt: Option,
) -> Result {
let introducer_key = introducer.inner.public_key.clone();
match database.node_by_key_mut(&introducer_key) {
@@ -869,7 +891,14 @@ impl IntroductionHandler {
}
}
None => {
- let new_introducer = NodeRecord::from(introducer);
+ let mut new_introducer = NodeRecord::from(introducer);
+ //TODO 468 add country undesirability
+ match user_exit_preferences_opt {
+ //TODO 788 check if country code is present in Neighborhood DB and if yes, perform assign country code to exit_countries without duplication
+ Some(user_exit_preferences) => user_exit_preferences
+ .assign_nodes_country_undesirability(&mut new_introducer),
+ None => (),
+ }
debug!(
self.logger,
"Adding introducer {} to database", introducer_key
@@ -984,6 +1013,7 @@ impl GossipHandler for StandardGossipHandler {
database,
&filtered_agrs,
gossip_source,
+ neighborhood_metadata.user_exit_preferences_opt.as_ref(),
);
db_changed = self.identify_and_update_obsolete_nodes(database, filtered_agrs) || db_changed;
db_changed =
@@ -1095,6 +1125,7 @@ impl StandardGossipHandler {
database: &mut NeighborhoodDatabase,
agrs: &[AccessibleGossipRecord],
gossip_source: SocketAddr,
+ user_exit_preferences_opt: Option<&UserExitPreferences>,
) -> bool {
let all_keys = database
.keys()
@@ -1112,7 +1143,15 @@ impl StandardGossipHandler {
}
})
.for_each(|agr| {
- let node_record = NodeRecord::from(agr);
+ let mut node_record = NodeRecord::from(agr);
+ // TODO modify for country undesirability in node_record (make it mut)
+ match user_exit_preferences_opt {
+ Some(user_exit_preferences) => {
+ //TODO 788 check if country code is present in Neighborhood DB and if yes, perform assign country code to exit_countries without duplication
+ user_exit_preferences.assign_nodes_country_undesirability(&mut node_record)
+ }
+ None => (),
+ }
trace!(
self.logger,
"Discovered new Node {:?}: {:?}",
@@ -1369,8 +1408,14 @@ mod tests {
use crate::neighborhood::gossip_producer::GossipProducer;
use crate::neighborhood::gossip_producer::GossipProducerReal;
use crate::neighborhood::node_record::NodeRecord;
+ use crate::neighborhood::{
+ ExitPreference, UserExitPreferences, COUNTRY_UNDESIRABILITY_FACTOR,
+ UNREACHABLE_COUNTRY_PENALTY,
+ };
use crate::sub_lib::cryptde_null::CryptDENull;
- use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage};
+ use crate::sub_lib::neighborhood::{
+ ConnectionProgressEvent, ConnectionProgressMessage, ExitLocation,
+ };
use crate::sub_lib::utils::time_t_timestamp;
use crate::test_utils::neighborhood_test_utils::{
db_from_node, gossip_about_nodes_from_database, linearly_connect_nodes,
@@ -1407,6 +1452,7 @@ mod tests {
connection_progress_peers: vec![],
cpm_recipient: make_cpm_recipient().0,
db_patch_size: DB_PATCH_SIZE_FOR_TEST,
+ user_exit_preferences_opt: None,
}
}
@@ -1677,6 +1723,7 @@ mod tests {
dest_db.add_arbitrary_half_neighbor(root_node.public_key(), half_debuted_node.public_key());
let logger = Logger::new("Debut test");
let subject = DebutHandler::new(logger);
+ let neighborhood_metadata = make_default_neighborhood_metadata();
let counter_debut = subject
.try_accept_debut(
@@ -1684,6 +1731,7 @@ mod tests {
&mut dest_db,
&AccessibleGossipRecord::from(&new_debutant),
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(4, 5, 6, 7)), 4567),
+ neighborhood_metadata.user_exit_preferences_opt,
)
.unwrap();
@@ -2020,6 +2068,16 @@ mod tests {
let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN);
let subject = IntroductionHandler::new(Logger::new("test"));
let agrs: Vec = gossip.try_into().unwrap();
+ let mut neighborhood_metadata = make_default_neighborhood_metadata();
+ neighborhood_metadata.user_exit_preferences_opt = Some(UserExitPreferences {
+ exit_countries: vec!["FR".to_string()],
+ location_preference: ExitPreference::ExitCountryNoFallback,
+ locations_opt: Some(vec![ExitLocation {
+ country_codes: vec!["FR".to_string()],
+ priority: 2,
+ }]),
+ db_countries: vec!["FR".to_string()],
+ });
let qualifies_result = subject.qualifies(&dest_db, &agrs, gossip_source);
let handle_result = subject.handle(
@@ -2027,7 +2085,7 @@ mod tests {
&mut dest_db,
agrs.clone(),
gossip_source,
- make_default_neighborhood_metadata(),
+ neighborhood_metadata,
);
assert_eq!(Qualification::Matched, qualifies_result);
@@ -2047,6 +2105,7 @@ mod tests {
dest_db.node_by_key_mut(&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;
expected_introducer.resign();
assert_eq!(result_introducer, &expected_introducer);
assert_eq!(
@@ -2407,6 +2466,15 @@ mod tests {
let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into();
let (cpm_recipient, recording_arc) = make_cpm_recipient();
let mut neighborhood_metadata = make_default_neighborhood_metadata();
+ neighborhood_metadata.user_exit_preferences_opt = Some(UserExitPreferences {
+ exit_countries: vec!["FR".to_string()],
+ location_preference: ExitPreference::ExitCountryWithFallback,
+ locations_opt: Some(vec![ExitLocation {
+ country_codes: vec!["FR".to_string()],
+ priority: 1,
+ }]),
+ db_countries: vec!["FR".to_string()],
+ });
neighborhood_metadata.cpm_recipient = cpm_recipient;
let system = System::new("test");
@@ -2419,6 +2487,22 @@ mod tests {
neighborhood_metadata,
);
+ assert_eq!(
+ dest_db
+ .node_by_key(node_a.public_key())
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32
+ );
+ assert_eq!(
+ dest_db
+ .node_by_key(node_b.public_key())
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ UNREACHABLE_COUNTRY_PENALTY
+ );
assert_eq!(Qualification::Matched, qualifies_result);
assert_eq!(GossipAcceptanceResult::Accepted, handle_result);
assert_eq!(
@@ -2979,12 +3063,22 @@ mod tests {
let (gossip, mut debut_node, gossip_source) = make_debut(2345, Mode::Standard);
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()],
+ location_preference: ExitPreference::ExitCountryWithFallback,
+ locations_opt: Some(vec![ExitLocation {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1,
+ }]),
+ db_countries: vec!["CZ".to_string()],
+ });
let result = subject.handle(
&mut dest_db,
gossip.try_into().unwrap(),
gossip_source,
- make_default_neighborhood_metadata(),
+ neighborhood_metadata,
);
let after = time_t_timestamp();
@@ -3034,6 +3128,11 @@ Length: 24 (0x18) bytes
let reference_node = dest_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!(
+ reference_node.metadata.country_undesirability,
+ UNREACHABLE_COUNTRY_PENALTY
+ );
+ reference_node.metadata.country_undesirability = 0u32;
assert_node_records_eq(reference_node, &debut_node, before, after);
}
diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs
index f7dc08404..6fbad2992 100644
--- a/node/src/neighborhood/mod.rs
+++ b/node/src/neighborhood/mod.rs
@@ -9,11 +9,6 @@ pub mod node_location;
pub mod node_record;
pub mod overall_connection_status;
-use std::collections::HashSet;
-use std::convert::TryFrom;
-use std::net::{IpAddr, SocketAddr};
-use std::path::PathBuf;
-
use actix::Context;
use actix::Handler;
use actix::MessageResult;
@@ -23,10 +18,18 @@ use actix::{Addr, AsyncContext};
use itertools::Itertools;
use masq_lib::messages::{
FromMessageBody, ToMessageBody, UiConnectionStage, UiConnectionStatusRequest,
+ UiSetExitLocationRequest, UiSetExitLocationResponse,
};
use masq_lib::messages::{UiConnectionStatusResponse, UiShutdownRequest};
-use masq_lib::ui_gateway::{MessageTarget, NodeFromUiMessage, NodeToUiMessage};
+use masq_lib::ui_gateway::{MessageBody, MessageTarget, NodeFromUiMessage, NodeToUiMessage};
use masq_lib::utils::{exit_process, ExpectValue, NeighborhoodModeLight};
+use std::collections::{HashMap, HashSet, VecDeque};
+use std::convert::TryFrom;
+use std::fmt::Debug;
+use std::net::{IpAddr, SocketAddr};
+use std::path::PathBuf;
+use std::rc::Rc;
+use std::string::ToString;
use crate::bootstrapper::BootstrapperConfig;
use crate::database::db_initializer::DbInitializationConfig;
@@ -48,14 +51,14 @@ use crate::sub_lib::cryptde::{CryptDE, CryptData, PlainData};
use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg};
use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage};
use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType};
-use crate::sub_lib::neighborhood::RouteQueryResponse;
-use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage;
use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor};
use crate::sub_lib::neighborhood::{ConfigurationChange, RemoveNeighborMessage};
use crate::sub_lib::neighborhood::{ConfigurationChangeMessage, RouteQueryMessage};
use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices};
use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService};
use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1};
+use crate::sub_lib::neighborhood::{ExitLocation, UpdateNodeRecordMetadataMessage};
+use crate::sub_lib::neighborhood::{ExitLocationSet, RouteQueryResponse};
use crate::sub_lib::neighborhood::{Hops, NeighborhoodMetadata, NodeQueryResponseMetadata};
use crate::sub_lib::neighborhood::{NRMetadataChange, NodeQueryMessage};
use crate::sub_lib::neighborhood::{NeighborhoodSubs, NeighborhoodTools};
@@ -74,15 +77,84 @@ use gossip_acceptor::GossipAcceptorReal;
use gossip_producer::GossipProducer;
use gossip_producer::GossipProducerReal;
use masq_lib::blockchains::chains::Chain;
+use masq_lib::constants::EXIT_COUNTRY_ERROR;
use masq_lib::crash_point::CrashPoint;
use masq_lib::logger::Logger;
+use masq_lib::ui_gateway::MessagePath::Conversation;
use neighborhood_database::NeighborhoodDatabase;
use node_record::NodeRecord;
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 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 {
+ routes: Vec<(Vec, i64)>,
+}
+
+#[derive(Debug, PartialEq, Eq, Clone)]
+pub enum ExitPreference {
+ Nothing,
+ ExitCountryWithFallback,
+ ExitCountryNoFallback,
+}
+
+#[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
+ location_preference: ExitPreference,
+ locations_opt: Option>,
+ db_countries: Vec,
+}
+
+impl UserExitPreferences {
+ fn new() -> UserExitPreferences {
+ UserExitPreferences {
+ exit_countries: vec![],
+ location_preference: ExitPreference::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.location_preference == ExitPreference::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,
@@ -106,6 +178,7 @@ pub struct Neighborhood {
db_password_opt: Option,
logger: Logger,
tools: NeighborhoodTools,
+ user_exit_preferences: UserExitPreferences,
}
impl Actor for Neighborhood {
@@ -117,10 +190,7 @@ impl Handler for Neighborhood {
fn handle(&mut self, msg: BindMessage, ctx: &mut Self::Context) -> Self::Result {
ctx.set_mailbox_capacity(NODE_MAILBOX_CAPACITY);
- self.hopper_opt = Some(msg.peer_actors.hopper.from_hopper_client);
- self.hopper_no_lookup_opt = Some(msg.peer_actors.hopper.from_hopper_client_no_lookup);
- self.connected_signal_opt = Some(msg.peer_actors.accountant.start);
- self.node_to_ui_recipient_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub);
+ self.handle_bind_message(msg);
}
}
@@ -388,7 +458,9 @@ impl Handler for Neighborhood {
fn handle(&mut self, msg: NodeFromUiMessage, _ctx: &mut Self::Context) -> Self::Result {
let client_id = msg.client_id;
- if let Ok((_, context_id)) = UiConnectionStatusRequest::fmb(msg.body.clone()) {
+ if let Ok((message, context_id)) = UiSetExitLocationRequest::fmb(msg.body.clone()) {
+ self.handle_exit_location_message(message, client_id, context_id);
+ } else if let Ok((_, context_id)) = UiConnectionStatusRequest::fmb(msg.body.clone()) {
self.handle_connection_status_message(client_id, context_id);
} else if let Ok((body, _)) = UiShutdownRequest::fmb(msg.body.clone()) {
self.handle_shutdown_order(client_id, body);
@@ -495,6 +567,7 @@ impl Neighborhood {
db_password_opt: config.db_password_opt.clone(),
logger: Logger::new("Neighborhood"),
tools: NeighborhoodTools::default(),
+ user_exit_preferences: UserExitPreferences::new(),
}
}
@@ -793,6 +866,7 @@ impl Neighborhood {
connection_progress_peers: self.overall_connection_status.get_peer_addrs(),
cpm_recipient,
db_patch_size: self.db_patch_size,
+ user_exit_preferences_opt: Some(self.user_exit_preferences.clone()),
};
let acceptance_result = self.gossip_acceptor.handle(
&mut self.neighborhood_database,
@@ -801,8 +875,12 @@ impl Neighborhood {
neighborhood_metadata,
);
match acceptance_result {
- GossipAcceptanceResult::Accepted => self.gossip_to_neighbors(),
+ GossipAcceptanceResult::Accepted => {
+ self.user_exit_preferences.db_countries = self.init_db_countries();
+ self.gossip_to_neighbors()
+ }
GossipAcceptanceResult::Reply(next_debut, target_key, target_node_addr) => {
+ self.user_exit_preferences.db_countries = self.init_db_countries();
self.handle_gossip_reply(next_debut, &target_key, &target_node_addr)
}
GossipAcceptanceResult::Failed(failure, target_key, target_node_addr) => {
@@ -813,8 +891,9 @@ impl Neighborhood {
self.handle_gossip_ignored(ignored_node_name, gossip_record_count)
}
GossipAcceptanceResult::Ban(reason) => {
- warning!(self.logger, "Malefactor detected at {}, but malefactor bans not yet implemented; ignoring: {}", gossip_source, 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
+ 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);
}
}
@@ -888,6 +967,7 @@ impl Neighborhood {
return_component_opt: Some(Component::ProxyServer),
payload_size: 10000,
hostname_opt: None,
+ target_country_opt: None,
};
if self.handle_route_query_message(msg).is_some() {
debug!(
@@ -1120,7 +1200,10 @@ impl Neighborhood {
minimum_hop_count, origin, target_component, target_str
))
}
- Some(route) => Ok(RouteSegment::new(route, target_component)),
+ Some(route) => {
+ let route_ref: Vec<&PublicKey> = route.iter().collect();
+ Ok(RouteSegment::new(route_ref, target_component))
+ },
}
}
@@ -1192,11 +1275,10 @@ impl Neighborhood {
fn validate_last_node_not_too_close_to_first_node(
&self,
- prefix_len: usize,
first_node_key: &PublicKey,
candidate_node_key: &PublicKey,
) -> bool {
- if prefix_len <= 2 {
+ if self.min_hops as usize <= 1usize {
true // Zero- and single-hop routes are not subject to exit-too-close restrictions
} else {
!self
@@ -1205,6 +1287,52 @@ impl Neighborhood {
}
}
+ fn validate_fallback_country_exit_codes(&self, last_node_cc_opt: Option<&String>) -> bool {
+ let last_cc = match last_node_cc_opt {
+ Some(cc) => cc.to_owned(),
+ None => "ZZ".to_string(),
+ };
+ if self.user_exit_preferences.exit_countries.contains(&last_cc) {
+ return true;
+ }
+ if self.user_exit_preferences.exit_countries.is_empty() {
+ 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;
+ }
+ }
+ return true;
+ }
+
+ fn validate_last_node_country_code(
+ &self,
+ last_node: &NodeRecord,
+ research_neighborhood: bool,
+ direction: RouteDirection,
+ ) -> bool {
+ if self.user_exit_preferences.location_preference == ExitPreference::Nothing
+ || (self.user_exit_preferences.location_preference == ExitPreference::ExitCountryWithFallback
+ && self.validate_fallback_country_exit_codes(last_node.inner.country_code_opt.as_ref()))
+ || research_neighborhood
+ || direction == RouteDirection::Back
+ {
+ true // Zero- and single-hop routes are not subject to exit-too-close restrictions
+ } else {
+ match last_node.inner.country_code_opt.as_ref() {
+ Some(country_code) => self
+ .user_exit_preferences
+ .exit_countries
+ .contains(&country_code),
+ _ => false,
+ }
+ }
+ }
+
fn compute_undesirability(
node_record: &NodeRecord,
payload_size: u64,
@@ -1215,6 +1343,7 @@ impl Neighborhood {
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)
@@ -1235,7 +1364,6 @@ impl Neighborhood {
rate_undesirability += UNREACHABLE_HOST_PENALTY;
}
}
-
rate_undesirability
}
@@ -1259,6 +1387,94 @@ impl Neighborhood {
return_route_id
}
+ pub fn find_exit_location<'a>(
+ &'a self,
+ source: &'a PublicKey,
+ minimum_hops: usize,
+ payload_size: usize,
+ ) -> (HashMap, Vec) {
+ let mut minimum_undesirability = i64::MAX;
+ let initial_undesirability = 0;
+ let mut research_exits: Vec> = vec![Rc::new(RouteElement {
+ previous: None,
+ public_key: source.clone(),
+ undesirability: initial_undesirability,
+ })];
+ let prefix = Rc::new(RouteElement {
+ previous: None,
+ public_key: source.clone(),
+ undesirability: initial_undesirability,
+ });
+ let over_routes = self.routing_engine(
+ source,
+ prefix,
+ None,
+ minimum_hops,
+ payload_size,
+ RouteDirection::Over,
+ &mut minimum_undesirability,
+ None,
+ true,
+ &mut research_exits,
+ );
+ let mut result_exit: HashMap = HashMap::new();
+ let mut research_exit: Vec = vec![];
+ for exit_node in over_routes {
+ let mut segment = VecDeque::from(vec![]);
+ let key = PublicKey::new(&[]);
+ let undesirability = exit_node.undesirability;
+ let mut current = Some(Rc::clone(&exit_node));
+ while let Some(node) = current {
+ segment.push_front(node.public_key.clone());
+ current = node.previous.as_ref().map(Rc::clone);
+ }
+ result_exit
+ .entry(key)
+ .and_modify(|e| {
+ e.routes
+ .push((Vec::from(segment.clone()), undesirability))
+ })
+ .or_insert(ExitLocationsRoutes {
+ routes: vec![(Vec::from(segment.clone()), undesirability)],
+ });
+ }
+ for exit_node in &research_exits {
+ let mut segment = VecDeque::from(vec![]);
+ let undesirability = exit_node.undesirability;
+ let mut current = Some(Rc::clone(&exit_node));
+ while let Some(node) = current {
+ segment.push_front(node.public_key.clone());
+ current = node.previous.as_ref().map(Rc::clone);
+ }
+ research_exit.push(exit_node.public_key.clone());
+ result_exit
+ .entry(exit_node.public_key.clone())
+ .and_modify(|e| {
+ e.routes
+ .push((Vec::from(segment.clone()), undesirability))
+ })
+ .or_insert(ExitLocationsRoutes {
+ routes: vec![(Vec::from(segment.clone()), undesirability)],
+ });
+ }
+ /*
+ 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)],
+ });
+ }
+ });*/
+ (result_exit, research_exit)
+ }
+
// Interface to main routing engine. Supply source key, target key--if any--in target_opt,
// minimum hops, size of payload in bytes, the route direction, and the hostname if you know it.
//
@@ -1266,6 +1482,7 @@ impl Neighborhood {
// target in hops_remaining or more hops with no cycles, or from the origin hops_remaining hops
// out into the MASQ Network. No round trips; if you want a round trip, call this method twice.
// If the return value is None, no qualifying route was found.
+ #[allow(clippy::too_many_arguments)]
fn find_best_route_segment<'a>(
&'a self,
source: &'a PublicKey,
@@ -1274,110 +1491,225 @@ impl Neighborhood {
payload_size: usize,
direction: RouteDirection,
hostname_opt: Option<&str>,
- ) -> Option> {
+ ) -> Option> {
let mut minimum_undesirability = i64::MAX;
let initial_undesirability =
self.compute_initial_undesirability(source, payload_size as u64, direction);
+ let prefix = Rc::new(RouteElement {
+ previous: None,
+ public_key: source.clone(),
+ undesirability: initial_undesirability,
+ });
let result = self
.routing_engine(
- vec![source],
- initial_undesirability,
+ source,
+ prefix,
target_opt,
minimum_hops,
payload_size,
direction,
&mut minimum_undesirability,
hostname_opt,
+ false,
+ &mut vec![],
)
.into_iter()
- .filter_map(|cr| match cr.undesirability <= minimum_undesirability {
- true => Some(cr.nodes),
- false => None,
- })
+ .filter(|cr| cr.undesirability <= minimum_undesirability)
.next();
- result
+ //TODO reverse the public keys from Rc list
+ match result {
+ Some(route_node) => {
+ let mut segment = VecDeque::from(vec![]);
+ let mut current = Some(Rc::clone(&route_node));
+ while let Some(node) = current {
+ segment.push_front(node.public_key.clone());
+ current = node.previous.as_ref().map(Rc::clone);
+ }
+ Some(Vec::from(segment))
+ },
+ None => None
+ }
+
}
#[allow(clippy::too_many_arguments)]
fn routing_engine<'a>(
&'a self,
- prefix: Vec<&'a PublicKey>,
- undesirability: i64,
+ first_node_key: &PublicKey,
+ prefix: Rc,
target_opt: Option<&'a PublicKey>,
hops_remaining: usize,
payload_size: usize,
direction: RouteDirection,
minimum_undesirability: &mut i64,
hostname_opt: Option<&str>,
- ) -> Vec> {
- if undesirability > *minimum_undesirability {
+ research_neighborhood: bool,
+ research_exits: &mut Vec>,
+ ) -> Vec> {
+ if prefix.undesirability > *minimum_undesirability && !research_neighborhood {
return vec![];
}
- let first_node_key = prefix.first().expect("Empty prefix");
+ //TODO when target node is present ignore all country_codes selection - write test for route back and ignore the country code
let previous_node = self
.neighborhood_database
- .node_by_key(prefix.last().expect("Empty prefix"))
+ .node_by_key(&prefix.public_key)
.expect("Last Node magically disappeared");
// Check to see if we're done. If we are, all three of these qualifications will pass.
if self.route_length_qualifies(hops_remaining)
&& self.last_key_qualifies(previous_node, target_opt)
&& self.validate_last_node_not_too_close_to_first_node(
- prefix.len(),
- *first_node_key,
+ first_node_key,
previous_node.public_key(),
)
{
- if undesirability < *minimum_undesirability {
- *minimum_undesirability = undesirability;
+ if !research_neighborhood && self.validate_last_node_country_code(
+ &previous_node,
+ research_neighborhood,
+ direction
+ ) {
+ if prefix.undesirability < *minimum_undesirability {
+ *minimum_undesirability = prefix.undesirability;
+ }
+ vec![prefix.clone()]
+ } else if research_neighborhood && Self::research_exit_contains(&research_exits, &prefix.public_key) {
+ vec![]
+ } else {
+ if research_neighborhood {
+ research_exits.push(Rc::new(RouteElement {
+ previous: Some(Rc::clone(&prefix)),
+ public_key: prefix.public_key.clone(),
+ undesirability: prefix.undesirability,
+ })
+ );
+ }
+ self.routing_guts(
+ first_node_key,
+ prefix,
+ target_opt,
+ hops_remaining,
+ payload_size,
+ direction,
+ minimum_undesirability,
+ hostname_opt,
+ research_neighborhood,
+ research_exits,
+ previous_node,
+ )
}
- vec![ComputedRouteSegment::new(prefix, undesirability)]
- } else if (hops_remaining == 0) && target_opt.is_none() {
+ } else if ((hops_remaining == 0) && target_opt.is_none() && !research_neighborhood)
+ && (self.user_exit_preferences.location_preference == ExitPreference::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:
// don't continue a targetless search past the minimum hop count
vec![]
} else {
- // Go through all the neighbors and compute shorter routes through all the ones we're not already using.
- previous_node
- .full_neighbors(&self.neighborhood_database)
- .iter()
- .filter(|node_record| !prefix.contains(&node_record.public_key()))
- .filter(|node_record| {
- node_record.routes_data()
- || Self::is_orig_node_on_back_leg(**node_record, target_opt, direction)
- })
- .flat_map(|node_record| {
- let mut new_prefix = prefix.clone();
- new_prefix.push(node_record.public_key());
-
- let new_hops_remaining = if hops_remaining == 0 {
- 0
- } else {
- hops_remaining - 1
- };
-
- let new_undesirability = self.compute_new_undesirability(
- node_record,
- undesirability,
- target_opt,
- new_hops_remaining,
- payload_size as u64,
- direction,
- hostname_opt,
- );
+ self.routing_guts(
+ first_node_key,
+ prefix,
+ target_opt,
+ hops_remaining,
+ payload_size,
+ direction,
+ minimum_undesirability,
+ hostname_opt,
+ research_neighborhood,
+ research_exits,
+ previous_node,
+ )
+ }
+ }
- self.routing_engine(
- new_prefix.clone(),
- new_undesirability,
- target_opt,
- new_hops_remaining,
- payload_size,
- direction,
- minimum_undesirability,
- hostname_opt,
- )
- })
- .collect()
+ //TODO testdrive me
+ fn research_exit_contains(research_exits: &Vec>, current_key: &PublicKey) -> bool {
+ for prefix in research_exits {
+ let mut current = Some(Rc::clone(&prefix));
+ while let Some(node) = current {
+ if *current_key == node.public_key {
+ return true;
+ }
+ current = node.previous.as_ref().map(Rc::clone);
+ }
+ }
+ false
+ }
+
+ //TODO testdrive me
+ fn prefix_contains(prefix: &Rc, node_key_searched: &PublicKey) -> bool {
+ let mut current = Some(Rc::clone(&prefix));
+ while let Some(node) = current {
+ if node_key_searched == &node.public_key {
+ return true;
+ }
+ current = node.previous.as_ref().map(Rc::clone);
}
+ false
+ }
+
+ #[allow(clippy::too_many_arguments)]
+ fn routing_guts<'a>(
+ &'a self,
+ first_node_key: &PublicKey,
+ prefix: Rc,
+ target_opt: Option<&'a PublicKey>,
+ hops_remaining: usize,
+ payload_size: usize,
+ direction: RouteDirection,
+ minimum_undesirability: &mut i64,
+ hostname_opt: Option<&str>,
+ research_neighborhood: bool,
+ research_exits: &mut Vec>,
+ previous_node: &NodeRecord,
+ ) -> Vec> {
+ // Go through all the neighbors and compute shorter routes through all the ones we're not already using.
+ let neighbor_nodes = previous_node.full_neighbors(&self.neighborhood_database);
+ let mut result_vector = Vec::with_capacity(neighbor_nodes.len());
+
+ let result_iter = neighbor_nodes
+ .iter()
+ .filter(|node_record| !Self::prefix_contains(&prefix, node_record.public_key())) //TODO make method to travers the elements
+ .filter(|node_record| {
+ node_record.routes_data()
+ || Self::is_orig_node_on_back_leg(**node_record, target_opt, direction)
+ })
+ .flat_map(|node_record| {
+ let new_hops_remaining = if hops_remaining == 0 {
+ 0
+ } else {
+ hops_remaining - 1
+ };
+
+ let new_undesirability = self.compute_new_undesirability(
+ node_record,
+ prefix.undesirability,
+ target_opt,
+ new_hops_remaining,
+ payload_size as u64,
+ direction,
+ hostname_opt,
+ );
+ let new_prefix = Rc::new(RouteElement {
+ previous: Some(Rc::clone(&prefix)),
+ public_key: node_record.public_key().clone(),
+ undesirability: new_undesirability,
+ });
+
+ self.routing_engine(
+ first_node_key,
+ new_prefix,
+ target_opt,
+ new_hops_remaining,
+ payload_size,
+ direction,
+ minimum_undesirability,
+ hostname_opt,
+ research_neighborhood,
+ research_exits,
+ )
+ });
+ result_vector.extend(result_iter);
+ result_vector
}
fn send_ask_about_debut_gossip_message(
@@ -1448,6 +1780,211 @@ impl Neighborhood {
undesirability + node_undesirability
}
+ fn handle_exit_location_message(
+ &mut self,
+ message: UiSetExitLocationRequest,
+ client_id: u64,
+ context_id: u64,
+ ) {
+ let (exit_locations_by_priority, missing_locations) =
+ self.extract_exit_locations_from_message(&message);
+ self.user_exit_preferences.location_preference = match (
+ message.fallback_routing,
+ exit_locations_by_priority.is_empty(),
+ ) {
+ (true, true) => ExitPreference::Nothing,
+ (true, false) => ExitPreference::ExitCountryWithFallback,
+ (false, false) => ExitPreference::ExitCountryNoFallback,
+ (false, true) => ExitPreference::Nothing,
+ };
+ let fallback_status = match self.user_exit_preferences.location_preference {
+ ExitPreference::Nothing => "Fallback Routing is set.",
+ ExitPreference::ExitCountryWithFallback => "Fallback Routing is set.",
+ ExitPreference::ExitCountryNoFallback => "Fallback Routing NOT set.",
+ };
+ self.set_exit_locations_opt(&exit_locations_by_priority);
+ 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
+ );
+ }
+ }
+ false => info!(
+ self.logger,
+ "Neighborhood is empty, no exit Nodes are available.",
+ ),
+ }
+ let message = self.create_exit_location_response(
+ client_id,
+ context_id,
+ missing_locations,
+ message.show_countries,
+ );
+ self.node_to_ui_recipient_opt
+ .as_ref()
+ .expect("UI Gateway is unbound")
+ .try_send(message)
+ .expect("UiGateway is dead");
+ }
+
+ fn create_exit_location_response(
+ &self,
+ client_id: u64,
+ context_id: u64,
+ missing_locations: Vec,
+ show_countries: bool,
+ ) -> NodeToUiMessage {
+ let errmessage = match show_countries {
+ true => match &missing_locations.is_empty() {
+ true => {
+ format!(
+ "Exit Countries: {:?}",
+ self.user_exit_preferences.db_countries
+ )
+ }
+ false => {
+ format!(
+ "Exit Countries: {:?}\nExit Location: following desired countries are missing in Neighborhood {:?}",
+ self.user_exit_preferences.db_countries,
+ missing_locations
+ )
+ }
+ },
+ false => match &missing_locations.is_empty() {
+ true => "".to_string(),
+ false => {
+ format!(
+ "Exit Location: following desired countries are missing in Neighborhood {:?}",
+ missing_locations
+ )
+ }
+ },
+ };
+ match &errmessage.is_empty() {
+ false => NodeToUiMessage {
+ target: MessageTarget::ClientId(client_id),
+ body: MessageBody {
+ opcode: "exit_location".to_string(),
+ path: Conversation(context_id),
+ payload: Err((EXIT_COUNTRY_ERROR, errmessage)),
+ },
+ },
+ true => NodeToUiMessage {
+ target: MessageTarget::ClientId(client_id),
+ body: UiSetExitLocationResponse {}.tmb(context_id),
+ },
+ }
+ }
+
+ 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() {
+ false => Some(exit_locations_by_priority.to_owned()),
+ true => match self.user_exit_preferences.location_preference {
+ ExitPreference::ExitCountryNoFallback => None,
+ _ => Some(exit_locations_by_priority.to_owned()),
+ },
+ };
+ }
+
+ fn set_country_undesirability_and_exit_countries(
+ &mut self,
+ exit_locations_by_priority: &Vec,
+ ) {
+ let nodes = self.neighborhood_database.nodes_mut();
+ match !&exit_locations_by_priority.is_empty() {
+ true => {
+ for node_record in nodes {
+ self.user_exit_preferences
+ .assign_nodes_country_undesirability(node_record)
+ }
+ }
+ false => {
+ self.user_exit_preferences.exit_countries = vec![];
+ for node_record in nodes {
+ node_record.metadata.country_undesirability = 0u32;
+ }
+ }
+ }
+ }
+
+ fn extract_exit_locations_from_message(
+ &mut self,
+ message: &UiSetExitLocationRequest,
+ ) -> (Vec, Vec) {
+ self.user_exit_preferences.db_countries = self.init_db_countries();
+ let mut countries_lack_in_neighborhood = vec![];
+ (
+ message
+ .to_owned()
+ .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.location_preference
+ == ExitPreference::ExitCountryWithFallback
+ {
+ self.user_exit_preferences.exit_countries.push(code.clone());
+ if self.user_exit_preferences.location_preference
+ == ExitPreference::ExitCountryWithFallback
+ {
+ countries_lack_in_neighborhood.push(code.clone());
+ }
+ } else {
+ countries_lack_in_neighborhood.push(code.clone());
+ }
+ }
+ ExitLocation {
+ country_codes: cc.country_codes,
+ priority: cc.priority,
+ }
+ })
+ .collect(),
+ 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;
+ let (exit_locations_db, exit_nodes) = self
+ .find_exit_location(root_key, min_hops, 0usize)
+ .to_owned();
+ let mut db_countries = vec![];
+ match (exit_locations_db.is_empty(), exit_nodes.is_empty()) {
+ (true, true) => {}
+ _ => {
+ for pub_key in exit_nodes {
+ let node_opt = self.neighborhood_database.node_by_key(&pub_key);
+ if let Some(node_record) = node_opt {
+ if let Some(cc) = &node_record.inner.country_code_opt {
+ db_countries.push(cc.clone())
+ }
+ }
+ }
+ }
+ }
+ db_countries.sort();
+ db_countries.dedup();
+ db_countries
+ }
+
fn handle_gossip_reply(
&self,
gossip: Gossip_0v1,
@@ -1610,6 +2147,13 @@ impl Neighborhood {
debug!(self.logger, "The value of min_hops ({}-hop -> {}-hop) and db_patch_size ({} -> {}) has been changed", prev_min_hops, self.min_hops, prev_db_patch_size, self.db_patch_size);
}
+ fn handle_bind_message(&mut self, msg: BindMessage) {
+ self.hopper_opt = Some(msg.peer_actors.hopper.from_hopper_client);
+ self.hopper_no_lookup_opt = Some(msg.peer_actors.hopper.from_hopper_client_no_lookup);
+ self.connected_signal_opt = Some(msg.peer_actors.accountant.start);
+ self.node_to_ui_recipient_opt = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub);
+ }
+
// GH-728
fn handle_new_password(&mut self, new_password: String) {
self.db_password_opt = Some(new_password);
@@ -1636,20 +2180,12 @@ enum UndesirabilityType<'hostname> {
ExitAndRouteResponse,
}
-struct ComputedRouteSegment<'a> {
- pub nodes: Vec<&'a PublicKey>,
+struct RouteElement {
+ pub previous: Option>,
+ pub public_key: PublicKey,
pub undesirability: i64,
}
-impl<'a> ComputedRouteSegment<'a> {
- pub fn new(nodes: Vec<&'a PublicKey>, undesirability: i64) -> Self {
- Self {
- nodes,
- undesirability,
- }
- }
-}
-
#[cfg(test)]
mod tests {
use actix::Recipient;
@@ -1669,7 +2205,9 @@ mod tests {
use tokio::prelude::Future;
use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT};
- use masq_lib::messages::{ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage};
+ use masq_lib::messages::{
+ CountryCodes, 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::MessagePath::Conversation;
@@ -1728,6 +2266,13 @@ mod tests {
};
use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock;
use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler};
+ use masq_lib::ui_gateway::MessageTarget::ClientId;
+
+ impl NeighborhoodDatabase {
+ pub fn set_root_key(&mut self, key: &PublicKey) {
+ self.this_node = key.clone();
+ }
+ }
impl Handler> for Neighborhood {
type Result = ();
@@ -1755,12 +2300,7 @@ mod tests {
vec![make_node_descriptor(make_ip(2))],
rate_pack(100),
);
- let country = "ZZ".to_string();
- let neighborhood_config = NeighborhoodConfig {
- mode,
- min_hops,
- country,
- };
+ let neighborhood_config = NeighborhoodConfig { mode, min_hops };
let subject = Neighborhood::new(
main_cryptde(),
@@ -1777,6 +2317,60 @@ mod tests {
assert_eq!(subject.db_patch_size, expected_db_patch_size);
}
+ // #[test]
+ // fn test_wtih_debut_allways_have_half_neighborship_in_handle_gossip() {
+ // let mut subject = make_standard_subject();
+ // subject.min_hops = Hops::OneHop;
+ // let root_node_key = subject.neighborhood_database.root_key();
+ // let first_neighbor = make_node_record(1111, true);
+ // let mut debut_db = subject.neighborhood_database.clone();
+ // debut_db.add_node(first_neighbor.clone()).unwrap();
+ // debut_db.this_node = first_neighbor.public_key().clone();
+ // debut_db.remove_node(&root_node_key.clone());
+ // let resinging = debut_db.node_by_key_mut(first_neighbor.public_key()).unwrap();
+ // resinging.resign();
+ // let debut = GossipBuilder::new(&debut_db).node(first_neighbor.public_key(), true).build();
+ //
+ // let peer_actors = peer_actors_builder().build();
+ // subject.handle_bind_message(BindMessage { peer_actors });
+ //
+ //
+ // subject.handle_gossip(
+ // debut,
+ // SocketAddr::from_str("1.1.1.1:1111").unwrap(),
+ // make_cpm_recipient().0,
+ // );
+ //
+ // print!("db after: {:?}\n", subject.neighborhood_database.to_dot_graph());
+ // println!("db_countries: {:?}", subject.user_exit_preferences.db_countries);
+ // }
+
+ #[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"
@@ -1792,7 +2386,6 @@ mod tests {
))
.unwrap()]),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
None,
@@ -1818,7 +2411,6 @@ mod tests {
))
.unwrap()]),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
None,
@@ -1840,7 +2432,6 @@ mod tests {
NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
None,
@@ -1870,7 +2461,6 @@ mod tests {
DEFAULT_RATE_PACK.clone(),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
None,
@@ -1910,7 +2500,6 @@ mod tests {
NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -1963,7 +2552,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -2044,7 +2632,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let bootstrap_config =
bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test");
@@ -2514,7 +3101,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let mut subject = Neighborhood::new(
main_cryptde(),
@@ -2592,7 +3178,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
None,
@@ -2636,7 +3221,9 @@ mod tests {
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 400));
+ let future = sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 400,
+ ));
System::current().stop_with_code(0);
system.run();
@@ -2652,8 +3239,10 @@ mod tests {
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 430));
-
+ let future = sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 430,
+ ));
+
System::current().stop_with_code(0);
system.run();
let result = future.wait().unwrap();
@@ -2692,7 +3281,7 @@ mod tests {
}
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let msg = RouteQueryMessage::data_indefinite_route_request(None, 54000);
+ let msg = RouteQueryMessage::data_indefinite_route_request(None, None, 54000);
let future = sub.send(msg);
@@ -2752,7 +3341,7 @@ mod tests {
subject.min_hops = Hops::TwoHops;
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let msg = RouteQueryMessage::data_indefinite_route_request(None, 20000);
+ let msg = RouteQueryMessage::data_indefinite_route_request(None, None, 20000);
let future = sub.send(msg);
@@ -2772,7 +3361,7 @@ mod tests {
let sub: Recipient = addr.recipient::();
let future = sub.send(RouteQueryMessage::data_indefinite_route_request(
- None, 12345,
+ None, None, 12345,
));
System::current().stop_with_code(0);
@@ -2868,7 +3457,9 @@ mod tests {
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 5000));
+ let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 5000,
+ ));
System::current().stop_with_code(0);
system.run();
@@ -2963,8 +3554,12 @@ mod tests {
let addr: Addr = subject.start();
let sub: Recipient = addr.recipient::();
- let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000));
- let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 3000));
+ let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 2000,
+ ));
+ let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 3000,
+ ));
System::current().stop_with_code(0);
system.run();
@@ -3014,15 +3609,17 @@ mod tests {
)
.unwrap();
- let route_request_1 =
- route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 1000));
+ let route_request_1 = route_sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 1000,
+ ));
configuration_change_msg_sub
.try_send(ConfigurationChangeMessage {
change: ConfigurationChange::UpdateConsumingWallet(expected_new_wallet),
})
.unwrap();
- let route_request_2 =
- route_sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000));
+ let route_request_2 = route_sub.send(RouteQueryMessage::data_indefinite_route_request(
+ None, None, 2000,
+ ));
System::current().stop();
system.run();
@@ -3064,6 +3661,584 @@ mod tests {
));
}
+ #[test]
+ fn exit_location_with_multiple_countries_and_priorities_can_be_changed_using_exit_location_msg()
+ {
+ init_test_logging();
+ 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 {
+ country_codes: vec!["CZ".to_string(), "SK".to_string()],
+ priority: 1,
+ },
+ CountryCodes {
+ country_codes: vec!["AT".to_string(), "DE".to_string()],
+ priority: 2,
+ },
+ CountryCodes {
+ country_codes: vec!["PL".to_string()],
+ priority: 3,
+ },
+ ],
+ show_countries: false,
+ };
+ let message = NodeFromUiMessage {
+ client_id: 0,
+ body: request.tmb(0),
+ };
+ let system = System::new(test_name);
+ let (ui_gateway, _, _) = make_recorder();
+ let mut subject = make_standard_subject();
+ subject.logger = Logger::new(test_name);
+ let cz = &mut make_node_record(3456, true);
+ cz.inner.country_code_opt = Some("CZ".to_string());
+ let us = &mut make_node_record(4567, true);
+ us.inner.country_code_opt = Some("US".to_string());
+ let sk = &mut make_node_record(5678, true);
+ sk.inner.country_code_opt = Some("SK".to_string());
+ let de = &mut make_node_record(7777, true);
+ de.inner.country_code_opt = Some("DE".to_string());
+ let at = &mut make_node_record(1325, true);
+ at.inner.country_code_opt = Some("AT".to_string());
+ let pl = &mut make_node_record(2543, true);
+ pl.inner.country_code_opt = Some("PL".to_string());
+ let db = &mut subject.neighborhood_database.clone();
+ db.add_node(cz.clone()).unwrap();
+ db.add_node(de.clone()).unwrap();
+ db.add_node(us.clone()).unwrap();
+ db.add_node(sk.clone()).unwrap();
+ db.add_node(at.clone()).unwrap();
+ db.add_node(pl.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, de);
+ dual_edge(cz, us);
+ dual_edge(us, sk);
+ dual_edge(us, at);
+ dual_edge(at, pl);
+ 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 us_public_key = us.inner.public_key.clone();
+ let sk_public_key = sk.inner.public_key.clone();
+ let de_public_key = de.inner.public_key.clone();
+ let at_public_key = at.inner.public_key.clone();
+ let pl_public_key = pl.inner.public_key.clone();
+ let assertion_msg = AssertionsMessage {
+ assertions: Box::new(move |neighborhood: &mut Neighborhood| {
+ assert_eq!(
+ neighborhood.user_exit_preferences.exit_countries,
+ vec!["SK".to_string(), "AT".to_string(), "PL".to_string(),]
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.location_preference,
+ ExitPreference::ExitCountryWithFallback
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&cz_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "cz We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "us We expecting {}, 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,
+ 0u32,
+ "sk We expecting 0, country is with Priority: 1"
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&de_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "de We expecting {}, 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,
+ 1 * COUNTRY_UNDESIRABILITY_FACTOR,
+ "at We expecting {}, country is with Priority: 2",
+ 1 * COUNTRY_UNDESIRABILITY_FACTOR
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&pl_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 2 * COUNTRY_UNDESIRABILITY_FACTOR,
+ "pl We expecting {}, country is with Priority: 3",
+ 2 * COUNTRY_UNDESIRABILITY_FACTOR
+ );
+ }),
+ };
+ subject_addr.try_send(BindMessage { peer_actors }).unwrap();
+
+ subject_addr.try_send(message).unwrap();
+ subject_addr.try_send(assertion_msg).unwrap();
+
+ System::current().stop();
+ system.run();
+
+ TestLogHandler::new().assert_logs_contain_in_order(vec![
+ &format!(
+ "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;"
+ ]);
+ }
+
+ #[test]
+ fn no_exit_location_is_set_if_desired_country_codes_not_present_in_neighborhood() {
+ init_test_logging();
+ 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 {
+ country_codes: vec!["CZ".to_string(), "SK".to_string(), "IN".to_string()],
+ priority: 1,
+ }],
+ show_countries: true,
+ };
+ let message = NodeFromUiMessage {
+ client_id: 0,
+ body: request.tmb(0),
+ };
+ let system = System::new(test_name);
+ let (ui_gateway, _recorder, arc_recorder) = make_recorder();
+ let mut subject = make_standard_subject();
+ subject.min_hops = Hops::TwoHops;
+ subject.logger = Logger::new(test_name);
+ let es = &mut make_node_record(3456, true);
+ es.inner.country_code_opt = Some("ES".to_string());
+ let us = &mut make_node_record(4567, true);
+ us.inner.country_code_opt = Some("US".to_string());
+ let hu = &mut make_node_record(5678, true);
+ hu.inner.country_code_opt = Some("US".to_string());
+ let de = &mut make_node_record(7777, true);
+ de.inner.country_code_opt = Some("DE".to_string());
+ let at = &mut make_node_record(1325, true);
+ at.inner.country_code_opt = Some("AT".to_string());
+ let pl = &mut make_node_record(2543, true);
+ pl.inner.country_code_opt = Some("PL".to_string());
+ let db = &mut subject.neighborhood_database.clone();
+ db.add_node(es.clone()).unwrap();
+ db.add_node(de.clone()).unwrap();
+ db.add_node(us.clone()).unwrap();
+ db.add_node(hu.clone()).unwrap();
+ db.add_node(at.clone()).unwrap();
+ db.add_node(pl.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(), es);
+ dual_edge(es, de);
+ dual_edge(es, us);
+ dual_edge(us, hu);
+ dual_edge(us, at);
+ dual_edge(at, pl);
+ subject.neighborhood_database = db.clone();
+ let subject_addr = subject.start();
+ let peer_actors = peer_actors_builder().ui_gateway(ui_gateway).build();
+ let es_public_key = es.inner.public_key.clone();
+ let us_public_key = us.inner.public_key.clone();
+ let hu_public_key = hu.inner.public_key.clone();
+ let de_public_key = de.inner.public_key.clone();
+ let at_public_key = at.inner.public_key.clone();
+ 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_eq!(
+ neighborhood.user_exit_preferences.locations_opt,
+ Some(vec![ExitLocation {
+ country_codes: vec!["CZ".to_string(), "SK".to_string(), "IN".to_string()],
+ priority: 1
+ }])
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.db_countries,
+ vec![
+ "AT".to_string(),
+ "DE".to_string(),
+ "PL".to_string(),
+ "US".to_string()
+ ]
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.location_preference,
+ ExitPreference::ExitCountryWithFallback
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&es_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "es We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "us We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "hu We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "de We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "at We expecting {}, 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,
+ UNREACHABLE_COUNTRY_PENALTY,
+ "pl We expecting {}, country is too close to be exit",
+ UNREACHABLE_COUNTRY_PENALTY
+ );
+ }),
+ };
+ subject_addr.try_send(BindMessage { peer_actors }).unwrap();
+
+ subject_addr.try_send(message).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)
+ .body
+ .payload
+ .clone();
+ let log_handler = TestLogHandler::new();
+ assert_eq!(
+ exit_handler_response,
+ Err((
+ 9223372036854775816,
+ "Exit Countries: [\"AT\", \"DE\", \"PL\", \"US\"]\nExit Location: following desired countries are missing in Neighborhood [\"CZ\", \"SK\", \"IN\"]".to_string(),
+ ))
+ );
+ log_handler.assert_logs_contain_in_order(vec![
+ &format!(
+ "INFO: {}: Fallback Routing is set. Exit location set:",
+ test_name
+ ),
+ &"Country Codes: [\"CZ\", \"SK\", \"IN\"] - Priority: 1;",
+ &format!(
+ "WARN: {}: Exit Location: following desired countries are missing in Neighborhood [\"CZ\", \"SK\", \"IN\"]",
+ test_name
+ ),
+ ]);
+ }
+
+ #[test]
+ fn exit_location_is_set_and_unset_with_fallback_routing_using_exit_location_msg() {
+ init_test_logging();
+ let test_name =
+ "exit_location_is_set_and_unset_with_fallback_routing_using_exit_location_msg";
+ let request = UiSetExitLocationRequest {
+ fallback_routing: false,
+ exit_locations: vec![
+ CountryCodes {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1,
+ },
+ CountryCodes {
+ country_codes: vec!["FR".to_string()],
+ priority: 2,
+ },
+ ],
+ show_countries: false,
+ };
+ let set_exit_location_message = NodeFromUiMessage {
+ client_id: 8765,
+ body: request.tmb(1234),
+ };
+ 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, false);
+ let fr = &mut make_node_record(5678, false);
+ fr.inner.country_code_opt = Some("FR".to_string());
+ let t = &make_node_record(7777, false);
+ let db = &mut subject.neighborhood_database.clone();
+ db.add_node(cz.clone()).unwrap();
+ db.add_node(t.clone()).unwrap();
+ db.add_node(r.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);
+ 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 fr_public_key = fr.inner.public_key.clone();
+ let t_public_key = t.inner.public_key.clone();
+ 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,
+ 0u32,
+ "CZ - We expecting zero, country is with Priority: 1"
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&r_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32,
+ "We expecting 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,
+ 1 * COUNTRY_UNDESIRABILITY_FACTOR,
+ "FR - We expecting {}, country is with Priority: 2",
+ 1 * COUNTRY_UNDESIRABILITY_FACTOR
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&t_public_key)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32,
+ "We expecting 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()]
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.location_preference,
+ ExitPreference::ExitCountryNoFallback
+ );
+ }),
+ };
+ 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 fr_public_key_2 = fr.inner.public_key.clone();
+ let t_public_key_2 = t.inner.public_key.clone();
+ 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,
+ 0u32,
+ "We expecting zero, exit_location was unset"
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&r_public_key_2)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32,
+ "We expecting zero, exit_location was unset"
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&fr_public_key_2)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32,
+ "We expecting zero, exit_location was unset"
+ );
+ assert_eq!(
+ neighborhood
+ .neighborhood_database
+ .node_by_key(&t_public_key_2)
+ .unwrap()
+ .metadata
+ .country_undesirability,
+ 0u32,
+ "We expecting zero, exit_location was unset"
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.exit_countries.is_empty(),
+ true
+ );
+ assert_eq!(
+ neighborhood.user_exit_preferences.location_preference,
+ ExitPreference::Nothing
+ )
+ }),
+ };
+
+ subject_addr.try_send(BindMessage { peer_actors }).unwrap();
+ subject_addr.try_send(set_exit_location_message).unwrap();
+ 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)
+ .unwrap();
+
+ System::current().stop();
+ system.run();
+ 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,
+ &NodeToUiMessage {
+ target: ClientId(8765),
+ body: MessageBody {
+ opcode: "exit_location".to_string(),
+ path: Conversation(1234),
+ payload: Err(
+ (9223372036854775816, "Exit Location: following desired countries are missing in Neighborhood [\"CZ\"]".to_string())
+ )
+ }
+ }
+ );
+ assert_eq!(
+ record_two,
+ &NodeToUiMessage {
+ target: MessageTarget::ClientId(6543),
+ body: UiSetExitLocationResponse {}.tmb(7894),
+ }
+ );
+ 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;",
+ test_name
+ ),
+ &format!(
+ "WARN: {}: Exit Location: following desired countries are missing in Neighborhood [\"CZ\"]",
+ test_name
+ ),
+ &format!(
+ "INFO: {}: Fallback Routing is set. Exit location unset.",
+ test_name
+ ),
+ ]);
+ }
+
#[test]
fn min_hops_can_be_changed_during_runtime_using_configuration_change_msg() {
init_test_logging();
@@ -3221,6 +4396,124 @@ 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
+
+ All these Nodes are standard-mode. L is the root Node.
+ */
+ #[test]
+ fn find_exit_location_test() {
+ 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 (_routes, mut exit_nodes) = subject.find_exit_location(&l, 3, 10_000);
+
+ let total_exit_nodes = exit_nodes.len();
+ exit_nodes.sort();
+ exit_nodes.dedup();
+ let dedup_len = exit_nodes.len();
+ assert_eq!(total_exit_nodes, dedup_len);
+ assert_eq!(total_exit_nodes, 20);
+ }
+
+ #[test]
+ fn find_exit_locations_in_row_structure_test() {
+ 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 n1 = make_node(db);
+ let n2 = make_node(db);
+ let n3 = make_node(db);
+ let n4 = make_node(db);
+ let n5 = make_node(db);
+ let f1 = make_node(db);
+ let f2 = make_node(db);
+ let f3 = make_node(db);
+ let f4 = make_node(db);
+ let f5 = 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);
+ db.add_arbitrary_full_neighbor(&n5, &f1);
+ db.add_arbitrary_full_neighbor(&f1, &f2);
+ 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 (_routes, mut exit_nodes) = subject.find_exit_location(&n1, 3, 10_000);
+
+ let total_exit_nodes = exit_nodes.len();
+ exit_nodes.sort();
+ exit_nodes.dedup();
+ let dedup_len = exit_nodes.len();
+ assert_eq!(total_exit_nodes, dedup_len);
+ assert_eq!(total_exit_nodes, 7);
+ }
+
/*
Database:
@@ -3254,14 +4547,16 @@ mod tests {
let route_opt =
subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None);
- assert_eq!(route_opt.unwrap(), vec![p, s, t]);
+ let route_ref_opt = remap_keys_to_reference(route_opt.as_ref());
+ assert_eq!(route_ref_opt.unwrap(), vec![p, s, t]);
// no [p, r, s] or [p, s, r] because s and r are both neighbors of p and can't exit for it
// At least two hops over from p to t
let route_opt =
subject.find_best_route_segment(p, Some(t), 2, 10000, RouteDirection::Over, None);
- assert_eq!(route_opt.unwrap(), vec![p, s, t]);
+ let route_ref_opt = remap_keys_to_reference(route_opt.as_ref());
+ assert_eq!(route_ref_opt.unwrap(), vec![p, s, t]);
// At least two hops over from t to p
let route_opt =
@@ -3274,7 +4569,8 @@ mod tests {
let route_opt =
subject.find_best_route_segment(t, Some(p), 2, 10000, RouteDirection::Back, None);
- assert_eq!(route_opt.unwrap(), vec![t, s, p]);
+ let route_ref_opt = remap_keys_to_reference(route_opt.as_ref());
+ assert_eq!(route_ref_opt.unwrap(), vec![t, s, p]);
// p is consume-only, but it's the originating Node, so including it is okay
// At least two hops from p to Q - impossible
@@ -3284,6 +4580,12 @@ mod tests {
assert_eq!(route_opt, None);
}
+ fn remap_keys_to_reference(route_opt: Option<&Vec>) -> Option> {
+ route_opt.map(|result| {
+ result.iter().collect()
+ })
+ }
+
/*
Database:
@@ -3304,6 +4606,13 @@ mod tests {
fn route_optimization_test() {
let mut subject = make_standard_subject();
let db = &mut subject.neighborhood_database;
+ let (recipient, _) = make_node_to_ui_recipient();
+ subject.node_to_ui_recipient_opt = Some(recipient);
+ let message = UiSetExitLocationRequest {
+ fallback_routing: true,
+ 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();
@@ -3331,11 +4640,9 @@ mod tests {
db.add_arbitrary_full_neighbor(f4, s4);
db.add_arbitrary_full_neighbor(f5, s5);
};
- let designate_root_node = |db: &mut NeighborhoodDatabase, key| {
- let root_node_key = db.root().public_key().clone();
- let node = db.node_by_key(key).unwrap().clone();
- db.root_mut().inner = node.inner.clone();
- db.root_mut().metadata = node.metadata.clone();
+ 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);
@@ -3348,15 +4655,126 @@ 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));
designate_root_node(db, &l);
+ 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)
- .unwrap();
+ .find_best_route_segment(&l, Some(&n), 3, 10000, RouteDirection::Back, None);
let after = Instant::now();
- assert_eq!(route, vec![&l, &g, &h, &i, &n]); // Cheaper than [&l, &q, &r, &s, &n]
+ let route_ref_opt = remap_keys_to_reference(route.as_ref());
+ assert_eq!(route_ref_opt.unwrap(), vec![&l, &g, &h, &i, &n]); // Cheaper than [&l, &q, &r, &s, &n]
+ let interval = after.duration_since(before);
+ assert!(
+ interval.as_millis() <= 100,
+ "Should have calculated route in <=100ms, but was {}ms",
+ interval.as_millis()
+ );
+ }
+
+ /* Complex testing of country_undesirability on large network with aim to find fallback routing and non fallback routing mechanisms
+
+ 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
+
+ All these Nodes are standard-mode. L is the root Node.
+
+ */
+ // TODO drive in the test where C route will be more expensive, than T route, so we have to pick up T exit-node
+ #[test]
+ fn route_optimization_country_codes() {
+ let mut subject = make_standard_subject();
+ let db = &mut subject.neighborhood_database;
+ let (recipient, _) = make_node_to_ui_recipient();
+ subject.node_to_ui_recipient_opt = Some(recipient);
+ let message = UiSetExitLocationRequest {
+ fallback_routing: false,
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1,
+ }],
+ 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();
+ 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));
+
+ 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());
+
+ subject.handle_exit_location_message(message, 0, 0);
+ let before = Instant::now();
+
+ let route_cz =
+ subject.find_best_route_segment(&l, None, 2, 10000, RouteDirection::Over, None);
+
+ let after = Instant::now();
+ let exit_node = checkdb.node_by_key(
+ &route_cz
+ .as_ref()
+ .unwrap()
+ .get(route_cz.as_ref().unwrap().len() - 1)
+ .unwrap(),
+ );
+ assert_eq!(
+ exit_node.unwrap().inner.country_code_opt,
+ Some("CZ".to_string())
+ );
let interval = after.duration_since(before);
assert!(
interval.as_millis() <= 100,
@@ -3373,6 +4791,160 @@ mod tests {
Test is written from the standpoint of P. Node q is non-routing.
*/
+ #[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);
+ subject.user_exit_preferences.location_preference = ExitPreference::ExitCountryWithFallback;
+ let message = UiSetExitLocationRequest {
+ fallback_routing: false,
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["CZ".to_string()],
+ priority: 1,
+ }],
+ 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);
+ subject.handle_exit_location_message(message, 0, 0);
+
+ let route_cz =
+ subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None);
+
+ assert_eq!(route_cz, None);
+ }
+
+ #[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()
+ .public_key()
+ .clone();
+ let a = &subject
+ .neighborhood_database
+ .add_node(make_node_record(2345, true))
+ .unwrap();
+ let b = &subject
+ .neighborhood_database
+ .add_node(make_node_record(5678, true))
+ .unwrap();
+ let c = &subject
+ .neighborhood_database
+ .add_node(make_node_record(1234, true))
+ .unwrap();
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(p, b);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(b, c);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(b, a);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(a, c);
+ let cdb = subject.neighborhood_database.clone();
+ let (recipient, _) = make_node_to_ui_recipient();
+ subject.node_to_ui_recipient_opt = Some(recipient);
+ let message = UiSetExitLocationRequest {
+ fallback_routing: true,
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["AU".to_string()],
+ priority: 1,
+ }],
+ show_countries: false,
+ };
+ subject.handle_exit_location_message(message, 0, 0);
+
+ let route_au =
+ subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None);
+
+ let exit_node = cdb.node_by_key(
+ &route_au
+ .as_ref()
+ .unwrap()
+ .get(route_au.as_ref().unwrap().len() - 1)
+ .unwrap(),
+ );
+ assert_eq!(
+ exit_node.unwrap().inner.country_code_opt,
+ Some("AU".to_string())
+ );
+ }
+
+ #[test]
+ fn route_for_fr_country_code_is_constructed_without_fallback_routing() {
+ let mut subject = make_standard_subject();
+ let p = &subject
+ .neighborhood_database
+ .root_mut()
+ .public_key()
+ .clone();
+ let a = &subject
+ .neighborhood_database
+ .add_node(make_node_record(2345, true))
+ .unwrap();
+ let b = &subject
+ .neighborhood_database
+ .add_node(make_node_record(5678, true))
+ .unwrap();
+ let c = &subject
+ .neighborhood_database
+ .add_node(make_node_record(1234, true))
+ .unwrap();
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(p, b);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(b, c);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(b, a);
+ subject
+ .neighborhood_database
+ .add_arbitrary_full_neighbor(a, c);
+ let cdb = subject.neighborhood_database.clone();
+ let (recipient, _) = make_node_to_ui_recipient();
+ subject.node_to_ui_recipient_opt = Some(recipient);
+ let message = UiSetExitLocationRequest {
+ fallback_routing: false,
+ exit_locations: vec![CountryCodes {
+ country_codes: vec!["FR".to_string()],
+ priority: 1,
+ }],
+ show_countries: false,
+ };
+ subject.handle_exit_location_message(message, 0, 0);
+
+ 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()
+ .get(route_fr.as_ref().unwrap().len() - 1)
+ .unwrap(),
+ );
+ assert_eq!(
+ exit_node.unwrap().inner.country_code_opt,
+ Some("FR".to_string())
+ );
+ }
+
#[test]
fn cant_route_through_non_routing_node() {
let mut subject = make_standard_subject();
@@ -3575,7 +5147,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -3976,7 +5547,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let bootstrap_config =
bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test");
@@ -4373,6 +5943,59 @@ 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 new_neighbor = make_node_record(2345, false);
+ 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.this_node = first_key.clone();
+ 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();
@@ -4722,7 +6345,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
this_node_inside.earning_wallet(),
None,
@@ -4786,7 +6408,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
NodeRecord::earning_wallet_from_key(&cryptde.public_key()),
NodeRecord::consuming_wallet_from_key(&cryptde.public_key()),
@@ -4844,7 +6465,6 @@ mod tests {
rate_pack(100),
),
min_hops: min_hops_in_neighborhood,
- country: "ZZ".to_string(),
},
make_wallet("earning"),
None,
@@ -4888,7 +6508,6 @@ mod tests {
rate_pack(100),
),
min_hops: min_hops_in_neighborhood,
- country: "ZZ".to_string(),
},
make_wallet("earning"),
None,
@@ -4982,6 +6601,7 @@ mod tests {
return_component_opt: None,
payload_size: 10000,
hostname_opt: None,
+ target_country_opt: None,
};
let unsuccessful_three_hop_route = addr.send(three_hop_route_request);
let asserted_node_record = a.clone();
@@ -5079,7 +6699,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string()
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -5142,7 +6761,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string()
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -5210,7 +6828,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string()
},
earning_wallet.clone(),
consuming_wallet.clone(),
@@ -5275,7 +6892,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string()
},
node_record.earning_wallet(),
None,
@@ -5352,6 +6968,7 @@ mod tests {
return_component_opt: Some(Component::ProxyServer),
payload_size: 10000,
hostname_opt: None,
+ target_country_opt: None,
});
assert_eq!(
@@ -5397,6 +7014,7 @@ mod tests {
return_component_opt: Some(Component::ProxyServer),
payload_size: 10000,
hostname_opt: None,
+ target_country_opt: None,
});
let next_door_neighbor_cryptde =
@@ -5451,6 +7069,7 @@ mod tests {
return_component_opt: Some(Component::ProxyServer),
payload_size: 10000,
hostname_opt: None,
+ target_country_opt: None,
});
let assert_hops = |cryptdes: Vec, route: &[CryptData]| {
@@ -5552,6 +7171,7 @@ mod tests {
return_component_opt: Some(Component::ProxyServer),
payload_size,
hostname_opt: None,
+ target_country_opt: None,
})
.unwrap();
@@ -5788,7 +7408,6 @@ mod tests {
NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
make_wallet("earning"),
None,
@@ -6159,7 +7778,6 @@ mod tests {
rate_pack(100),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
};
let bootstrap_config =
bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, test_name);
@@ -6184,7 +7802,6 @@ mod tests {
NeighborhoodConfig {
mode: NeighborhoodMode::ConsumeOnly(vec![make_node_descriptor(make_ip(1))]),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
make_wallet("earning"),
None,
diff --git a/node/src/neighborhood/neighborhood_database.rs b/node/src/neighborhood/neighborhood_database.rs
index acd6ee99c..176e41513 100644
--- a/node/src/neighborhood/neighborhood_database.rs
+++ b/node/src/neighborhood/neighborhood_database.rs
@@ -25,7 +25,7 @@ pub const ISOLATED_NODE_GRACE_PERIOD_SECS: u32 = 30;
#[derive(Clone)]
pub struct NeighborhoodDatabase {
- this_node: PublicKey,
+ pub this_node: PublicKey,
by_public_key: HashMap,
by_ip_addr: HashMap,
logger: Logger,
@@ -77,6 +77,10 @@ impl NeighborhoodDatabase {
self.node_by_key(&self.this_node).expect("Internal error")
}
+ pub fn root_key(&self) -> &PublicKey {
+ &self.this_node
+ }
+
pub fn root_mut(&mut self) -> &mut NodeRecord {
let root_key = &self.this_node.clone();
self.node_by_key_mut(root_key).expect("Internal error")
@@ -94,6 +98,13 @@ impl NeighborhoodDatabase {
self.by_public_key.get_mut(public_key)
}
+ pub fn nodes_mut(&mut self) -> Vec<&mut NodeRecord> {
+ self.by_public_key
+ .iter_mut()
+ .map(|(_key, node_record)| node_record)
+ .collect()
+ }
+
pub fn node_by_ip(&self, ip_addr: &IpAddr) -> Option<&NodeRecord> {
match self.by_ip_addr.get(ip_addr) {
Some(key) => self.node_by_key(key),
@@ -288,11 +299,16 @@ impl NeighborhoodDatabase {
to: k.clone(),
})
});
+ let country_code = match &nr.inner.country_code_opt {
+ Some(cc) => cc.clone(),
+ None => "ZZ".to_string(),
+ };
node_renderables.push(NodeRenderable {
inner: Some(NodeRenderableInner {
version: nr.version(),
accepts_connections: nr.accepts_connections(),
routes_data: nr.routes_data(),
+ country_code
}),
public_key: public_key.clone(),
node_addr: nr.node_addr_opt(),
@@ -370,7 +386,9 @@ mod tests {
use crate::sub_lib::cryptde_null::CryptDENull;
use crate::sub_lib::utils::time_t_timestamp;
use crate::test_utils::assert_string_contains;
- use crate::test_utils::neighborhood_test_utils::{db_from_node, make_node_record};
+ use crate::test_utils::neighborhood_test_utils::{
+ db_from_node, make_node_record, make_segmented_ip, make_segments,
+ };
use masq_lib::constants::DEFAULT_CHAIN;
use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN;
use std::iter::FromIterator;
@@ -561,6 +579,50 @@ mod tests {
);
}
+ #[test]
+ fn nodes_mut_works() {
+ let root_node = make_node_record(1234, true);
+ let node_a = make_node_record(2345, false);
+ let node_b = make_node_record(3456, true);
+ let mut subject = NeighborhoodDatabase::new(
+ root_node.public_key(),
+ (&root_node).into(),
+ Wallet::from_str("0x0000000000000000000000000000000000004444").unwrap(),
+ &CryptDENull::from(root_node.public_key(), TEST_DEFAULT_CHAIN),
+ );
+ subject.add_node(node_a.clone()).unwrap();
+ subject.add_node(node_b.clone()).unwrap();
+ let mut iterator: 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);
+ node.metadata.node_addr_opt = Some(NodeAddr::new(
+ &make_segmented_ip(seg1, seg2, seg3, seg4),
+ &[iterator],
+ ));
+ keys_nums.push((node.inner.public_key.clone(), iterator));
+ iterator += 1;
+ }
+
+ for (pub_key, num) in keys_nums {
+ let (seg1, seg2, seg3, seg4) = make_segments(num);
+ assert_eq!(
+ &subject
+ .node_by_key(&pub_key)
+ .unwrap()
+ .clone()
+ .metadata
+ .node_addr_opt,
+ &Some(NodeAddr::new(
+ &make_segmented_ip(seg1, seg2, seg3, seg4),
+ &[num]
+ ))
+ );
+ }
+ }
+
#[test]
fn add_half_neighbor_works() {
let this_node = make_node_record(1234, true);
diff --git a/node/src/neighborhood/node_record.rs b/node/src/neighborhood/node_record.rs
index 2a944f138..a84349e8f 100644
--- a/node/src/neighborhood/node_record.rs
+++ b/node/src/neighborhood/node_record.rs
@@ -347,7 +347,7 @@ pub struct NodeRecordMetadata {
pub node_addr_opt: Option,
pub unreachable_hosts: HashSet,
pub node_location_opt: Option,
- pub node_distrust_score: u32,
+ pub country_undesirability: u32,
//TODO introduce various scores for latency, reliability and so
}
@@ -358,7 +358,7 @@ impl NodeRecordMetadata {
node_addr_opt: None,
unreachable_hosts: Default::default(),
node_location_opt: None,
- node_distrust_score: Default::default(),
+ country_undesirability: 0u32,
}
}
}
diff --git a/node/src/node_configurator/mod.rs b/node/src/node_configurator/mod.rs
index a45a2969a..135d3b20f 100644
--- a/node/src/node_configurator/mod.rs
+++ b/node/src/node_configurator/mod.rs
@@ -305,7 +305,7 @@ pub trait DirsWrapper: Send {
fn dup(&self) -> Box; // because implementing Clone for traits is problematic.
}
-pub struct DirsWrapperReal;
+pub struct DirsWrapperReal {}
impl DirsWrapper for DirsWrapperReal {
fn data_dir(&self) -> Option {
@@ -315,7 +315,19 @@ impl DirsWrapper for DirsWrapperReal {
home_dir()
}
fn dup(&self) -> Box {
- Box::new(DirsWrapperReal)
+ Box::new(DirsWrapperReal::default())
+ }
+}
+
+impl DirsWrapperReal {
+ pub fn new() -> Self {
+ Self {}
+ }
+}
+
+impl Default for DirsWrapperReal {
+ fn default() -> Self {
+ DirsWrapperReal::new()
}
}
@@ -348,7 +360,7 @@ mod tests {
"/nonexistent_home/nonexistent_alice".to_string(),
)),
);
- let chain_name = "polygon-mumbai";
+ let chain_name = "polygon-amoy";
let result =
data_directory_from_context(&dirs_wrapper, &real_user, Chain::from(chain_name));
@@ -356,7 +368,7 @@ mod tests {
assert_eq!(
result,
PathBuf::from(
- "/nonexistent_home/nonexistent_alice/.local/share/MASQ/polygon-mumbai".to_string()
+ "/nonexistent_home/nonexistent_alice/.local/share/MASQ/polygon-amoy".to_string()
)
)
}
@@ -377,7 +389,8 @@ mod tests {
let args_vec: Vec = args.into();
let app = determine_config_file_path_app();
let user_specific_data =
- determine_user_specific_data(&DirsWrapperReal {}, &app, args_vec.as_slice()).unwrap();
+ determine_user_specific_data(&DirsWrapperReal::default(), &app, args_vec.as_slice())
+ .unwrap();
assert_eq!(
&format!(
@@ -418,7 +431,8 @@ mod tests {
std::env::set_var("MASQ_CONFIG_FILE", "booga.toml");
let app = determine_config_file_path_app();
let user_specific_data =
- determine_user_specific_data(&DirsWrapperReal {}, &app, args_vec.as_slice()).unwrap();
+ determine_user_specific_data(&DirsWrapperReal::default(), &app, args_vec.as_slice())
+ .unwrap();
assert_eq!(
format!(
"{}",
@@ -454,7 +468,8 @@ mod tests {
let app = determine_config_file_path_app();
let user_specific_data =
- determine_user_specific_data(&DirsWrapperReal {}, &app, args_vec.as_slice()).unwrap();
+ determine_user_specific_data(&DirsWrapperReal::default(), &app, args_vec.as_slice())
+ .unwrap();
assert_eq!(
"/tmp/booga.toml",
@@ -474,7 +489,7 @@ mod tests {
let args_vec: Vec = args.into();
let user_specific_data = determine_user_specific_data(
- &DirsWrapperReal {},
+ &DirsWrapperReal::default(),
&determine_config_file_path_app(),
args_vec.as_slice(),
)
@@ -498,7 +513,7 @@ mod tests {
let args_vec: Vec = args.into();
let user_specific_data = determine_user_specific_data(
- &DirsWrapperReal {},
+ &DirsWrapperReal::default(),
&determine_config_file_path_app(),
args_vec.as_slice(),
)
@@ -521,7 +536,7 @@ mod tests {
let args_vec: Vec = args.into();
let user_specific_data = determine_user_specific_data(
- &DirsWrapperReal {},
+ &DirsWrapperReal::default(),
&determine_config_file_path_app(),
args_vec.as_slice(),
)
@@ -545,7 +560,7 @@ mod tests {
let args_vec: Vec = args.into();
let user_specific_data = determine_user_specific_data(
- &DirsWrapperReal {},
+ &DirsWrapperReal::default(),
&determine_config_file_path_app(),
args_vec.as_slice(),
)
diff --git a/node/src/node_configurator/node_configurator_standard.rs b/node/src/node_configurator/node_configurator_standard.rs
index 49cb35703..97f0da35e 100644
--- a/node/src/node_configurator/node_configurator_standard.rs
+++ b/node/src/node_configurator/node_configurator_standard.rs
@@ -63,7 +63,7 @@ impl Default for NodeConfiguratorStandardPrivileged {
impl NodeConfiguratorStandardPrivileged {
pub fn new() -> Self {
Self {
- dirs_wrapper: Box::new(DirsWrapperReal {}),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
}
}
}
@@ -375,8 +375,10 @@ mod tests {
use crate::test_utils::unshared_test_utils::{
make_pre_populated_mocked_directory_wrapper, make_simplified_multi_config,
};
- use crate::test_utils::{assert_string_contains, main_cryptde, ArgsBuilder};
- use dirs::home_dir;
+ use crate::test_utils::{
+ assert_string_contains, main_cryptde,
+ make_node_base_dir_and_return_its_absolute_and_relative_path_to_os_home_dir, ArgsBuilder,
+ };
use masq_lib::blockchains::chains::Chain;
use masq_lib::constants::DEFAULT_CHAIN;
use masq_lib::multi_config::VirtualCommandLine;
@@ -597,8 +599,12 @@ mod tests {
)
.unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut bootstrapper_config)
- .unwrap();
+ privileged_parse_args(
+ &DirsWrapperReal::default(),
+ &multi_config,
+ &mut bootstrapper_config,
+ )
+ .unwrap();
let node_parse_args_configurator = UnprivilegedParseArgsConfigurationDaoNull {};
node_parse_args_configurator
.unprivileged_parse_args(
@@ -660,13 +666,13 @@ mod tests {
"ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01ABCDEF01",
)
.param("--real-user", "999:999:/home/booga")
- .param("--chain", "polygon-mumbai");
+ .param("--chain", "polygon-amoy");
let mut config = BootstrapperConfig::new();
let vcls: Vec> =
vec![Box::new(CommandLineVcl::new(args.into()))];
let multi_config = make_new_multi_config(&app_node(), vcls).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
assert_eq!(
value_m!(multi_config, "config-file", PathBuf),
@@ -685,7 +691,6 @@ mod tests {
NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop, // not populated on the privileged side
min_hops: Hops::ThreeHops,
- country: "ZZ".to_string()
}
);
assert_eq!(
@@ -712,7 +717,7 @@ mod tests {
vec![Box::new(CommandLineVcl::new(args.into()))];
let multi_config = make_new_multi_config(&app_node(), vcls).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
assert_eq!(None, value_m!(multi_config, "config-file", PathBuf));
assert_eq!(
@@ -724,7 +729,7 @@ mod tests {
assert!(config.main_cryptde_null_opt.is_none());
assert_eq!(
config.real_user,
- RealUser::new(None, None, None).populate(&DirsWrapperReal {})
+ RealUser::new(None, None, None).populate(&DirsWrapperReal::default())
);
}
@@ -740,7 +745,7 @@ mod tests {
vec![Box::new(CommandLineVcl::new(args.into()))];
let multi_config = make_new_multi_config(&app_node(), vcls).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
#[cfg(target_os = "linux")]
assert_eq!(
@@ -766,7 +771,7 @@ mod tests {
vec![Box::new(CommandLineVcl::new(args.into()))];
let multi_config = make_new_multi_config(&app_node(), vcls).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
assert_eq!(None, value_m!(multi_config, "config-file", PathBuf));
assert_eq!(
@@ -778,7 +783,7 @@ mod tests {
assert!(config.main_cryptde_null_opt.is_none());
assert_eq!(
config.real_user,
- RealUser::new(None, None, None).populate(&DirsWrapperReal {})
+ RealUser::new(None, None, None).populate(&DirsWrapperReal::default())
);
}
@@ -790,7 +795,7 @@ mod tests {
let vcl = Box::new(CommandLineVcl::new(args.into()));
let multi_config = make_new_multi_config(&app_node(), vec![vcl]).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
assert_eq!(config.crash_point, CrashPoint::None);
}
@@ -803,7 +808,7 @@ mod tests {
let vcl = Box::new(CommandLineVcl::new(args.into()));
let multi_config = make_new_multi_config(&app_node(), vec![vcl]).unwrap();
- privileged_parse_args(&DirsWrapperReal {}, &multi_config, &mut config).unwrap();
+ privileged_parse_args(&DirsWrapperReal::default(), &multi_config, &mut config).unwrap();
assert_eq!(config.crash_point, CrashPoint::Panic);
}
@@ -1021,8 +1026,8 @@ mod tests {
);
let home_dir = canonicalize(home_dir).unwrap();
let data_dir = &home_dir.join("data_dir");
- let config_file_relative = File::create(home_dir.join("config.toml")).unwrap();
- fill_up_config_file(config_file_relative);
+ let config_file = File::create(home_dir.join("config.toml")).unwrap();
+ fill_up_config_file(config_file);
let env_vec_array = vec![
(
"MASQ_CONFIG_FILE",
@@ -1072,16 +1077,26 @@ mod tests {
}
#[test]
- fn server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file(
- ) {
+ fn tilde_in_config_file_path_from_commandline_and_args_uploaded_from_config_file() {
+ //server_initializer_collected_params_handle_tilde_in_path_config_file_from_commandline_and_real_user_from_config_file
running_test();
let _guard = EnvironmentGuard::new();
let _clap_guard = ClapGuard::new();
- let home_dir = home_dir().expect("expectexd home dir");
- let data_dir = &home_dir.join("masqhome");
- let _create_data_dir = create_dir_all(data_dir);
- let config_file_relative = File::create(data_dir.join("config.toml")).unwrap();
- fill_up_config_file(config_file_relative);
+ let (node_home_dir, node_home_dir_relative_to_os_home_dir) =
+ make_node_base_dir_and_return_its_absolute_and_relative_path_to_os_home_dir(
+ "node_configurator_standard",
+ "tilde_in_config_file_path_from_commandline_and_real_user_from_config_file",
+ );
+ let data_dir = &node_home_dir.join("data_dir");
+ let node_data_dir_relative_to_os_home_dir_str = node_home_dir_relative_to_os_home_dir
+ .join("data_dir")
+ .as_os_str()
+ .to_str()
+ .unwrap()
+ .to_string();
+ create_dir_all(data_dir).unwrap();
+ let config_file = File::create(data_dir.join("config.toml")).unwrap();
+ fill_up_config_file(config_file);
let env_vec_array = vec![
("MASQ_BLOCKCHAIN_SERVICE_URL", "https://www.mainnet2.com"),
#[cfg(not(target_os = "windows"))]
@@ -1094,24 +1109,40 @@ mod tests {
#[cfg(not(target_os = "windows"))]
let args = ArgsBuilder::new()
.param("--blockchain-service-url", "https://www.mainnet1.com")
- .param("--config-file", "~/masqhome/config.toml")
- .param("--data-directory", "~/masqhome");
+ .param(
+ "--config-file",
+ &format!(
+ "~/{}/config.toml",
+ node_data_dir_relative_to_os_home_dir_str
+ ),
+ )
+ .param(
+ "--data-directory",
+ &format!("~/{}", node_data_dir_relative_to_os_home_dir_str),
+ );
#[cfg(target_os = "windows")]
let args = ArgsBuilder::new()
.param("--blockchain-service-url", "https://www.mainnet1.com")
- .param("--config-file", "~\\masqhome\\config.toml")
- .param("--data-directory", "~\\masqhome");
+ .param(
+ "--config-file",
+ &format!(
+ "~\\{}\\config.toml",
+ node_data_dir_relative_to_os_home_dir_str
+ ),
+ )
+ .param(
+ "--data-directory",
+ &format!("~\\{}", node_data_dir_relative_to_os_home_dir_str),
+ );
let args_vec: Vec = args.into();
- let dir_wrapper = DirsWrapperMock::new()
- .home_dir_result(Some(home_dir.to_path_buf()))
- .data_dir_result(Some(data_dir.to_path_buf()));
- let result = server_initializer_collected_params(&dir_wrapper, args_vec.as_slice());
- let multiconfig = result.unwrap();
+ let multiconfig =
+ server_initializer_collected_params(&DirsWrapperReal::default(), args_vec.as_slice())
+ .unwrap();
assert_eq!(
value_m!(multiconfig, "data-directory", String).unwrap(),
- data_dir.to_string_lossy().to_string()
+ data_dir.as_os_str().to_str().unwrap()
);
#[cfg(not(target_os = "windows"))]
{
@@ -1136,12 +1167,11 @@ mod tests {
}
#[test]
- fn server_initializer_collected_params_handle_config_file_from_environment_and_real_user_from_config_file_with_data_directory(
- ) {
+ fn config_file_from_env_and_real_user_from_config_file_with_data_directory_from_command_line() {
running_test();
let _guard = EnvironmentGuard::new();
let _clap_guard = ClapGuard::new();
- let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","server_initializer_collected_params_handle_config_file_from_environment_and_real_user_from_config_file_with_data_directory");
+ let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","config_file_from_env_and_real_user_from_config_file_with_data_directory_from_command_line");
let data_dir = &home_dir.join("data_dir");
create_dir_all(home_dir.join("config")).expect("expected directory for config");
let config_file_relative = File::create(&home_dir.join("config/config.toml")).unwrap();
@@ -1186,7 +1216,6 @@ mod tests {
let _guard = EnvironmentGuard::new();
let _clap_guard = ClapGuard::new();
let home_dir = ensure_node_home_directory_exists( "node_configurator_standard","server_initializer_collected_params_fails_on_naked_dir_config_file_without_data_directory");
-
let data_dir = &home_dir.join("data_dir");
vec![("MASQ_CONFIG_FILE", "config/config.toml")]
.into_iter()
@@ -1205,15 +1234,13 @@ mod tests {
running_test();
let _guard = EnvironmentGuard::new();
let _clap_guard = ClapGuard::new();
- let home_dir = ensure_node_home_directory_exists(
+ let node_home_dir = ensure_node_home_directory_exists(
"node_configurator_standard",
"server_initializer_collected_params_combine_vcls_properly",
);
- let data_dir = &home_dir.join("data_dir");
- let config_file = File::create(&home_dir.join("booga.toml")).unwrap();
- let current_directory = current_dir().unwrap();
+ let data_dir = &node_home_dir.join("data_dir");
+ let config_file = File::create(&node_home_dir.join("booga.toml")).unwrap();
fill_up_config_file(config_file);
-
let env_vec_array = vec![
("MASQ_CONFIG_FILE", "booga.toml"),
("MASQ_CLANDESTINE_PORT", "8888"),
@@ -1227,10 +1254,13 @@ mod tests {
.into_iter()
.for_each(|(name, value)| std::env::set_var(name, value));
let dir_wrapper = DirsWrapperMock::new()
- .home_dir_result(Some(home_dir.clone()))
+ .home_dir_result(Some(node_home_dir.clone()))
.data_dir_result(Some(data_dir.to_path_buf()));
let args = ArgsBuilder::new()
- .param("--data-directory", current_directory.join(Path::new("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vcls_properly/home")).to_string_lossy().to_string().as_str())
+ .param(
+ "--data-directory",
+ node_home_dir.to_string_lossy().to_string().as_str(),
+ )
.param("--clandestine-port", "1111")
.param("--real-user", "1001:1001:cooga");
let args_vec: Vec = args.into();
@@ -1254,7 +1284,11 @@ mod tests {
{
assert_eq!(
value_m!(multiconfig, "config-file", String).unwrap(),
- current_directory.join("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vcls_properly/home/booga.toml").to_string_lossy().to_string()
+ node_home_dir
+ .join("booga.toml")
+ .as_os_str()
+ .to_str()
+ .unwrap()
);
assert_eq!(
value_m!(multiconfig, "real-user", String).unwrap(),
@@ -1264,7 +1298,7 @@ mod tests {
#[cfg(target_os = "windows")]
assert_eq!(
value_m!(multiconfig, "config-file", String).unwrap(),
- current_directory.join("generated/test/node_configurator_standard/server_initializer_collected_params_combine_vcls_properly/home\\booga.toml").to_string_lossy().to_string()
+ node_home_dir.to_string_lossy().to_string()
);
}
@@ -1612,30 +1646,30 @@ mod tests {
running_test();
let home_dir = Path::new("/home/cooga");
let home_dir_poly_main = home_dir.join(".local").join("MASQ").join("polygon-mainnet");
- let home_dir_poly_mumbai = home_dir.join(".local").join("MASQ").join("polygon-mumbai");
+ let home_dir_poly_amoy = home_dir.join(".local").join("MASQ").join("polygon-amoy");
vec![
(None, None, Some(home_dir_poly_main.to_str().unwrap())),
(
- Some("polygon-mumbai"),
+ Some("polygon-amoy"),
None,
- Some(home_dir_poly_mumbai.to_str().unwrap()),
+ Some(home_dir_poly_amoy.to_str().unwrap()),
),
(None, Some("/cooga"), Some("/cooga")),
- (Some("polygon-mumbai"), Some("/cooga"), Some("/cooga")),
+ (Some("polygon-amoy"), Some("/cooga"), Some("/cooga")),
(
None,
- Some("/cooga/polygon-mumbai"),
- Some("/cooga/polygon-mumbai"),
+ Some("/cooga/polygon-amoy"),
+ Some("/cooga/polygon-amoy"),
),
(
None,
- Some("/cooga/polygon-mumbai/polygon-mainnet"),
- Some("/cooga/polygon-mumbai/polygon-mainnet"),
+ Some("/cooga/polygon-amoy/polygon-mainnet"),
+ Some("/cooga/polygon-amoy/polygon-mainnet"),
),
(
- Some("polygon-mumbai"),
- Some("/cooga/polygon-mumbai"),
- Some("/cooga/polygon-mumbai"),
+ Some("polygon-amoy"),
+ Some("/cooga/polygon-amoy"),
+ Some("/cooga/polygon-amoy"),
),
]
.iter()
diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs
index c05cb971c..91b0ca81f 100644
--- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs
+++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs
@@ -222,11 +222,7 @@ pub fn make_neighborhood_config(
};
match make_neighborhood_mode(multi_config, neighbor_configs, persistent_config) {
- Ok(mode) => Ok(NeighborhoodConfig {
- mode,
- min_hops,
- country: "ZZ".to_string(),
- }),
+ Ok(mode) => Ok(NeighborhoodConfig { mode, min_hops }),
Err(e) => Err(e),
}
}
@@ -722,7 +718,7 @@ mod tests {
DEFAULT_RATE_PACK
),
min_hops: Hops::OneHop,
- country: "ZZ".to_string()
+
})
);
}
@@ -858,7 +854,7 @@ mod tests {
Ok(NeighborhoodConfig {
mode: NeighborhoodMode::Standard(node_addr, _, _),
min_hops: Hops::ThreeHops,
- country: _,
+ ..
}) => node_addr,
x => panic!("Wasn't expecting {:?}", x),
};
diff --git a/node/src/privilege_drop.rs b/node/src/privilege_drop.rs
index 1eba9cf88..2b4d8e198 100644
--- a/node/src/privilege_drop.rs
+++ b/node/src/privilege_drop.rs
@@ -190,7 +190,9 @@ mod tests {
let mut subject = PrivilegeDropperReal::new();
subject.id_wrapper = Box::new(id_wrapper);
- subject.drop_privileges(&RealUser::new(None, None, None).populate(&DirsWrapperReal {}));
+ subject.drop_privileges(
+ &RealUser::new(None, None, None).populate(&DirsWrapperReal::default()),
+ );
}
#[cfg(not(target_os = "windows"))]
@@ -283,7 +285,9 @@ mod tests {
let mut subject = PrivilegeDropperReal::new();
subject.id_wrapper = Box::new(id_wrapper);
- subject.drop_privileges(&RealUser::new(None, None, None).populate(&DirsWrapperReal {}));
+ subject.drop_privileges(
+ &RealUser::new(None, None, None).populate(&DirsWrapperReal::default()),
+ );
let setuid_params = setuid_params_arc.lock().unwrap();
assert!(setuid_params.is_empty());
diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs
index 058c7c12f..a08985f05 100644
--- a/node/src/proxy_server/mod.rs
+++ b/node/src/proxy_server/mod.rs
@@ -1123,6 +1123,7 @@ impl IBCDHelper for IBCDHelperReal {
neighborhood_sub
.send(RouteQueryMessage::data_indefinite_route_request(
hostname_opt,
+ None,
payload_size,
))
.then(move |route_result| {
@@ -1581,7 +1582,11 @@ mod tests {
let record = recording.get_record::(0);
assert_eq!(
record,
- &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47)
+ &RouteQueryMessage::data_indefinite_route_request(
+ Some("nowhere.com".to_string()),
+ None,
+ 47
+ )
);
let recording = proxy_server_recording_arc.lock().unwrap();
assert_eq!(recording.len(), 0);
@@ -1703,6 +1708,7 @@ mod tests {
neighborhood_record,
&RouteQueryMessage::data_indefinite_route_request(
Some("realdomain.nu".to_string()),
+ None,
12
)
);
@@ -2095,7 +2101,8 @@ mod tests {
target_component: Component::ProxyClient,
return_component_opt: Some(Component::ProxyServer),
payload_size: 47,
- hostname_opt: Some("nowhere.com".to_string())
+ hostname_opt: Some("nowhere.com".to_string()),
+ target_country_opt: None,
}
);
let dispatcher_recording = dispatcher_log_arc.lock().unwrap();
@@ -2174,7 +2181,8 @@ mod tests {
target_component: Component::ProxyClient,
return_component_opt: Some(Component::ProxyServer),
payload_size: 16,
- hostname_opt: None
+ hostname_opt: None,
+ target_country_opt: None,
}
);
let dispatcher_recording = dispatcher_log_arc.lock().unwrap();
@@ -2402,7 +2410,11 @@ mod tests {
let record = recording.get_record::(0);
assert_eq!(
record,
- &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47)
+ &RouteQueryMessage::data_indefinite_route_request(
+ Some("nowhere.com".to_string()),
+ None,
+ 47
+ )
);
}
@@ -3006,7 +3018,11 @@ mod tests {
let record = recording.get_record::(0);
assert_eq!(
record,
- &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47)
+ &RouteQueryMessage::data_indefinite_route_request(
+ Some("nowhere.com".to_string()),
+ None,
+ 47
+ )
);
TestLogHandler::new().exists_log_containing(&format!(
"WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com"
@@ -3182,7 +3198,11 @@ mod tests {
let record = recording.get_record::(0);
assert_eq!(
record,
- &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47)
+ &RouteQueryMessage::data_indefinite_route_request(
+ Some("nowhere.com".to_string()),
+ None,
+ 47
+ )
);
TestLogHandler::new().exists_log_containing(&format!(
"WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com"
diff --git a/node/src/run_modes_factories.rs b/node/src/run_modes_factories.rs
index 61a3294ba..c3c009438 100644
--- a/node/src/run_modes_factories.rs
+++ b/node/src/run_modes_factories.rs
@@ -82,7 +82,7 @@ pub trait DaemonInitializer {
impl DumpConfigRunnerFactory for DumpConfigRunnerFactoryReal {
fn make(&self) -> Box {
Box::new(DumpConfigRunnerReal {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
})
}
}
@@ -111,7 +111,7 @@ impl DaemonInitializerFactory for DaemonInitializerFactoryReal {
impl Default for DIClusteredParams {
fn default() -> Self {
Self {
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
logger_initializer_wrapper: Box::new(LoggerInitializerWrapperReal),
channel_factory: Box::new(ChannelFactoryReal::new()),
recipients_factory: Box::new(RecipientsFactoryReal::new()),
diff --git a/node/src/server_initializer.rs b/node/src/server_initializer.rs
index 048e8d6fa..36ba6c17c 100644
--- a/node/src/server_initializer.rs
+++ b/node/src/server_initializer.rs
@@ -94,7 +94,7 @@ impl Default for ServerInitializerReal {
dns_socket_server: Box::new(DnsSocketServer::new()),
bootstrapper: Box::new(Bootstrapper::new(Box::new(LoggerInitializerWrapperReal {}))),
privilege_dropper: Box::new(PrivilegeDropperReal::new()),
- dirs_wrapper: Box::new(DirsWrapperReal),
+ dirs_wrapper: Box::new(DirsWrapperReal::default()),
}
}
}
@@ -394,14 +394,13 @@ pub mod tests {
use masq_lib::crash_point::CrashPoint;
use masq_lib::multi_config::MultiConfig;
use masq_lib::shared_schema::{ConfiguratorError, ParamError};
- use masq_lib::test_utils::fake_stream_holder::{
- ByteArrayReader, ByteArrayWriter, FakeStreamHolder,
- };
+ use masq_lib::test_utils::fake_stream_holder::FakeStreamHolder;
use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler};
use masq_lib::utils::slice_of_strs_to_vec_of_strings;
use std::cell::RefCell;
use std::ops::Not;
use std::sync::{Arc, Mutex};
+ use test_utilities::byte_array_reader_writer::{ByteArrayReader, ByteArrayWriter};
impl ConfiguredByPrivilege for CrashTestDummy {
fn initialize_as_privileged(
diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs
index f2f49afec..955984164 100644
--- a/node/src/sub_lib/neighborhood.rs
+++ b/node/src/sub_lib/neighborhood.rs
@@ -3,7 +3,7 @@
use crate::neighborhood::gossip::Gossip_0v1;
use crate::neighborhood::node_record::NodeRecord;
use crate::neighborhood::overall_connection_status::ConnectionProgress;
-use crate::neighborhood::Neighborhood;
+use crate::neighborhood::{Neighborhood, UserExitPreferences};
use crate::sub_lib::configurator::NewPasswordMessage;
use crate::sub_lib::cryptde::{CryptDE, PublicKey};
use crate::sub_lib::cryptde_real::CryptDEReal;
@@ -380,6 +380,39 @@ pub enum Hops {
SixHops = 6,
}
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct ExitLocation {
+ pub country_codes: Vec,
+ pub priority: usize,
+}
+
+pub struct ExitLocationSet {
+ pub locations: Vec,
+}
+
+impl Display for ExitLocation {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Country Codes: {:?}, Priority: {};",
+ self.country_codes, self.priority
+ )
+ }
+}
+
+impl Display for ExitLocationSet {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ for exit_location in self.locations.iter() {
+ write!(
+ f,
+ "Country Codes: {:?} - Priority: {}; ",
+ exit_location.country_codes, exit_location.priority
+ )?;
+ }
+ Ok(())
+ }
+}
+
impl FromStr for Hops {
type Err = String;
@@ -406,7 +439,6 @@ impl Display for Hops {
pub struct NeighborhoodConfig {
pub mode: NeighborhoodMode,
pub min_hops: Hops,
- pub country: String,
}
lazy_static! {
@@ -478,6 +510,7 @@ pub struct RouteQueryMessage {
pub return_component_opt: Option,
pub payload_size: usize,
pub hostname_opt: Option,
+ pub target_country_opt: Option,
}
impl Message for RouteQueryMessage {
@@ -487,6 +520,7 @@ impl Message for RouteQueryMessage {
impl RouteQueryMessage {
pub fn data_indefinite_route_request(
hostname_opt: Option,
+ target_country_opt: Option,
payload_size: usize,
) -> RouteQueryMessage {
RouteQueryMessage {
@@ -495,6 +529,7 @@ impl RouteQueryMessage {
return_component_opt: Some(Component::ProxyServer),
payload_size,
hostname_opt,
+ target_country_opt,
}
}
}
@@ -595,6 +630,7 @@ pub struct NeighborhoodMetadata {
pub connection_progress_peers: Vec,
pub cpm_recipient: Recipient,
pub db_patch_size: u8,
+ pub user_exit_preferences_opt: Option,
}
pub struct NeighborhoodTools {
@@ -718,12 +754,12 @@ mod tests {
}
#[test]
- fn parse_works_for_mumbai() {
- let descriptor = "masq://polygon-mumbai:as45cs5c5@1.2.3.4:4444";
+ fn parse_works_for_amoy() {
+ let descriptor = "masq://polygon-amoy:as45cs5c5@1.2.3.4:4444";
let result = NodeDescriptor::parse_url(descriptor).unwrap();
- assert_eq!(result, (Chain::PolyMumbai, "as45cs5c5", "1.2.3.4:4444"))
+ assert_eq!(result, (Chain::PolyAmoy, "as45cs5c5", "1.2.3.4:4444"))
}
#[test]
@@ -750,7 +786,7 @@ mod tests {
assert_eq!(
result,
Err(
- "Chain identifier 'bitcoin' is not valid; possible values are 'polygon-mainnet', 'eth-mainnet', 'polygon-mumbai', 'eth-ropsten' while formatted as 'masq://:@'"
+ "Chain identifier 'bitcoin' is not valid; possible values are 'polygon-mainnet', 'eth-mainnet', 'polygon-amoy', 'eth-ropsten' while formatted as 'masq://:@'"
.to_string()
)
);
@@ -849,7 +885,7 @@ mod tests {
let result = DescriptorParsingError::WrongChainIdentifier("blah").to_string();
- assert_eq!(result, "Chain identifier 'blah' is not valid; possible values are 'polygon-mainnet', 'eth-mainnet', 'polygon-mumbai', 'eth-ropsten' while formatted as 'masq://:@'")
+ assert_eq!(result, "Chain identifier 'blah' is not valid; possible values are 'polygon-mainnet', 'eth-mainnet', 'polygon-amoy', 'eth-ropsten' while formatted as 'masq://:@'")
}
#[test]
@@ -1049,7 +1085,7 @@ mod tests {
#[test]
fn data_indefinite_route_request() {
- let result = RouteQueryMessage::data_indefinite_route_request(None, 7500);
+ let result = RouteQueryMessage::data_indefinite_route_request(None, None, 7500);
assert_eq!(
result,
@@ -1058,7 +1094,8 @@ mod tests {
target_component: Component::ProxyClient,
return_component_opt: Some(Component::ProxyServer),
payload_size: 7500,
- hostname_opt: None
+ hostname_opt: None,
+ target_country_opt: None,
}
);
}
diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs
index 60a9b7741..5a2ce8edd 100644
--- a/node/src/test_utils/mod.rs
+++ b/node/src/test_utils/mod.rs
@@ -39,11 +39,14 @@ use crate::sub_lib::sequence_buffer::SequencedPacket;
use crate::sub_lib::stream_key::StreamKey;
use crate::sub_lib::wallet::Wallet;
use crossbeam_channel::{unbounded, Receiver, Sender};
+use dirs::home_dir;
use ethsign_crypto::Keccak256;
use futures::sync::mpsc::SendError;
use lazy_static::lazy_static;
use masq_lib::constants::HTTP_PORT;
-use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN;
+use masq_lib::test_utils::utils::{
+ ensure_node_home_directory_exists, node_home_directory, TEST_DEFAULT_CHAIN,
+};
use rand::RngCore;
use regex::Regex;
use rustc_hex::ToHex;
@@ -51,12 +54,14 @@ use serde_derive::{Deserialize, Serialize};
use std::collections::btree_set::BTreeSet;
use std::collections::HashSet;
use std::convert::From;
+use std::env::current_dir;
use std::fmt::Debug;
use std::hash::Hash;
use std::io::ErrorKind;
use std::io::Read;
use std::iter::repeat;
use std::net::{Shutdown, TcpStream};
+use std::path::PathBuf;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;
@@ -516,7 +521,7 @@ pub fn assert_eq_debug(a: T, b: T) {
assert_eq!(a_str, b_str);
}
-//must stay without cfg(test) -- used in another crate
+// Must stay without cfg(test) -- used in another crate
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct TestRawTransaction {
pub nonce: U256,
@@ -529,6 +534,20 @@ pub struct TestRawTransaction {
pub data: Vec,
}
+pub fn make_node_base_dir_and_return_its_absolute_and_relative_path_to_os_home_dir(
+ module: &str,
+ name: &str,
+) -> (PathBuf, PathBuf) {
+ let node_base_dir_relative = ensure_node_home_directory_exists(module, name);
+ let home_dir_path = home_dir().unwrap();
+ let current_dir = current_dir().unwrap();
+ let current_dir_tilde_like_path = current_dir.strip_prefix(home_dir_path).unwrap();
+ let node_base_dir_tilde_path =
+ current_dir_tilde_like_path.join(node_home_directory(module, name));
+ let node_base_dir_absolute = current_dir.join(node_base_dir_relative);
+ (node_base_dir_absolute, node_base_dir_tilde_path)
+}
+
#[macro_export]
macro_rules! arbitrary_id_stamp_in_trait {
() => {
diff --git a/node/src/test_utils/neighborhood_test_utils.rs b/node/src/test_utils/neighborhood_test_utils.rs
index 72545797a..983fd7932 100644
--- a/node/src/test_utils/neighborhood_test_utils.rs
+++ b/node/src/test_utils/neighborhood_test_utils.rs
@@ -66,13 +66,22 @@ impl From<(&NeighborhoodDatabase, &PublicKey, bool)> for AccessibleGossipRecord
}
}
-pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord {
+pub fn make_segments(n: u16) -> (u8, u8, u8, u8) {
let seg1 = ((n / 1000) % 10) as u8;
let seg2 = ((n / 100) % 10) as u8;
let seg3 = ((n / 10) % 10) as u8;
let seg4 = (n % 10) as u8;
+ (seg1, seg2, seg3, seg4)
+}
+
+pub fn make_segmented_ip(seg1: u8, seg2: u8, seg3: u8, seg4: u8) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(seg1, seg2, seg3, seg4))
+}
+
+pub fn make_node_record(n: u16, has_ip: bool) -> NodeRecord {
+ let (seg1, seg2, seg3, seg4) = make_segments(n);
let key = PublicKey::new(&[seg1, seg2, seg3, seg4]);
- let ip_addr = IpAddr::V4(Ipv4Addr::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 location_opt = match country_code.is_empty() {
@@ -148,12 +157,10 @@ pub fn neighborhood_from_nodes(
*root.rate_pack(),
),
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
None => NeighborhoodConfig {
mode: NeighborhoodMode::ZeroHop,
min_hops: MIN_HOPS_FOR_TEST,
- country: "ZZ".to_string(),
},
};
config.earning_wallet = root.earning_wallet();
diff --git a/node/tests/contract_test.rs b/node/tests/contract_test.rs
index 6002f392b..8e9e74395 100644
--- a/node/tests/contract_test.rs
+++ b/node/tests/contract_test.rs
@@ -105,14 +105,13 @@ where
}
#[test]
-fn masq_erc20_contract_exists_on_polygon_mumbai_integration() {
+fn masq_erc20_contract_exists_on_polygon_amoy_integration() {
let blockchain_urls = vec![
- "https://rpc-mumbai.polygon.technology",
- "https://matic-mumbai.chainstacklabs.com",
- "https://rpc-mumbai.maticvigil.com",
- "https://matic-testnet-archive-rpc.bwarelabs.com",
+ "https://rpc-amoy.polygon.technology",
+ "https://rpc.ankr.com/polygon_amoy",
+ "https://80002.rpc.thirdweb.com",
];
- let chain = Chain::PolyMumbai;
+ let chain = Chain::PolyAmoy;
let assertion_body = |url, chain| assert_contract_existence(url, chain, "tMASQ", 18);
assert_contract(blockchain_urls, &chain, assertion_body)
diff --git a/node/tests/dump_configuration_test.rs b/node/tests/dump_configuration_test.rs
index 044eb2043..62a045b0d 100644
--- a/node/tests/dump_configuration_test.rs
+++ b/node/tests/dump_configuration_test.rs
@@ -22,7 +22,7 @@ fn dump_configuration_with_an_existing_database_integration() {
Some(
CommandConfig::new()
.pair("--ui-port", &port.to_string())
- .pair("--chain", "polygon-mumbai"),
+ .pair("--chain", "polygon-amoy"),
),
true,
true,
@@ -38,7 +38,7 @@ fn dump_configuration_with_an_existing_database_integration() {
let mut node = utils::MASQNode::run_dump_config(
test_name,
- Some(CommandConfig::new().pair("--chain", "polygon-mumbai")),
+ Some(CommandConfig::new().pair("--chain", "polygon-amoy")),
false,
true,
true,
diff --git a/node/tests/node_exits_from_future_panic_test.rs b/node/tests/node_exits_from_future_panic_test.rs
index 1419dc0fe..dc3ec25ab 100644
--- a/node/tests/node_exits_from_future_panic_test.rs
+++ b/node/tests/node_exits_from_future_panic_test.rs
@@ -67,7 +67,7 @@ const STAT_FORMAT_PARAM_NAME: &str = "-f";
fn node_logfile_does_not_belong_to_root_integration() {
let mut node = MASQNode::start_standard(
"node_logfile_does_not_belong_to_root_integration",
- Some(CommandConfig::new().pair("--chain", "polygon-mumbai")),
+ Some(CommandConfig::new().pair("--chain", "polygon-amoy")),
true,
true,
false,
diff --git a/test_utilities/Cargo.toml b/test_utilities/Cargo.toml
new file mode 100644
index 000000000..93191e691
--- /dev/null
+++ b/test_utilities/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "test_utilities"
+version = "0.1.0"
+edition = "2021"
+authors = ["Dan Wiebe ", "MASQ"]
+license = "GPL-3.0-only"
+description = "Testing utilities Code common to Node and masq; also, temporarily, to dns_utility"
+workspace = "../node"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
\ No newline at end of file
diff --git a/test_utilities/src/byte_array_reader_writer.rs b/test_utilities/src/byte_array_reader_writer.rs
new file mode 100644
index 000000000..fe611f781
--- /dev/null
+++ b/test_utilities/src/byte_array_reader_writer.rs
@@ -0,0 +1,132 @@
+// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
+
+use std::cmp::min;
+use std::io;
+use std::io::Read;
+use std::io::Write;
+use std::io::{BufRead, Error};
+use std::sync::{Arc, Mutex};
+
+pub struct ByteArrayWriter {
+ inner_arc: Arc>,
+}
+
+pub struct ByteArrayWriterInner {
+ byte_array: Vec,
+ next_error: Option,
+}
+
+impl ByteArrayWriterInner {
+ pub fn get_bytes(&self) -> Vec {
+ self.byte_array.clone()
+ }
+ pub fn get_string(&self) -> String {
+ String::from_utf8(self.get_bytes()).unwrap()
+ }
+}
+
+impl Default for ByteArrayWriter {
+ fn default() -> Self {
+ ByteArrayWriter {
+ inner_arc: Arc::new(Mutex::new(ByteArrayWriterInner {
+ byte_array: vec![],
+ next_error: None,
+ })),
+ }
+ }
+}
+
+impl ByteArrayWriter {
+ pub fn new() -> ByteArrayWriter {
+ Self::default()
+ }
+
+ pub fn inner_arc(&self) -> Arc> {
+ self.inner_arc.clone()
+ }
+
+ pub fn get_bytes(&self) -> Vec {
+ self.inner_arc.lock().unwrap().byte_array.clone()
+ }
+ pub fn get_string(&self) -> String {
+ String::from_utf8(self.get_bytes()).unwrap()
+ }
+
+ pub fn reject_next_write(&mut self, error: Error) {
+ self.inner_arc().lock().unwrap().next_error = Some(error);
+ }
+}
+
+impl Write for ByteArrayWriter {
+ fn write(&mut self, buf: &[u8]) -> io::Result {
+ let mut inner = self.inner_arc.lock().unwrap();
+ if let Some(next_error) = inner.next_error.take() {
+ Err(next_error)
+ } else {
+ for byte in buf {
+ inner.byte_array.push(*byte)
+ }
+ Ok(buf.len())
+ }
+ }
+
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+pub struct ByteArrayReader {
+ byte_array: Vec,
+ position: usize,
+ next_error: Option,
+}
+
+impl ByteArrayReader {
+ pub fn new(byte_array: &[u8]) -> ByteArrayReader {
+ ByteArrayReader {
+ byte_array: byte_array.to_vec(),
+ position: 0,
+ next_error: None,
+ }
+ }
+
+ pub fn reject_next_read(mut self, error: Error) -> ByteArrayReader {
+ self.next_error = Some(error);
+ self
+ }
+}
+
+impl Read for ByteArrayReader {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result {
+ match self.next_error.take() {
+ Some(error) => Err(error),
+ None => {
+ let to_copy = min(buf.len(), self.byte_array.len() - self.position);
+ #[allow(clippy::needless_range_loop)]
+ for idx in 0..to_copy {
+ buf[idx] = self.byte_array[self.position + idx]
+ }
+ self.position += to_copy;
+ Ok(to_copy)
+ }
+ }
+ }
+}
+
+impl BufRead for ByteArrayReader {
+ fn fill_buf(&mut self) -> io::Result<&[u8]> {
+ match self.next_error.take() {
+ Some(error) => Err(error),
+ None => Ok(&self.byte_array[self.position..]),
+ }
+ }
+
+ fn consume(&mut self, amt: usize) {
+ let result = self.position + amt;
+ self.position = if result < self.byte_array.len() {
+ result
+ } else {
+ self.byte_array.len()
+ }
+ }
+}
diff --git a/test_utilities/src/lib.rs b/test_utilities/src/lib.rs
new file mode 100644
index 000000000..552c3b7d1
--- /dev/null
+++ b/test_utilities/src/lib.rs
@@ -0,0 +1,3 @@
+// Copyright (c) 2024, MASQ (https://masq.ai) and/or its affiliates. All rights reserved.
+
+pub mod byte_array_reader_writer;