From bb8e6999ed4242822f4c762e8c1c16b6fa272456 Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Mon, 27 Oct 2025 22:49:04 -0400 Subject: [PATCH 1/9] Functionality and tests. What's the big picture now? --- .gitignore | 1 + masq_lib/src/constants.rs | 2 +- node/src/database/db_initializer.rs | 9 +- .../src/database/db_migrations/db_migrator.rs | 2 + .../migrations/migration_11_to_12.rs | 73 +++++++ .../database/db_migrations/migrations/mod.rs | 3 +- node/src/db_config/config_dao_null.rs | 6 +- .../src/db_config/persistent_configuration.rs | 191 ++++++++++++++++++ .../unprivileged_parse_args_configuration.rs | 76 ++++++- node/src/sub_lib/neighborhood.rs | 16 ++ node/src/test_utils/mod.rs | 6 +- .../persistent_configuration_mock.rs | 11 + 12 files changed, 389 insertions(+), 7 deletions(-) create mode 100644 node/src/database/db_migrations/migrations/migration_11_to_12.rs diff --git a/.gitignore b/.gitignore index 88a2ab5f90..bdf669f129 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ **/*.rs.bk .idea/azure/ .idea/inspectionProfiles/Project_Default.xml +.idea/copilot.* ### Node node_modules diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index a67f86128c..0c1a640fb3 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -5,7 +5,7 @@ use crate::data_version::DataVersion; use const_format::concatcp; pub const DEFAULT_CHAIN: Chain = Chain::BaseMainnet; -pub const CURRENT_SCHEMA_VERSION: usize = 11; +pub const CURRENT_SCHEMA_VERSION: usize = 12; pub const HIGHEST_RANDOM_CLANDESTINE_PORT: u16 = 9999; pub const HTTP_PORT: u16 = 80; diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index cd5c5b1bb5..f741b974f9 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -5,7 +5,7 @@ use crate::database::db_migrations::db_migrator::{DbMigrator, DbMigratorReal}; use crate::db_config::secure_config_layer::EXAMPLE_ENCRYPTED; use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; -use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; +use crate::sub_lib::neighborhood::{DEFAULT_RATE_PACK, DEFAULT_RATE_PACK_LIMITS}; use crate::sub_lib::utils::db_connection_launch_panic; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::{ @@ -256,6 +256,13 @@ impl DbInitializerReal { false, "rate pack", ); + Self::set_config_value( + conn, + "rate_pack_limits", + Some(DEFAULT_RATE_PACK_LIMITS), + false, + "rate pack limits", + ); Self::set_config_value( conn, "scan_intervals", diff --git a/node/src/database/db_migrations/db_migrator.rs b/node/src/database/db_migrations/db_migrator.rs index 369a78788f..0182875093 100644 --- a/node/src/database/db_migrations/db_migrator.rs +++ b/node/src/database/db_migrations/db_migrator.rs @@ -17,6 +17,7 @@ use crate::database::db_migrations::migrator_utils::{ }; use crate::database::rusqlite_wrappers::{ConnectionWrapper, TransactionSafeWrapper}; use masq_lib::logger::Logger; +use crate::database::db_migrations::migrations::migration_11_to_12::Migrate_11_to_12; pub trait DbMigrator { fn migrate_database( @@ -82,6 +83,7 @@ impl DbMigratorReal { &Migrate_8_to_9, &Migrate_9_to_10, &Migrate_10_to_11, + &Migrate_11_to_12, ] } diff --git a/node/src/database/db_migrations/migrations/migration_11_to_12.rs b/node/src/database/db_migrations/migrations/migration_11_to_12.rs new file mode 100644 index 0000000000..f2ff17b92f --- /dev/null +++ b/node/src/database/db_migrations/migrations/migration_11_to_12.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +use crate::database::db_migrations::db_migrator::DatabaseMigration; +use crate::database::db_migrations::migrator_utils::DBMigDeclarator; +use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK_LIMITS; + +#[allow(non_camel_case_types)] +pub struct Migrate_11_to_12; + +impl DatabaseMigration for Migrate_11_to_12 { + fn migrate<'a>( + &self, + declaration_utils: Box, + ) -> rusqlite::Result<()> { + declaration_utils.execute_upon_transaction(&[ + &format!( + "INSERT INTO config (name, value, encrypted) VALUES ('rate_pack_limits', '{}', 0)", + DEFAULT_RATE_PACK_LIMITS + ) + ]) + } + + fn old_version(&self) -> usize { + 11 + } +} + +#[cfg(test)] +mod tests { + use crate::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, DATABASE_FILE, + }; + use crate::test_utils::database_utils::{ + bring_db_0_back_to_life_and_return_connection, make_external_data, retrieve_config_row, + }; + use masq_lib::test_utils::utils::ensure_node_home_directory_exists; + use std::fs::create_dir_all; + + #[test] + fn migration_from_11_to_12_is_properly_set() { + let dir_path = ensure_node_home_directory_exists( + "db_migrations", + "migration_from_11_to_12_is_properly_set", + ); + create_dir_all(&dir_path).unwrap(); + let db_path = dir_path.join(DATABASE_FILE); + let _ = bring_db_0_back_to_life_and_return_connection(&db_path); + let subject = DbInitializerReal::default(); + + let result = subject.initialize_to_version( + &dir_path, + 11, + DbInitializationConfig::create_or_migrate(make_external_data()), + ); + + assert!(result.is_ok()); + + let result = subject.initialize_to_version( + &dir_path, + 12, + DbInitializationConfig::create_or_migrate(make_external_data()), + ); + + assert!(result.is_ok()); + let connection = result.unwrap(); + let (lc_value, lc_encrypted) = retrieve_config_row(connection.as_ref(), "rate_pack_limits"); + let (cs_value, cs_encrypted) = retrieve_config_row(connection.as_ref(), "schema_version"); + assert_eq!(lc_value, Some("100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000".to_string())); + assert_eq!(lc_encrypted, false); + assert_eq!(cs_value, Some("12".to_string())); + assert_eq!(cs_encrypted, false) + } +} diff --git a/node/src/database/db_migrations/migrations/mod.rs b/node/src/database/db_migrations/migrations/mod.rs index 53b7b7bb67..53733f416f 100644 --- a/node/src/database/db_migrations/migrations/mod.rs +++ b/node/src/database/db_migrations/migrations/mod.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod migration_0_to_1; -pub mod migration_10_to_11; pub mod migration_1_to_2; pub mod migration_2_to_3; pub mod migration_3_to_4; @@ -11,3 +10,5 @@ pub mod migration_6_to_7; pub mod migration_7_to_8; pub mod migration_8_to_9; pub mod migration_9_to_10; +pub mod migration_10_to_11; +pub mod migration_11_to_12; diff --git a/node/src/db_config/config_dao_null.rs b/node/src/db_config/config_dao_null.rs index fd7fb7e059..32a2f249e3 100644 --- a/node/src/db_config/config_dao_null.rs +++ b/node/src/db_config/config_dao_null.rs @@ -5,7 +5,7 @@ use crate::database::rusqlite_wrappers::TransactionSafeWrapper; use crate::db_config::config_dao::{ConfigDao, ConfigDaoError, ConfigDaoRecord}; use crate::neighborhood::DEFAULT_MIN_HOPS; use crate::sub_lib::accountant::{DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS}; -use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; +use crate::sub_lib::neighborhood::{DEFAULT_RATE_PACK, DEFAULT_RATE_PACK_LIMITS}; use itertools::Itertools; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::{CURRENT_SCHEMA_VERSION, DEFAULT_GAS_PRICE}; @@ -138,6 +138,10 @@ impl Default for ConfigDaoNull { "rate_pack".to_string(), (Some(DEFAULT_RATE_PACK.to_string()), false), ); + data.insert( + "rate_pack_limits".to_string(), + (Some(DEFAULT_RATE_PACK_LIMITS.to_string()), false), + ); data.insert( "scan_intervals".to_string(), (Some(DEFAULT_SCAN_INTERVALS.to_string()), false), diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index ba25999ccb..4274b9c46c 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -26,6 +26,12 @@ use std::fmt::Display; use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; use std::str::FromStr; use websocket::url::Url; +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref RATE_PACK_LIMIT_FORMAT: Regex = Regex::new(r"^(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)$").unwrap(); +} #[derive(Clone, PartialEq, Eq, Debug)] pub enum PersistentConfigError { @@ -159,6 +165,7 @@ pub trait PersistentConfiguration { fn payment_thresholds(&self) -> Result; fn set_payment_thresholds(&mut self, curves: String) -> Result<(), PersistentConfigError>; fn rate_pack(&self) -> Result; + fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError>; fn set_rate_pack(&mut self, rate_pack: String) -> Result<(), PersistentConfigError>; fn scan_intervals(&self) -> Result; fn set_scan_intervals(&mut self, intervals: String) -> Result<(), PersistentConfigError>; @@ -570,6 +577,42 @@ impl PersistentConfiguration for PersistentConfigurationReal { self.simple_set_method("rate_pack", rate_pack) } + fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError> { + let limits_string = self + .get("rate_pack_limits") + .expect("Required value rate_pack_limits missing from CONFIG table: database is corrupt!") + .expect("Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!"); + let captures = RATE_PACK_LIMIT_FORMAT.captures(limits_string.as_str()) + .expect("Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei."); + let candidate = ( + RatePack { + routing_byte_rate: u64::from_str(captures.get(1).expect("blah").as_str()).expect("blah"), + routing_service_rate: u64::from_str(captures.get(3).expect("blah").as_str()).expect("blah"), + exit_byte_rate: u64::from_str(captures.get(5).expect("blah").as_str()).expect("blah"), + exit_service_rate: u64::from_str(captures.get(7).expect("blah").as_str()).expect("blah"), + }, + RatePack { + routing_byte_rate: u64::from_str(captures.get(2).expect("blah").as_str()).expect("blah"), + routing_service_rate: u64::from_str(captures.get(4).expect("blah").as_str()).expect("blah"), + exit_byte_rate: u64::from_str(captures.get(6).expect("blah").as_str()).expect("blah"), + exit_service_rate: u64::from_str(captures.get(8).expect("blah").as_str()).expect("blah"), + } + ); + let check_rate_pack_limit_order = |low: u64, high: u64, field_name: &str| { + if low >= high { + panic!( + "Rate pack limits should have low limits less than high limits, but {} limits are {}-{}", + field_name, low, high + ); + } + }; + check_rate_pack_limit_order(candidate.0.routing_byte_rate, candidate.1.routing_byte_rate, "routing_byte_rate"); + check_rate_pack_limit_order(candidate.0.routing_service_rate, candidate.1.routing_service_rate, "routing_service_rate"); + check_rate_pack_limit_order(candidate.0.exit_byte_rate, candidate.1.exit_byte_rate, "exit_byte_rate"); + check_rate_pack_limit_order(candidate.0.exit_service_rate, candidate.1.exit_service_rate, "exit_service_rate"); + Ok(candidate) + } + fn scan_intervals(&self) -> Result { self.combined_params_get_method(|str: &str| ScanIntervals::try_from(str), "scan_intervals") } @@ -2284,6 +2327,154 @@ mod tests { getter_method_plain_data_does_not_tolerate_none_value!("rate_pack"); } + #[test] + fn rate_pack_limits_works() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "100-200|300-400|500000000000000000-600000000000000000|700-800", + ( + RatePack { + routing_byte_rate: 100, + routing_service_rate: 300, + exit_byte_rate: 500_000_000_000_000_000, + exit_service_rate: 700, + }, + RatePack { + routing_byte_rate: 200, + routing_service_rate: 400, + exit_byte_rate: 600_000_000_000_000_000, + exit_service_rate: 800, + } + ) + ); + } + + #[test] + #[should_panic(expected = "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!")] + fn rate_pack_limits_panics_at_none_value() { + getter_method_plain_data_does_not_tolerate_none_value!("rate_pack_limits"); + } + + #[test] + #[should_panic(expected = "Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.")] + fn rate_pack_limits_panics_at_syntax_error_in_value() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "Booga!", + // Irrelevant but necessary + ( + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + }, + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + } + ) + ); + } + + #[test] + #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but routing_byte_rate limits are 1-1")] + fn rate_pack_limits_panics_when_routing_byte_rate_limits_are_reversed() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "1-1|0-1|0-1|0-1", + // Irrelevant but necessary + ( + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + }, + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + } + ) + ); + } + + #[test] + #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but routing_service_rate limits are 1-1")] + fn rate_pack_limits_panics_when_routing_service_rate_limits_are_reversed() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "0-1|1-1|0-1|0-1", + // Irrelevant but necessary + ( + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + }, + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + } + ) + ); + } + + #[test] + #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but exit_byte_rate limits are 1-1")] + fn rate_pack_limits_panics_when_exit_byte_rate_limits_are_reversed() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "0-1|0-1|1-1|0-1", + // Irrelevant but necessary + ( + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + }, + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + } + ) + ); + } + + #[test] + #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but exit_service_rate limits are 1-1")] + fn rate_pack_limits_panics_when_exit_service_rate_limits_are_reversed() { + persistent_config_plain_data_assertions_for_simple_get_method!( + "rate_pack_limits", + "0-1|0-1|0-1|1-1", + // Irrelevant but necessary + ( + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + }, + RatePack { + routing_byte_rate: 0, + routing_service_rate: 0, + exit_byte_rate: 0, + exit_service_rate: 0, + } + ) + ); + } + #[test] fn scan_intervals_get_method_works() { persistent_config_plain_data_assertions_for_simple_get_method!( diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 8ff2ed7c4c..4538c46b47 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -556,14 +556,42 @@ fn configure_rate_pack( multi_config: &MultiConfig, persist_config: &mut dyn PersistentConfiguration, ) -> Result { - process_combined_params( + let check_min_and_max = |candidate: u64, min: u64, max: u64, name: &str, error: ConfiguratorError| -> ConfiguratorError { + let mut result = error; + if candidate < min { + result = result.another_required("rate-pack", &format!("Value of {} ({}) is below the minimum allowed ({})", name, candidate, min)); + } else if candidate > max { + result = result.another_required("rate-pack", &format!("Value of {} ({}) is above the maximum allowed ({})", name, candidate, max)); + } + result + }; + match process_combined_params( "rate-pack", multi_config, persist_config, |str: &str| RatePack::try_from(str), |pc: &dyn PersistentConfiguration| pc.rate_pack(), |pc: &mut dyn PersistentConfiguration, rate_pack| pc.set_rate_pack(rate_pack), - ) + ) { + Ok(rate_pack) => { + let (low_limit, high_limit) = match persist_config.rate_pack_limits() { + Ok(pair) => pair, + Err(e) => return Err(e.into_configurator_error("rate-pack")) + }; + let mut error = ConfiguratorError::new(vec![]); + error = check_min_and_max(rate_pack.routing_byte_rate, low_limit.routing_byte_rate, high_limit.routing_byte_rate, "routing_byte_rate", error); + error = check_min_and_max(rate_pack.routing_service_rate, low_limit.routing_service_rate, high_limit.routing_service_rate, "routing_service_rate", error); + error = check_min_and_max(rate_pack.exit_byte_rate, low_limit.exit_byte_rate, high_limit.exit_byte_rate, "exit_byte_rate", error); + error = check_min_and_max(rate_pack.exit_service_rate, low_limit.exit_service_rate, high_limit.exit_service_rate, "exit_service_rate", error); + if error.len() > 0 { + Err(error) + } + else { + Ok(rate_pack) + } + }, + Err(e) => Err(e), + } } fn process_combined_params<'a, T: PartialEq, C1, C2>( @@ -2421,6 +2449,46 @@ mod tests { assert_eq!(result, expected_rate_pack) } + #[test] + fn configure_rate_pack_complains_when_minimums_are_transgressed() { + let mut persistent_config = PersistentConfigurationMock::new() + .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) + .set_rate_pack_result(Ok(())) + .rate_pack_limits_result(Ok(( + RatePack::new(5, 5, 5, 5), + RatePack::new(7, 7, 7, 7) + ))); + + let result = configure_rate_pack(&make_simplified_multi_config(["--rate-pack", "4|4|4|4"]), &mut persistent_config); + + let expected_error = ConfiguratorError::new(vec![]) + .another_required("rate-pack", "Value of routing_byte_rate (4) is below the minimum allowed (5)") + .another_required("rate-pack", "Value of routing_service_rate (4) is below the minimum allowed (5)") + .another_required("rate-pack", "Value of exit_byte_rate (4) is below the minimum allowed (5)") + .another_required("rate-pack", "Value of exit_service_rate (4) is below the minimum allowed (5)"); + assert_eq!(result, Err(expected_error)); + } + + #[test] + fn configure_rate_pack_complains_when_maximums_are_transgressed() { + let mut persistent_config = PersistentConfigurationMock::new() + .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) + .set_rate_pack_result(Ok(())) + .rate_pack_limits_result(Ok(( + RatePack::new(5, 5, 5, 5), + RatePack::new(7, 7, 7, 7) + ))); + + let result = configure_rate_pack(&make_simplified_multi_config(["--rate-pack", "8|8|8|8"]), &mut persistent_config); + + let expected_error = ConfiguratorError::new(vec![]) + .another_required("rate-pack", "Value of routing_byte_rate (8) is above the maximum allowed (7)") + .another_required("rate-pack", "Value of routing_service_rate (8) is above the maximum allowed (7)") + .another_required("rate-pack", "Value of exit_byte_rate (8) is above the maximum allowed (7)") + .another_required("rate-pack", "Value of exit_service_rate (8) is above the maximum allowed (7)"); + assert_eq!(result, Err(expected_error)); + } + #[test] fn compute_mapping_protocol_returns_saved_value_if_nothing_supplied() { let multi_config = make_new_multi_config( @@ -2661,6 +2729,10 @@ mod tests { .past_neighbors_result(past_neighbors_result) .mapping_protocol_result(Ok(Some(AutomapProtocol::Pcp))) .rate_pack_result(Ok(rate_pack)) + .rate_pack_limits_result(Ok(( + RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), + ))) .min_hops_result(Ok(min_hops)) } } diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index f26282aa60..e5179ab127 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -48,6 +48,8 @@ pub const ZERO_RATE_PACK: RatePack = RatePack { exit_service_rate: 0, }; +pub const DEFAULT_RATE_PACK_LIMITS: &str = "100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000"; + #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct RatePack { pub routing_byte_rate: u64, @@ -57,6 +59,20 @@ pub struct RatePack { } impl RatePack { + pub fn new( + routing_byte_rate: u64, + routing_service_rate: u64, + exit_byte_rate: u64, + exit_service_rate: u64, + ) -> Self { + Self { + routing_byte_rate, + routing_service_rate, + exit_byte_rate, + exit_service_rate, + } + } + pub fn routing_charge(&self, payload_size: u64) -> u64 { self.routing_service_rate + (self.routing_byte_rate * payload_size) } diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 351b27711c..4dffff8c5f 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -507,7 +507,7 @@ pub mod unshared_test_utils { use crate::node_test_utils::DirsWrapperMock; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::CryptDE; - use crate::sub_lib::neighborhood::{ConnectionProgressMessage, DEFAULT_RATE_PACK}; + use crate::sub_lib::neighborhood::{ConnectionProgressMessage, RatePack, DEFAULT_RATE_PACK}; use crate::sub_lib::proxy_client::ClientResponsePayload_0v1; use crate::sub_lib::proxy_server::{ClientRequestPayload_0v1, ProxyProtocol}; use crate::sub_lib::sequence_buffer::SequencedPacket; @@ -631,6 +631,10 @@ pub mod unshared_test_utils { } else { config }; + let config = config.rate_pack_limits_result(Ok(( + RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + ))); config } diff --git a/node/src/test_utils/persistent_configuration_mock.rs b/node/src/test_utils/persistent_configuration_mock.rs index 138a1deb65..ec70cb3b25 100644 --- a/node/src/test_utils/persistent_configuration_mock.rs +++ b/node/src/test_utils/persistent_configuration_mock.rs @@ -76,6 +76,7 @@ pub struct PersistentConfigurationMock { set_payment_thresholds_params: Arc>>, set_payment_thresholds_results: RefCell>>, rate_pack_results: RefCell>>, + rate_pack_limits_results: RefCell>>, set_rate_pack_params: Arc>>, set_rate_pack_results: RefCell>>, scan_intervals_results: RefCell>>, @@ -152,6 +153,7 @@ impl Clone for PersistentConfigurationMock { set_payment_thresholds_params: self.set_payment_thresholds_params.clone(), set_payment_thresholds_results: self.set_payment_thresholds_results.clone(), rate_pack_results: self.rate_pack_results.clone(), + rate_pack_limits_results: self.rate_pack_limits_results.clone(), set_rate_pack_params: self.set_rate_pack_params.clone(), set_rate_pack_results: self.set_rate_pack_results.clone(), scan_intervals_results: self.scan_intervals_results.clone(), @@ -402,6 +404,10 @@ impl PersistentConfiguration for PersistentConfigurationMock { self.set_rate_pack_results.borrow_mut().remove(0) } + fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError> { + self.rate_pack_limits_results.borrow_mut().remove(0) + } + fn scan_intervals(&self) -> Result { self.scan_intervals_results.borrow_mut().remove(0) } @@ -759,6 +765,11 @@ impl PersistentConfigurationMock { self } + pub fn rate_pack_limits_result(self, result: Result<(RatePack, RatePack), PersistentConfigError>) -> Self { + self.rate_pack_limits_results.borrow_mut().push(result); + self + } + pub fn set_rate_pack_params(mut self, params: &Arc>>) -> Self { self.set_rate_pack_params = params.clone(); self From 61d70f53144487216006a5bd238c858e456e07fe Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Tue, 28 Oct 2025 08:15:56 -0400 Subject: [PATCH 2/9] I think that might be it --- node/src/daemon/setup_reporter.rs | 26 ++-- node/src/database/db_initializer.rs | 14 +- .../src/database/db_migrations/db_migrator.rs | 2 +- .../migrations/migration_11_to_12.rs | 18 ++- .../database/db_migrations/migrations/mod.rs | 4 +- .../src/db_config/persistent_configuration.rs | 89 +++++++++---- .../unprivileged_parse_args_configuration.rs | 124 +++++++++++++----- node/src/sub_lib/neighborhood.rs | 3 +- node/src/test_utils/mod.rs | 2 +- .../persistent_configuration_mock.rs | 5 +- 10 files changed, 207 insertions(+), 80 deletions(-) diff --git a/node/src/daemon/setup_reporter.rs b/node/src/daemon/setup_reporter.rs index a7448eba44..84502ed367 100644 --- a/node/src/daemon/setup_reporter.rs +++ b/node/src/daemon/setup_reporter.rs @@ -1518,7 +1518,7 @@ mod tests { ("neighborhood-mode", "originate-only", Set), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678", Set), ("payment-thresholds","1234|50000|1000|1000|20000|20000",Set), - ("rate-pack","1|3|3|8",Set), + ("rate-pack","100|300|300|800",Set), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga", Set), ("scan-intervals","150|150|150",Set), @@ -1548,7 +1548,7 @@ mod tests { ("neighborhood-mode", "originate-only", Set), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678", Set), ("payment-thresholds","1234|50000|1000|1000|20000|20000",Set), - ("rate-pack","1|3|3|8",Set), + ("rate-pack","100|300|300|800",Set), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga", Set), ("scan-intervals","150|150|150",Set), @@ -1588,7 +1588,7 @@ mod tests { ("neighborhood-mode", "originate-only"), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678"), ("payment-thresholds","1234|50000|1000|1000|15000|15000"), - ("rate-pack","1|3|3|8"), + ("rate-pack","100|300|300|800"), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga"), ("scan-intervals","140|130|150"), @@ -1623,7 +1623,7 @@ mod tests { ("neighborhood-mode", "originate-only", Set), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678", Set), ("payment-thresholds","1234|50000|1000|1000|15000|15000",Set), - ("rate-pack","1|3|3|8",Set), + ("rate-pack","100|300|300|800",Set), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga", Set), ("scan-intervals","140|130|150",Set), @@ -1664,7 +1664,7 @@ mod tests { ("MASQ_NEIGHBORHOOD_MODE", "originate-only"), ("MASQ_NEIGHBORS", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678"), ("MASQ_PAYMENT_THRESHOLDS","12345|50000|1000|1234|19000|20000"), - ("MASQ_RATE_PACK","1|3|3|8"), + ("MASQ_RATE_PACK","100|300|300|800"), #[cfg(not(target_os = "windows"))] ("MASQ_REAL_USER", "9999:9999:booga"), ("MASQ_SCANS", "off"), @@ -1696,7 +1696,7 @@ mod tests { ("neighborhood-mode", "originate-only", Configured), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678", Configured), ("payment-thresholds","12345|50000|1000|1234|19000|20000",Configured), - ("rate-pack","1|3|3|8",Configured), + ("rate-pack","100|300|300|800",Configured), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga", Configured), ("scan-intervals","133|133|111",Configured), @@ -1756,7 +1756,9 @@ mod tests { .write_all(b"neighborhood-mode = \"standard\"\n") .unwrap(); config_file.write_all(b"scans = \"off\"\n").unwrap(); - config_file.write_all(b"rate-pack = \"2|2|2|2\"\n").unwrap(); + config_file + .write_all(b"rate-pack = \"200|200|200|200\"\n") + .unwrap(); config_file .write_all(b"payment-thresholds = \"3333|55|33|646|999|999\"\n") .unwrap(); @@ -1799,7 +1801,7 @@ mod tests { .unwrap(); config_file.write_all(b"scans = \"off\"\n").unwrap(); config_file - .write_all(b"rate-pack = \"55|50|60|61\"\n") + .write_all(b"rate-pack = \"5500|5000|6000|6100\"\n") .unwrap(); config_file .write_all(b"payment-thresholds = \"4000|1000|3000|3333|10000|20000\"\n") @@ -1864,7 +1866,7 @@ mod tests { "4000|1000|3000|3333|10000|20000", Configured, ), - ("rate-pack", "55|50|60|61", Configured), + ("rate-pack", "5500|5000|6000|6100", Configured), #[cfg(not(target_os = "windows"))] ( "real-user", @@ -1914,7 +1916,7 @@ mod tests { ("MASQ_NEIGHBORHOOD_MODE", "originate-only"), ("MASQ_NEIGHBORS", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678"), ("MASQ_PAYMENT_THRESHOLDS","1234|50000|1000|1000|20000|20000"), - ("MASQ_RATE_PACK","1|3|3|8"), + ("MASQ_RATE_PACK","100|300|300|800"), #[cfg(not(target_os = "windows"))] ("MASQ_REAL_USER", "9999:9999:booga"), ("MASQ_SCANS", "off"), @@ -1977,7 +1979,7 @@ mod tests { Set, ), ("payment-thresholds", "4321|66666|777|987|123456|124444", Set), - ("rate-pack", "10|30|13|28", Set), + ("rate-pack", "1000|3000|1300|2800", Set), #[cfg(not(target_os = "windows"))] ("real-user", "6666:6666:agoob", Set), ("scan-intervals", "111|111|111", Set), @@ -2011,7 +2013,7 @@ mod tests { ("neighborhood-mode", "originate-only", Configured), ("neighbors", "masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@1.2.3.4:1234,masq://base-sepolia:MTIzNDU2Nzg5MTEyMzQ1Njc4OTIxMjM0NTY3ODkzMTI@5.6.7.8:5678", Configured), ("payment-thresholds","1234|50000|1000|1000|20000|20000",Configured), - ("rate-pack","1|3|3|8",Configured), + ("rate-pack","100|300|300|800",Configured), #[cfg(not(target_os = "windows"))] ("real-user", "9999:9999:booga", Configured), ("scan-intervals","150|150|155",Configured), diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index f741b974f9..0673a3035f 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -668,7 +668,7 @@ mod tests { #[test] fn constants_have_correct_values() { assert_eq!(DATABASE_FILE, "node-data.db"); - assert_eq!(CURRENT_SCHEMA_VERSION, 11); + assert_eq!(CURRENT_SCHEMA_VERSION, 12); } #[test] @@ -966,6 +966,12 @@ mod tests { Some(&DEFAULT_RATE_PACK.to_string()), false, ); + verify( + &mut config_vec, + "rate_pack_limits", + Some(DEFAULT_RATE_PACK_LIMITS), + false, + ); verify( &mut config_vec, "scan_intervals", @@ -1078,6 +1084,12 @@ mod tests { Some(&DEFAULT_RATE_PACK.to_string()), false, ); + verify( + &mut config_vec, + "rate_pack_limits", + Some(DEFAULT_RATE_PACK_LIMITS), + false, + ); verify( &mut config_vec, "scan_intervals", diff --git a/node/src/database/db_migrations/db_migrator.rs b/node/src/database/db_migrations/db_migrator.rs index 0182875093..f3a3252565 100644 --- a/node/src/database/db_migrations/db_migrator.rs +++ b/node/src/database/db_migrations/db_migrator.rs @@ -3,6 +3,7 @@ use crate::database::db_initializer::ExternalData; use crate::database::db_migrations::migrations::migration_0_to_1::Migrate_0_to_1; use crate::database::db_migrations::migrations::migration_10_to_11::Migrate_10_to_11; +use crate::database::db_migrations::migrations::migration_11_to_12::Migrate_11_to_12; use crate::database::db_migrations::migrations::migration_1_to_2::Migrate_1_to_2; use crate::database::db_migrations::migrations::migration_2_to_3::Migrate_2_to_3; use crate::database::db_migrations::migrations::migration_3_to_4::Migrate_3_to_4; @@ -17,7 +18,6 @@ use crate::database::db_migrations::migrator_utils::{ }; use crate::database::rusqlite_wrappers::{ConnectionWrapper, TransactionSafeWrapper}; use masq_lib::logger::Logger; -use crate::database::db_migrations::migrations::migration_11_to_12::Migrate_11_to_12; pub trait DbMigrator { fn migrate_database( diff --git a/node/src/database/db_migrations/migrations/migration_11_to_12.rs b/node/src/database/db_migrations/migrations/migration_11_to_12.rs index f2ff17b92f..87261688c2 100644 --- a/node/src/database/db_migrations/migrations/migration_11_to_12.rs +++ b/node/src/database/db_migrations/migrations/migration_11_to_12.rs @@ -12,12 +12,10 @@ impl DatabaseMigration for Migrate_11_to_12 { &self, declaration_utils: Box, ) -> rusqlite::Result<()> { - declaration_utils.execute_upon_transaction(&[ - &format!( - "INSERT INTO config (name, value, encrypted) VALUES ('rate_pack_limits', '{}', 0)", - DEFAULT_RATE_PACK_LIMITS - ) - ]) + declaration_utils.execute_upon_transaction(&[&format!( + "INSERT INTO config (name, value, encrypted) VALUES ('rate_pack_limits', '{}', 0)", + DEFAULT_RATE_PACK_LIMITS + )]) } fn old_version(&self) -> usize { @@ -65,7 +63,13 @@ mod tests { let connection = result.unwrap(); let (lc_value, lc_encrypted) = retrieve_config_row(connection.as_ref(), "rate_pack_limits"); let (cs_value, cs_encrypted) = retrieve_config_row(connection.as_ref(), "schema_version"); - assert_eq!(lc_value, Some("100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000".to_string())); + assert_eq!( + lc_value, + Some( + "100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000" + .to_string() + ) + ); assert_eq!(lc_encrypted, false); assert_eq!(cs_value, Some("12".to_string())); assert_eq!(cs_encrypted, false) diff --git a/node/src/database/db_migrations/migrations/mod.rs b/node/src/database/db_migrations/migrations/mod.rs index 53733f416f..23c395d108 100644 --- a/node/src/database/db_migrations/migrations/mod.rs +++ b/node/src/database/db_migrations/migrations/mod.rs @@ -1,6 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. pub mod migration_0_to_1; +pub mod migration_10_to_11; +pub mod migration_11_to_12; pub mod migration_1_to_2; pub mod migration_2_to_3; pub mod migration_3_to_4; @@ -10,5 +12,3 @@ pub mod migration_6_to_7; pub mod migration_7_to_8; pub mod migration_8_to_9; pub mod migration_9_to_10; -pub mod migration_10_to_11; -pub mod migration_11_to_12; diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 4274b9c46c..1c8acd666c 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -16,21 +16,22 @@ use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack}; use crate::sub_lib::wallet::Wallet; +use lazy_static::lazy_static; use masq_lib::blockchains::chains::Chain; use masq_lib::constants::{HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT}; use masq_lib::shared_schema::{ConfiguratorError, ParamError}; use masq_lib::utils::NeighborhoodModeLight; use masq_lib::utils::{to_string, AutomapProtocol}; +use regex::Regex; use rustc_hex::{FromHex, ToHex}; use std::fmt::Display; use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; use std::str::FromStr; use websocket::url::Url; -use lazy_static::lazy_static; -use regex::Regex; lazy_static! { - static ref RATE_PACK_LIMIT_FORMAT: Regex = Regex::new(r"^(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)$").unwrap(); + static ref RATE_PACK_LIMIT_FORMAT: Regex = + Regex::new(r"^(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)$").unwrap(); } #[derive(Clone, PartialEq, Eq, Debug)] @@ -580,23 +581,35 @@ impl PersistentConfiguration for PersistentConfigurationReal { fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError> { let limits_string = self .get("rate_pack_limits") - .expect("Required value rate_pack_limits missing from CONFIG table: database is corrupt!") - .expect("Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!"); + .expect( + "Required value rate_pack_limits missing from CONFIG table: database is corrupt!", + ) + .expect( + "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!", + ); let captures = RATE_PACK_LIMIT_FORMAT.captures(limits_string.as_str()) .expect("Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei."); let candidate = ( RatePack { - routing_byte_rate: u64::from_str(captures.get(1).expect("blah").as_str()).expect("blah"), - routing_service_rate: u64::from_str(captures.get(3).expect("blah").as_str()).expect("blah"), - exit_byte_rate: u64::from_str(captures.get(5).expect("blah").as_str()).expect("blah"), - exit_service_rate: u64::from_str(captures.get(7).expect("blah").as_str()).expect("blah"), + routing_byte_rate: u64::from_str(captures.get(1).expect("blah").as_str()) + .expect("blah"), + routing_service_rate: u64::from_str(captures.get(3).expect("blah").as_str()) + .expect("blah"), + exit_byte_rate: u64::from_str(captures.get(5).expect("blah").as_str()) + .expect("blah"), + exit_service_rate: u64::from_str(captures.get(7).expect("blah").as_str()) + .expect("blah"), }, RatePack { - routing_byte_rate: u64::from_str(captures.get(2).expect("blah").as_str()).expect("blah"), - routing_service_rate: u64::from_str(captures.get(4).expect("blah").as_str()).expect("blah"), - exit_byte_rate: u64::from_str(captures.get(6).expect("blah").as_str()).expect("blah"), - exit_service_rate: u64::from_str(captures.get(8).expect("blah").as_str()).expect("blah"), - } + routing_byte_rate: u64::from_str(captures.get(2).expect("blah").as_str()) + .expect("blah"), + routing_service_rate: u64::from_str(captures.get(4).expect("blah").as_str()) + .expect("blah"), + exit_byte_rate: u64::from_str(captures.get(6).expect("blah").as_str()) + .expect("blah"), + exit_service_rate: u64::from_str(captures.get(8).expect("blah").as_str()) + .expect("blah"), + }, ); let check_rate_pack_limit_order = |low: u64, high: u64, field_name: &str| { if low >= high { @@ -606,10 +619,26 @@ impl PersistentConfiguration for PersistentConfigurationReal { ); } }; - check_rate_pack_limit_order(candidate.0.routing_byte_rate, candidate.1.routing_byte_rate, "routing_byte_rate"); - check_rate_pack_limit_order(candidate.0.routing_service_rate, candidate.1.routing_service_rate, "routing_service_rate"); - check_rate_pack_limit_order(candidate.0.exit_byte_rate, candidate.1.exit_byte_rate, "exit_byte_rate"); - check_rate_pack_limit_order(candidate.0.exit_service_rate, candidate.1.exit_service_rate, "exit_service_rate"); + check_rate_pack_limit_order( + candidate.0.routing_byte_rate, + candidate.1.routing_byte_rate, + "routing_byte_rate", + ); + check_rate_pack_limit_order( + candidate.0.routing_service_rate, + candidate.1.routing_service_rate, + "routing_service_rate", + ); + check_rate_pack_limit_order( + candidate.0.exit_byte_rate, + candidate.1.exit_byte_rate, + "exit_byte_rate", + ); + check_rate_pack_limit_order( + candidate.0.exit_service_rate, + candidate.1.exit_service_rate, + "exit_service_rate", + ); Ok(candidate) } @@ -2350,13 +2379,17 @@ mod tests { } #[test] - #[should_panic(expected = "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!")] + #[should_panic( + expected = "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!" + )] fn rate_pack_limits_panics_at_none_value() { getter_method_plain_data_does_not_tolerate_none_value!("rate_pack_limits"); } #[test] - #[should_panic(expected = "Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.")] + #[should_panic( + expected = "Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei." + )] fn rate_pack_limits_panics_at_syntax_error_in_value() { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", @@ -2380,7 +2413,9 @@ mod tests { } #[test] - #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but routing_byte_rate limits are 1-1")] + #[should_panic( + expected = "Rate pack limits should have low limits less than high limits, but routing_byte_rate limits are 1-1" + )] fn rate_pack_limits_panics_when_routing_byte_rate_limits_are_reversed() { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", @@ -2404,7 +2439,9 @@ mod tests { } #[test] - #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but routing_service_rate limits are 1-1")] + #[should_panic( + expected = "Rate pack limits should have low limits less than high limits, but routing_service_rate limits are 1-1" + )] fn rate_pack_limits_panics_when_routing_service_rate_limits_are_reversed() { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", @@ -2428,7 +2465,9 @@ mod tests { } #[test] - #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but exit_byte_rate limits are 1-1")] + #[should_panic( + expected = "Rate pack limits should have low limits less than high limits, but exit_byte_rate limits are 1-1" + )] fn rate_pack_limits_panics_when_exit_byte_rate_limits_are_reversed() { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", @@ -2452,7 +2491,9 @@ mod tests { } #[test] - #[should_panic(expected = "Rate pack limits should have low limits less than high limits, but exit_service_rate limits are 1-1")] + #[should_panic( + expected = "Rate pack limits should have low limits less than high limits, but exit_service_rate limits are 1-1" + )] fn rate_pack_limits_panics_when_exit_service_rate_limits_are_reversed() { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 4538c46b47..7e0f68ef3b 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -556,12 +556,29 @@ fn configure_rate_pack( multi_config: &MultiConfig, persist_config: &mut dyn PersistentConfiguration, ) -> Result { - let check_min_and_max = |candidate: u64, min: u64, max: u64, name: &str, error: ConfiguratorError| -> ConfiguratorError { + let check_min_and_max = |candidate: u64, + min: u64, + max: u64, + name: &str, + error: ConfiguratorError| + -> ConfiguratorError { let mut result = error; if candidate < min { - result = result.another_required("rate-pack", &format!("Value of {} ({}) is below the minimum allowed ({})", name, candidate, min)); + result = result.another_required( + "rate-pack", + &format!( + "Value of {} ({}) is below the minimum allowed ({})", + name, candidate, min + ), + ); } else if candidate > max { - result = result.another_required("rate-pack", &format!("Value of {} ({}) is above the maximum allowed ({})", name, candidate, max)); + result = result.another_required( + "rate-pack", + &format!( + "Value of {} ({}) is above the maximum allowed ({})", + name, candidate, max + ), + ); } result }; @@ -576,20 +593,43 @@ fn configure_rate_pack( Ok(rate_pack) => { let (low_limit, high_limit) = match persist_config.rate_pack_limits() { Ok(pair) => pair, - Err(e) => return Err(e.into_configurator_error("rate-pack")) + Err(e) => return Err(e.into_configurator_error("rate-pack")), }; let mut error = ConfiguratorError::new(vec![]); - error = check_min_and_max(rate_pack.routing_byte_rate, low_limit.routing_byte_rate, high_limit.routing_byte_rate, "routing_byte_rate", error); - error = check_min_and_max(rate_pack.routing_service_rate, low_limit.routing_service_rate, high_limit.routing_service_rate, "routing_service_rate", error); - error = check_min_and_max(rate_pack.exit_byte_rate, low_limit.exit_byte_rate, high_limit.exit_byte_rate, "exit_byte_rate", error); - error = check_min_and_max(rate_pack.exit_service_rate, low_limit.exit_service_rate, high_limit.exit_service_rate, "exit_service_rate", error); - if error.len() > 0 { + error = check_min_and_max( + rate_pack.routing_byte_rate, + low_limit.routing_byte_rate, + high_limit.routing_byte_rate, + "routing_byte_rate", + error, + ); + error = check_min_and_max( + rate_pack.routing_service_rate, + low_limit.routing_service_rate, + high_limit.routing_service_rate, + "routing_service_rate", + error, + ); + error = check_min_and_max( + rate_pack.exit_byte_rate, + low_limit.exit_byte_rate, + high_limit.exit_byte_rate, + "exit_byte_rate", + error, + ); + error = check_min_and_max( + rate_pack.exit_service_rate, + low_limit.exit_service_rate, + high_limit.exit_service_rate, + "exit_service_rate", + error, + ); + if !error.is_empty() { Err(error) - } - else { + } else { Ok(rate_pack) } - }, + } Err(e) => Err(e), } } @@ -2454,18 +2494,30 @@ mod tests { let mut persistent_config = PersistentConfigurationMock::new() .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) .set_rate_pack_result(Ok(())) - .rate_pack_limits_result(Ok(( - RatePack::new(5, 5, 5, 5), - RatePack::new(7, 7, 7, 7) - ))); + .rate_pack_limits_result(Ok((RatePack::new(5, 5, 5, 5), RatePack::new(7, 7, 7, 7)))); - let result = configure_rate_pack(&make_simplified_multi_config(["--rate-pack", "4|4|4|4"]), &mut persistent_config); + let result = configure_rate_pack( + &make_simplified_multi_config(["--rate-pack", "4|4|4|4"]), + &mut persistent_config, + ); let expected_error = ConfiguratorError::new(vec![]) - .another_required("rate-pack", "Value of routing_byte_rate (4) is below the minimum allowed (5)") - .another_required("rate-pack", "Value of routing_service_rate (4) is below the minimum allowed (5)") - .another_required("rate-pack", "Value of exit_byte_rate (4) is below the minimum allowed (5)") - .another_required("rate-pack", "Value of exit_service_rate (4) is below the minimum allowed (5)"); + .another_required( + "rate-pack", + "Value of routing_byte_rate (4) is below the minimum allowed (5)", + ) + .another_required( + "rate-pack", + "Value of routing_service_rate (4) is below the minimum allowed (5)", + ) + .another_required( + "rate-pack", + "Value of exit_byte_rate (4) is below the minimum allowed (5)", + ) + .another_required( + "rate-pack", + "Value of exit_service_rate (4) is below the minimum allowed (5)", + ); assert_eq!(result, Err(expected_error)); } @@ -2474,18 +2526,30 @@ mod tests { let mut persistent_config = PersistentConfigurationMock::new() .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) .set_rate_pack_result(Ok(())) - .rate_pack_limits_result(Ok(( - RatePack::new(5, 5, 5, 5), - RatePack::new(7, 7, 7, 7) - ))); + .rate_pack_limits_result(Ok((RatePack::new(5, 5, 5, 5), RatePack::new(7, 7, 7, 7)))); - let result = configure_rate_pack(&make_simplified_multi_config(["--rate-pack", "8|8|8|8"]), &mut persistent_config); + let result = configure_rate_pack( + &make_simplified_multi_config(["--rate-pack", "8|8|8|8"]), + &mut persistent_config, + ); let expected_error = ConfiguratorError::new(vec![]) - .another_required("rate-pack", "Value of routing_byte_rate (8) is above the maximum allowed (7)") - .another_required("rate-pack", "Value of routing_service_rate (8) is above the maximum allowed (7)") - .another_required("rate-pack", "Value of exit_byte_rate (8) is above the maximum allowed (7)") - .another_required("rate-pack", "Value of exit_service_rate (8) is above the maximum allowed (7)"); + .another_required( + "rate-pack", + "Value of routing_byte_rate (8) is above the maximum allowed (7)", + ) + .another_required( + "rate-pack", + "Value of routing_service_rate (8) is above the maximum allowed (7)", + ) + .another_required( + "rate-pack", + "Value of exit_byte_rate (8) is above the maximum allowed (7)", + ) + .another_required( + "rate-pack", + "Value of exit_service_rate (8) is above the maximum allowed (7)", + ); assert_eq!(result, Err(expected_error)); } diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index e5179ab127..448c3e50ee 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -48,7 +48,8 @@ pub const ZERO_RATE_PACK: RatePack = RatePack { exit_service_rate: 0, }; -pub const DEFAULT_RATE_PACK_LIMITS: &str = "100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000"; +pub const DEFAULT_RATE_PACK_LIMITS: &str = + "100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000"; #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct RatePack { diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 4dffff8c5f..7c78d99a93 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -633,7 +633,7 @@ pub mod unshared_test_utils { }; let config = config.rate_pack_limits_result(Ok(( RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), - RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ))); config } diff --git a/node/src/test_utils/persistent_configuration_mock.rs b/node/src/test_utils/persistent_configuration_mock.rs index ec70cb3b25..afcb7428d7 100644 --- a/node/src/test_utils/persistent_configuration_mock.rs +++ b/node/src/test_utils/persistent_configuration_mock.rs @@ -765,7 +765,10 @@ impl PersistentConfigurationMock { self } - pub fn rate_pack_limits_result(self, result: Result<(RatePack, RatePack), PersistentConfigError>) -> Self { + pub fn rate_pack_limits_result( + self, + result: Result<(RatePack, RatePack), PersistentConfigError>, + ) -> Self { self.rate_pack_limits_results.borrow_mut().push(result); self } From 6f57d99bc0ddf10e674d6e8b10016d782a50a5c4 Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Thu, 30 Oct 2025 07:34:25 -0400 Subject: [PATCH 3/9] Review issues --- .../src/db_config/persistent_configuration.rs | 72 ++++++++++--------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 1c8acd666c..5119be6d2a 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -22,7 +22,7 @@ use masq_lib::constants::{HIGHEST_USABLE_PORT, LOWEST_USABLE_INSECURE_PORT}; use masq_lib::shared_schema::{ConfiguratorError, ParamError}; use masq_lib::utils::NeighborhoodModeLight; use masq_lib::utils::{to_string, AutomapProtocol}; -use regex::Regex; +use regex::{Captures, Regex}; use rustc_hex::{FromHex, ToHex}; use std::fmt::Display; use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; @@ -31,7 +31,7 @@ use websocket::url::Url; lazy_static! { static ref RATE_PACK_LIMIT_FORMAT: Regex = - Regex::new(r"^(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)\|(\d+)-(\d+)$").unwrap(); + Regex::new(r"^(\d{1,19})-(\d{1,19})\|(\d{1,19})-(\d{1,19})\|(\d{1,19})-(\d{1,19})\|(\d{1,19})-(\d{1,19})$").unwrap(); } #[derive(Clone, PartialEq, Eq, Debug)] @@ -588,53 +588,27 @@ impl PersistentConfiguration for PersistentConfigurationReal { "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!", ); let captures = RATE_PACK_LIMIT_FORMAT.captures(limits_string.as_str()) - .expect("Syntax error in rate_pack_limits value 'Booga!': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei."); + .expect(format!("Syntax error in rate_pack_limits value '{}': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.", limits_string).as_str()); let candidate = ( - RatePack { - routing_byte_rate: u64::from_str(captures.get(1).expect("blah").as_str()) - .expect("blah"), - routing_service_rate: u64::from_str(captures.get(3).expect("blah").as_str()) - .expect("blah"), - exit_byte_rate: u64::from_str(captures.get(5).expect("blah").as_str()) - .expect("blah"), - exit_service_rate: u64::from_str(captures.get(7).expect("blah").as_str()) - .expect("blah"), - }, - RatePack { - routing_byte_rate: u64::from_str(captures.get(2).expect("blah").as_str()) - .expect("blah"), - routing_service_rate: u64::from_str(captures.get(4).expect("blah").as_str()) - .expect("blah"), - exit_byte_rate: u64::from_str(captures.get(6).expect("blah").as_str()) - .expect("blah"), - exit_service_rate: u64::from_str(captures.get(8).expect("blah").as_str()) - .expect("blah"), - }, + Self::extract_candidate(&captures, 1), + Self::extract_candidate(&captures, 2) ); - let check_rate_pack_limit_order = |low: u64, high: u64, field_name: &str| { - if low >= high { - panic!( - "Rate pack limits should have low limits less than high limits, but {} limits are {}-{}", - field_name, low, high - ); - } - }; - check_rate_pack_limit_order( + Self::check_rate_pack_limit_order( candidate.0.routing_byte_rate, candidate.1.routing_byte_rate, "routing_byte_rate", ); - check_rate_pack_limit_order( + Self::check_rate_pack_limit_order( candidate.0.routing_service_rate, candidate.1.routing_service_rate, "routing_service_rate", ); - check_rate_pack_limit_order( + Self::check_rate_pack_limit_order( candidate.0.exit_byte_rate, candidate.1.exit_byte_rate, "exit_byte_rate", ); - check_rate_pack_limit_order( + Self::check_rate_pack_limit_order( candidate.0.exit_service_rate, candidate.1.exit_service_rate, "exit_service_rate", @@ -743,6 +717,34 @@ impl PersistentConfigurationReal { parameter_name ) } + + fn extract_candidate(captures: &Captures, start_index: usize) -> RatePack { + RatePack { + routing_byte_rate: Self::parse_capture(captures, start_index), + routing_service_rate: Self::parse_capture(captures, start_index + 2), + exit_byte_rate: Self::parse_capture(captures, start_index + 4), + exit_service_rate: Self::parse_capture(captures, start_index + 6), + } + } + + fn parse_capture(captures: &Captures, index: usize) -> u64 { + u64::from_str( + captures + .get(index) + .expect("Internal error: regex needs four captures") + .as_str(), + ) + .expect("Internal error: regex must require u64") + } + + fn check_rate_pack_limit_order(low: u64, high: u64, field_name: &str) { + if low >= high { + panic!( + "Rate pack limits should have low limits less than high limits, but {} limits are {}-{}", + field_name, low, high + ); + } + } } #[cfg(test)] From cad907e8bc758293aff831d739e5dde1e48192fc Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Thu, 30 Oct 2025 07:44:01 -0400 Subject: [PATCH 4/9] Starting Node validation --- node/src/neighborhood/gossip_acceptor.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index f99eba27d9..e85b537a63 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -305,6 +305,7 @@ impl DebutHandler { } None => (), } +todo!("Drive in a call to GossipAcceptorReal::validate_new_version() here"); let debut_node_key = database .add_node(debuting_node) .expect("Debuting Node suddenly appeared in database"); @@ -1412,6 +1413,10 @@ impl GossipAcceptorReal { debut_target_node_addr.clone(), )) } + + fn validate_new_version(node_record: &NodeRecord, neighborhood_database: &NeighborhoodDatabase) -> bool { + todo!("Test-drive me") + } } #[cfg(test)] From 84e31c5846a453202ef4d959de129fffbabba160 Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Sun, 9 Nov 2025 01:14:05 -0500 Subject: [PATCH 5/9] One test failing --- .../src/db_config/persistent_configuration.rs | 234 ++++++- node/src/neighborhood/gossip_acceptor.rs | 647 +++++++++++++----- node/src/neighborhood/mod.rs | 490 ++++++------- .../unprivileged_parse_args_configuration.rs | 34 +- node/src/sub_lib/neighborhood.rs | 84 +++ node/src/test_utils/database_utils.rs | 18 + node/src/test_utils/mod.rs | 8 +- .../persistent_configuration_mock.rs | 8 +- 8 files changed, 1073 insertions(+), 450 deletions(-) diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 5119be6d2a..4e47b2e188 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -14,7 +14,7 @@ use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::{CryptDE, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::cryptde_real::CryptDEReal; -use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack}; +use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack, RatePackLimits}; use crate::sub_lib::wallet::Wallet; use lazy_static::lazy_static; use masq_lib::blockchains::chains::Chain; @@ -26,8 +26,11 @@ use regex::{Captures, Regex}; use rustc_hex::{FromHex, ToHex}; use std::fmt::Display; use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; +use std::path::PathBuf; use std::str::FromStr; use websocket::url::Url; +use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; +use crate::sub_lib::utils::db_connection_launch_panic; lazy_static! { static ref RATE_PACK_LIMIT_FORMAT: Regex = @@ -166,7 +169,7 @@ pub trait PersistentConfiguration { fn payment_thresholds(&self) -> Result; fn set_payment_thresholds(&mut self, curves: String) -> Result<(), PersistentConfigError>; fn rate_pack(&self) -> Result; - fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError>; + fn rate_pack_limits(&self) -> Result; fn set_rate_pack(&mut self, rate_pack: String) -> Result<(), PersistentConfigError>; fn scan_intervals(&self) -> Result; fn set_scan_intervals(&mut self, intervals: String) -> Result<(), PersistentConfigError>; @@ -578,7 +581,7 @@ impl PersistentConfiguration for PersistentConfigurationReal { self.simple_set_method("rate_pack", rate_pack) } - fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError> { + fn rate_pack_limits(&self) -> Result { let limits_string = self .get("rate_pack_limits") .expect( @@ -589,28 +592,28 @@ impl PersistentConfiguration for PersistentConfigurationReal { ); let captures = RATE_PACK_LIMIT_FORMAT.captures(limits_string.as_str()) .expect(format!("Syntax error in rate_pack_limits value '{}': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.", limits_string).as_str()); - let candidate = ( + let candidate = RatePackLimits::new( Self::extract_candidate(&captures, 1), Self::extract_candidate(&captures, 2) ); Self::check_rate_pack_limit_order( - candidate.0.routing_byte_rate, - candidate.1.routing_byte_rate, + candidate.lo.routing_byte_rate, + candidate.hi.routing_byte_rate, "routing_byte_rate", ); Self::check_rate_pack_limit_order( - candidate.0.routing_service_rate, - candidate.1.routing_service_rate, + candidate.lo.routing_service_rate, + candidate.hi.routing_service_rate, "routing_service_rate", ); Self::check_rate_pack_limit_order( - candidate.0.exit_byte_rate, - candidate.1.exit_byte_rate, + candidate.lo.exit_byte_rate, + candidate.hi.exit_byte_rate, "exit_byte_rate", ); Self::check_rate_pack_limit_order( - candidate.0.exit_service_rate, - candidate.1.exit_service_rate, + candidate.lo.exit_service_rate, + candidate.hi.exit_service_rate, "exit_service_rate", ); Ok(candidate) @@ -747,6 +750,201 @@ impl PersistentConfigurationReal { } } +pub struct PersistentConfigurationInvalid {} + +impl PersistentConfiguration for PersistentConfigurationInvalid { + fn blockchain_service_url(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_blockchain_service_url(&mut self, _url: &str) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn current_schema_version(&self) -> String { + PersistentConfigurationInvalid::invalid() + } + fn chain_name(&self) -> String { + PersistentConfigurationInvalid::invalid() + } + fn check_password( + &self, + _db_password_opt: Option, + ) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn change_password( + &mut self, + _old_password_opt: Option, + _new_password: &str, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn consuming_wallet(&self, _db_password: &str) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn consuming_wallet_private_key( + &self, + _db_password: &str, + ) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn clandestine_port(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_clandestine_port(&mut self, _port: u16) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn cryptde( + &self, + _db_password: &str, + ) -> Result>, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_cryptde( + &mut self, + _cryptde: &dyn CryptDE, + _db_password: &str, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn earning_wallet(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn earning_wallet_address(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn gas_price(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_gas_price(&mut self, _gas_price: u64) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn mapping_protocol(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_mapping_protocol( + &mut self, + _value_opt: Option, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn min_hops(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_min_hops(&mut self, _value: Hops) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn neighborhood_mode(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_neighborhood_mode( + &mut self, + _value: NeighborhoodModeLight, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn past_neighbors( + &self, + _db_password: &str, + ) -> Result>, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_past_neighbors( + &mut self, + _node_descriptors_opt: Option>, + _db_password: &str, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn start_block(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_start_block(&mut self, _value_opt: Option) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn max_block_count(&self) -> Result, PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_max_block_count(&mut self, _value_opt: Option) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_start_block_from_txn( + &mut self, + _value_opt: Option, + _transaction: &mut TransactionSafeWrapper, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn set_wallet_info( + &mut self, + _consuming_wallet_private_key: &str, + _earning_wallet_address: &str, + _db_password: &str, + ) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn payment_thresholds(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_payment_thresholds(&mut self, _curves: String) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn rate_pack(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_rate_pack(&mut self, _rate_pack: String) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + fn rate_pack_limits(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn scan_intervals(&self) -> Result { + PersistentConfigurationInvalid::invalid() + } + fn set_scan_intervals(&mut self, _intervals: String) -> Result<(), PersistentConfigError> { + PersistentConfigurationInvalid::invalid() + } + arbitrary_id_stamp_in_trait!(); +} + +impl PersistentConfigurationInvalid { + pub fn new() -> Self { + Self {} + } + + fn invalid() -> ! { + panic!("PersistentConfiguration is uninitialized") + } +} + +pub trait PersistentConfigurationFactory { + fn make(&self) -> Box; +} + +pub struct PersistentConfigurationFactoryReal { + data_directory: PathBuf +} + +impl PersistentConfigurationFactory for PersistentConfigurationFactoryReal { + fn make(&self) -> Box { + let db_initializer: &dyn DbInitializer = &DbInitializerReal::default(); + let conn = db_initializer + .initialize( + &self.data_directory.as_path(), + DbInitializationConfig::panic_on_migration(), + ) + .unwrap_or_else(|err| db_connection_launch_panic(err, &self.data_directory)); + Box::new(PersistentConfigurationReal::from(conn)) + } +} + +impl PersistentConfigurationFactoryReal { + pub fn new(data_directory: PathBuf) -> Self { + Self { + data_directory + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -2363,7 +2561,7 @@ mod tests { persistent_config_plain_data_assertions_for_simple_get_method!( "rate_pack_limits", "100-200|300-400|500000000000000000-600000000000000000|700-800", - ( + RatePackLimits::new( RatePack { routing_byte_rate: 100, routing_service_rate: 300, @@ -2397,7 +2595,7 @@ mod tests { "rate_pack_limits", "Booga!", // Irrelevant but necessary - ( + RatePackLimits::new( RatePack { routing_byte_rate: 0, routing_service_rate: 0, @@ -2423,7 +2621,7 @@ mod tests { "rate_pack_limits", "1-1|0-1|0-1|0-1", // Irrelevant but necessary - ( + RatePackLimits::new( RatePack { routing_byte_rate: 0, routing_service_rate: 0, @@ -2449,7 +2647,7 @@ mod tests { "rate_pack_limits", "0-1|1-1|0-1|0-1", // Irrelevant but necessary - ( + RatePackLimits::new( RatePack { routing_byte_rate: 0, routing_service_rate: 0, @@ -2475,7 +2673,7 @@ mod tests { "rate_pack_limits", "0-1|0-1|1-1|0-1", // Irrelevant but necessary - ( + RatePackLimits::new( RatePack { routing_byte_rate: 0, routing_service_rate: 0, @@ -2501,7 +2699,7 @@ mod tests { "rate_pack_limits", "0-1|0-1|0-1|1-1", // Irrelevant but necessary - ( + RatePackLimits::new( RatePack { routing_byte_rate: 0, routing_service_rate: 0, diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index e85b537a63..ff47c933e9 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -7,15 +7,14 @@ use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, Neighborh use crate::neighborhood::node_record::NodeRecord; use crate::neighborhood::UserExitPreferences; use crate::sub_lib::cryptde::{CryptDE, PublicKey}; -use crate::sub_lib::neighborhood::{ - ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, -}; +use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, RatePackLimits}; use crate::sub_lib::node_addr::NodeAddr; use masq_lib::logger::Logger; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::net::{IpAddr, SocketAddr}; use std::time::{Duration, SystemTime}; +use crate::db_config::persistent_configuration::PersistentConfiguration; /// Note: if you decide to change this, make sure you test thoroughly. Values less than 5 may lead /// to inability to grow the network beyond a very small size; values greater than 5 may lead to @@ -33,8 +32,6 @@ pub enum GossipAcceptanceResult { Reply(Gossip_0v1, PublicKey, NodeAddr), // The incoming Gossip was proper, and we tried to accept it, but couldn't. Failed(GossipFailure_0v1, PublicKey, NodeAddr), - // The incoming Gossip contained nothing we didn't know. Don't send out any Gossip because of it. - Ignored, // Gossip was ignored because it was evil: ban the sender of the Gossip as a malefactor. Ban(String), } @@ -64,10 +61,11 @@ trait GossipHandler: NamedType + Send /* Send because lazily-written tests requi agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult; + ) -> Vec; } struct DebutHandler { + rate_pack_limits: RatePackLimits, logger: Logger, } @@ -150,7 +148,7 @@ impl GossipHandler for DebutHandler { mut agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let source_agr = { let mut agr = agrs.remove(0); // empty Gossip shouldn't get here if agr.node_addr_opt.is_none() { @@ -158,6 +156,14 @@ impl GossipHandler for DebutHandler { } agr }; + if let Err(message) = GossipAcceptorReal::validate_new_version( + &source_agr, + format!("Debut from {} at {}", source_agr.inner.public_key, gossip_source), + &self.rate_pack_limits, + &self.logger + ) { + return vec![GossipAcceptanceResult::Ban(message)]; + } let source_key = source_agr.inner.public_key.clone(); let source_node_addr = source_agr .node_addr_opt @@ -178,11 +184,11 @@ impl GossipHandler for DebutHandler { preferred_key, preferred_ip, ); - return GossipAcceptanceResult::Reply( + return vec![GossipAcceptanceResult::Reply( Self::make_pass_gossip(database, preferred_key), source_key, source_node_addr, - ); + )]; } if let Ok(result) = self.try_accept_debut( cryptde, @@ -191,7 +197,7 @@ impl GossipHandler for DebutHandler { gossip_source, neighborhood_metadata.user_exit_preferences_opt, ) { - return result; + return vec![result]; } debug!(self.logger, "Seeking neighbor for Pass"); let lcn_key = match Self::find_least_connected_half_neighbor_excluding( @@ -203,14 +209,14 @@ impl GossipHandler for DebutHandler { "Neighbor count at maximum, but no non-common neighbors. DebutHandler is reluctantly ignoring debut from {} at {}", source_key, source_node_addr ); - return GossipAcceptanceResult::Failed( + return vec![GossipAcceptanceResult::Failed( GossipFailure_0v1::NoSuitableNeighbors, (&source_agr.inner.public_key).clone(), (&source_agr .node_addr_opt .expect("Debuter's NodeAddr disappeared")) .clone(), - ); + )]; } Some(key) => key, }; @@ -230,17 +236,20 @@ impl GossipHandler for DebutHandler { lcn_key, lcn_ip_str ); - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( Self::make_pass_gossip(database, lcn_key), source_key, source_node_addr, - ) + )] } } impl DebutHandler { - fn new(logger: Logger) -> DebutHandler { - DebutHandler { logger } + fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> DebutHandler { + DebutHandler { + rate_pack_limits: rate_pack_limits.clone(), + logger + } } fn find_more_appropriate_neighbor<'b>( @@ -305,7 +314,6 @@ impl DebutHandler { } None => (), } -todo!("Drive in a call to GossipAcceptorReal::validate_new_version() here"); let debut_node_key = database .add_node(debuting_node) .expect("Debuting Node suddenly appeared in database"); @@ -580,7 +588,7 @@ impl GossipHandler for PassHandler { agrs: Vec, _gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let pass_agr = &agrs[0]; // empty Gossip shouldn't get here let pass_target_node_addr: NodeAddr = pass_agr .node_addr_opt @@ -609,21 +617,21 @@ impl GossipHandler for PassHandler { }; let mut hash_map = self.previous_pass_targets.borrow_mut(); - let gossip_acceptance_result = match hash_map.get_mut(&pass_target_ip_addr) { + let gossip_acceptance_results = match hash_map.get_mut(&pass_target_ip_addr) { None => match neighborhood_metadata .connection_progress_peers .contains(&pass_target_ip_addr) { true => { send_cpm(ConnectionProgressEvent::PassLoopFound); - GossipAcceptanceResult::Ignored + vec![] } false => { hash_map.insert(pass_target_ip_addr, SystemTime::now()); send_cpm(ConnectionProgressEvent::PassGossipReceived( pass_target_ip_addr, )); - gossip_acceptance_reply() + vec![gossip_acceptance_reply()] } }, Some(timestamp) => { @@ -633,16 +641,16 @@ impl GossipHandler for PassHandler { *timestamp = SystemTime::now(); if duration_since <= PASS_GOSSIP_EXPIRED_TIME { send_cpm(ConnectionProgressEvent::PassLoopFound); - GossipAcceptanceResult::Ignored + vec![] } else { send_cpm(ConnectionProgressEvent::PassGossipReceived( pass_target_ip_addr, )); - gossip_acceptance_reply() + vec![gossip_acceptance_reply()] } } }; - gossip_acceptance_result + gossip_acceptance_results } } @@ -655,6 +663,7 @@ impl PassHandler { } struct IntroductionHandler { + rate_pack_limits: RatePackLimits, logger: Logger, } @@ -701,12 +710,36 @@ impl GossipHandler for IntroductionHandler { agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { if database.root().full_neighbor_keys(database).len() >= MAX_DEGREE { - GossipAcceptanceResult::Ignored + vec![] } else { let (introducer, introducee) = Self::identify_players(agrs, gossip_source) .expect("Introduction not properly qualified"); + let mut introducer_ban_message = if let Err(message) = GossipAcceptorReal::validate_new_version( + &introducer, + format!( + "Introducer {} from {}", + introducer.inner.public_key, gossip_source + ), + &self.rate_pack_limits, + &self.logger + ) { + Some(message) + } + else { + None + }; + let ignore_introducee = GossipAcceptorReal::validate_new_version( + &introducee, + format!( + "Introducee {} at {}", + introducee.inner.public_key, + introducee.node_addr_opt.as_ref().expect("NodeAddr disappeared").ip_addr(), + ), + &self.rate_pack_limits, + &self.logger + ).is_err(); let introducer_key = introducer.inner.public_key.clone(); let introducer_ip_addr = introducer .node_addr_opt @@ -718,39 +751,59 @@ impl GossipHandler for IntroductionHandler { .as_ref() .expect("IP Address not found for the Node Addr.") .ip_addr(); - match self.update_database( - database, - cryptde, - introducer, - neighborhood_metadata.user_exit_preferences_opt, - ) { - Ok(_) => (), - Err(e) => { - return GossipAcceptanceResult::Ban(format!( - "Introducer {} tried changing immutable characteristic: {}", - introducer_key, e - )); + if introducer_ban_message.is_none() { + match self.update_database( + database, + cryptde, + introducer, + neighborhood_metadata.user_exit_preferences_opt, + ) { + Ok(_) => (), + Err(e) => { + introducer_ban_message = Some(format!( + "Introducer {} tried changing immutable characteristic: {}", + introducer_key, e + )); + } } } - let connection_progress_message = ConnectionProgressMessage { - peer_addr: introducer_ip_addr, - event: ConnectionProgressEvent::IntroductionGossipReceived(introducee_ip_addr), - }; - neighborhood_metadata - .cpm_recipient - .try_send(connection_progress_message) - .expect("Neighborhood is dead"); - let (debut, target_key, target_node_addr) = - GossipAcceptorReal::make_debut_triple(database, &introducee) - .expect("Introduction not properly qualified"); - GossipAcceptanceResult::Reply(debut, target_key, target_node_addr) + + if !ignore_introducee { + let connection_progress_message = ConnectionProgressMessage { + peer_addr: introducer_ip_addr, + event: ConnectionProgressEvent::IntroductionGossipReceived(introducee_ip_addr), + }; + neighborhood_metadata + .cpm_recipient + .try_send(connection_progress_message) + .expect("Neighborhood is dead"); + } + + if ignore_introducee { + if let Some(message) = introducer_ban_message { + vec![GossipAcceptanceResult::Ban(message)] + } else { + vec![GossipAcceptanceResult::Accepted] + } + } + else { + let (debut, target_key, target_node_addr) = + GossipAcceptorReal::make_debut_triple(database, &introducee) + .expect("Introduction not properly qualified"); + let mut result = vec![]; + if let Some(message) = introducer_ban_message { + result.push(GossipAcceptanceResult::Ban(message)); + } + result.push(GossipAcceptanceResult::Reply(debut, target_key, target_node_addr)); + result + } } } } impl IntroductionHandler { - fn new(logger: Logger) -> IntroductionHandler { - IntroductionHandler { logger } + fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> IntroductionHandler { + IntroductionHandler { rate_pack_limits: rate_pack_limits.clone(), logger } } fn verify_size(agrs: &[AccessibleGossipRecord]) -> Option { @@ -1017,7 +1070,7 @@ impl GossipHandler for StandardGossipHandler { agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); @@ -1050,13 +1103,13 @@ impl GossipHandler for StandardGossipHandler { .try_send(cpm) .unwrap_or_else(|e| panic!("Neighborhood is dead: {}", e)); } - GossipAcceptanceResult::Accepted + vec![GossipAcceptanceResult::Accepted] } else { debug!( self.logger, "Gossip contained nothing new: StandardGossipHandler is ignoring it" ); - GossipAcceptanceResult::Ignored + vec![] } } } @@ -1302,7 +1355,7 @@ impl GossipHandler for RejectHandler { _agrs: Vec, _gossip_source: SocketAddr, _neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { panic!("Should never be called") } } @@ -1320,7 +1373,7 @@ pub trait GossipAcceptor: Send /* Send because lazily-written tests require it * agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult; + ) -> Vec; } pub struct GossipAcceptorReal { @@ -1336,7 +1389,7 @@ impl GossipAcceptor for GossipAcceptorReal { agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let (qualification, handler_ref) = self .gossip_handlers .iter() @@ -1361,19 +1414,25 @@ impl GossipAcceptor for GossipAcceptorReal { Qualification::Unmatched => { panic!("Nothing in gossip_handlers returned Matched or Malformed") } - Qualification::Malformed(reason) => GossipAcceptanceResult::Ban(reason), + Qualification::Malformed(reason) => vec![GossipAcceptanceResult::Ban(reason)], } } } impl GossipAcceptorReal { - pub fn new(cryptde: Box) -> GossipAcceptorReal { + pub fn new( + cryptde: Box, + persistent_config: &dyn PersistentConfiguration + ) -> GossipAcceptorReal { + let rate_pack_limits = &persistent_config.rate_pack_limits() + .expect("RatePackLimits should be set"); + let logger = Logger::new("GossipAcceptor"); GossipAcceptorReal { gossip_handlers: vec![ - Box::new(DebutHandler::new(logger.clone())), + Box::new(DebutHandler::new(rate_pack_limits, logger.clone())), Box::new(PassHandler::new()), - Box::new(IntroductionHandler::new(logger.clone())), + Box::new(IntroductionHandler::new(rate_pack_limits, logger.clone())), Box::new(StandardGossipHandler::new(logger.clone())), Box::new(RejectHandler::new()), ], @@ -1414,8 +1473,47 @@ impl GossipAcceptorReal { )) } - fn validate_new_version(node_record: &NodeRecord, neighborhood_database: &NeighborhoodDatabase) -> bool { - todo!("Test-drive me") + fn validate_new_version( + agr: &AccessibleGossipRecord, + agr_description: String, + rate_pack_limits: &RatePackLimits, + logger: &Logger, + ) -> Result<(), String> { + match rate_pack_limits.analyze(&agr.inner.rate_pack) { + Ok(_) => Ok(()), + Err(e) => { + let message = format!( + "{} rejected due to rate pack limit violation: {:?}", + agr_description, e + ); + warning!(logger, "{}", message.as_str()); + Err(message) + } + } + } +} + +pub struct GossipAcceptorInvalid {} + +impl GossipAcceptor for GossipAcceptorInvalid { + fn handle( + &self, + _database: &mut NeighborhoodDatabase, + _agrs: Vec, + _gossip_source: SocketAddr, + _neighborhood_metadata: NeighborhoodMetadata, + ) -> Vec { + Self::invalid() + } +} + +impl GossipAcceptorInvalid { + pub fn new() -> Self { + Self {} + } + + fn invalid() -> ! { + panic!("GossipAcceptor was never initialized"); } } @@ -1431,7 +1529,7 @@ mod tests { UNREACHABLE_COUNTRY_PENALTY, }; use crate::sub_lib::cryptde_null::CryptDENull; - use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage}; + use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, RatePack}; 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, @@ -1452,6 +1550,7 @@ mod tests { use std::ops::{Add, Sub}; use std::str::FromStr; use std::time::Duration; + use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; lazy_static! { static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); @@ -1480,6 +1579,15 @@ mod tests { } } + impl RatePackLimits { + pub fn test_default() -> RatePackLimits { + Self { + lo: RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), + hi: RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), + } + } + } + #[test] fn proper_debut_of_accepting_node_with_populated_database_is_identified_and_handled() { let (gossip, new_node, gossip_source_opt) = make_debut(2345, Mode::Standard); @@ -1489,7 +1597,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_key); let cryptde = CryptDENull::from(db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let qualifies_result = subject.qualifies(&db, &agrs_vec.as_slice(), gossip_source_opt.clone()); @@ -1508,11 +1616,11 @@ mod tests { .build(); assert_eq!( handle_result, - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( introduction, new_node.public_key().clone(), new_node.node_addr_opt().unwrap(), - ), + )], ); } @@ -1525,7 +1633,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_key); let cryptde = CryptDENull::from(db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let qualifies_result = subject.qualifies(&db, agrs_vec.as_slice(), gossip_source.clone()); let handle_result = subject.handle( @@ -1543,11 +1651,11 @@ mod tests { .build(); assert_eq!( handle_result, - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( introduction, new_node.public_key().clone(), NodeAddr::from(&gossip_source), - ), + )], ); } @@ -1579,7 +1687,7 @@ mod tests { .node(src_db.root().public_key(), true) .build(); let agrs_vec: Vec = gossip.try_into().unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let result = subject.handle( &cryptde, @@ -1589,7 +1697,7 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); } #[test] @@ -1611,7 +1719,7 @@ mod tests { .node(src_db.root().public_key(), true) .build(); let agrs_vec: Vec = gossip.try_into().unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let result = subject.handle( &cryptde, @@ -1623,18 +1731,18 @@ mod tests { assert_eq!( result, - GossipAcceptanceResult::Failed( + vec![GossipAcceptanceResult::Failed( GossipFailure_0v1::NoSuitableNeighbors, src_root.public_key().clone(), src_root.node_addr_opt().unwrap(), - ) + )] ); } #[test] fn debut_with_node_addr_not_accepting_connections_is_rejected() { let (mut gossip, _j, gossip_source) = make_debut(2345, Mode::OriginateOnly); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); gossip.node_records[0].node_addr_opt = Some(NodeAddr::new( &IpAddr::from_str("1.2.3.4").unwrap(), &[1234], @@ -1655,7 +1763,7 @@ mod tests { fn debut_without_node_addr_accepting_connections_is_rejected() { let (mut gossip, _j, gossip_source) = make_debut(2345, Mode::Standard); gossip.node_records[0].node_addr_opt = None; - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&make_meaningless_db(), agrs_vec.as_slice(), gossip_source); @@ -1673,7 +1781,7 @@ mod tests { let (mut gossip, _, gossip_source) = make_debut(2345, Mode::Standard); gossip.node_records[0].node_addr_opt = Some(NodeAddr::new(&IpAddr::from_str("1.2.3.4").unwrap(), &[])); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&make_meaningless_db(), agrs_vec.as_slice(), gossip_source); @@ -1695,7 +1803,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), neighbor_key); db.add_node(new_node.clone()).unwrap(); let agrs_vec: Vec = gossip.try_into().unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let result = subject.qualifies(&db, agrs_vec.as_slice(), gossip_source); @@ -1723,7 +1831,7 @@ mod tests { .build() .try_into() .unwrap(); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let result = subject.handle( &dest_cryptde, @@ -1733,7 +1841,40 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); + } + + #[test] + fn debut_is_rejected_when_validation_fails() { + init_test_logging(); + let test_name = "debut_is_rejected_when_validation_fails"; + let root_node = make_node_record(1234, true); + let root_node_cryptde = CryptDENull::from(&root_node.public_key(), TEST_DEFAULT_CHAIN); + let mut src_db = db_from_node(&root_node); + let agrs_vec: Vec = GossipBuilder::new(&src_db) + .node(root_node.public_key(), true) + .build() + .try_into() + .unwrap(); + let subject = DebutHandler::new( + &RatePackLimits::new( + RatePack::new(0, 0, 0, 0), + RatePack::new(0, 0, 0, 0) + ), + Logger::new(test_name) + ); + + let result = subject.handle( + &root_node_cryptde, + &mut src_db, + agrs_vec, + root_node.node_addr_opt().clone().unwrap().into(), + make_default_neighborhood_metadata(), + ); + + let message = r#"Debut from AQIDBA at 1.2.3.4:1234 rejected due to rate pack limit violation: ConfiguratorError { param_errors: [ParamError { parameter: "rate-pack", reason: "Value of routing_byte_rate (1235) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of routing_service_rate (1434) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of exit_byte_rate (1237) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of exit_service_rate (1634) is above the maximum allowed (0)" }] }"#.to_string(); + assert_eq!(result, vec![GossipAcceptanceResult::Ban(message.clone())]); + TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); } #[test] @@ -1749,7 +1890,7 @@ mod tests { half_neighbor_debutant.public_key(), ); let logger = Logger::new("Debut test"); - let subject = DebutHandler::new(logger); + let subject = DebutHandler::new(&RatePackLimits::test_default(), logger); let neighborhood_metadata = make_default_neighborhood_metadata(); let counter_debut = subject @@ -1794,12 +1935,12 @@ mod tests { .node(dest_db.root().public_key(), true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + handle_result, + vec![GossipAcceptanceResult::Reply( debut, pass_target.public_key().clone(), pass_target.node_addr_opt().unwrap().clone(), - ), - handle_result + )], ); } @@ -1842,7 +1983,10 @@ mod tests { #[test] fn gossip_containing_other_than_two_records_is_not_an_introduction() { let (gossip, _, gossip_source) = make_debut(2345, Mode::Standard); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&make_meaningless_db(), agrs_vec.as_slice(), gossip_source); @@ -1857,7 +2001,10 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); dest_db.add_node(not_introducee.clone()).unwrap(); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1871,7 +2018,10 @@ mod tests { let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); gossip.node_records[0].node_addr_opt = None; - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1886,7 +2036,10 @@ mod tests { let dest_db = db_from_node(&dest_root); gossip.node_records[0].node_addr_opt = Some(NodeAddr::new(&IpAddr::from_str("2.3.4.5").unwrap(), &[])); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1903,7 +2056,10 @@ mod tests { let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); gossip.node_records[1].node_addr_opt = None; - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1918,7 +2074,10 @@ mod tests { let dest_db = db_from_node(&dest_root); gossip.node_records[1].node_addr_opt = Some(NodeAddr::new(&IpAddr::from_str("3.4.5.6").unwrap(), &[])); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1941,7 +2100,10 @@ mod tests { &IpAddr::from_str("4.5.6.7").unwrap(), &[4567], )); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1958,7 +2120,10 @@ mod tests { &IpAddr::from_str("2.3.4.5").unwrap(), &[2345], )); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -1976,7 +2141,10 @@ mod tests { let (gossip, gossip_source) = make_introduction(2345, 3456); let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[0].inner.public_key = dest_root.public_key().clone(); let introducer_key = &agrs[0].inner.public_key; @@ -1997,7 +2165,10 @@ mod tests { let (gossip, _) = make_introduction(2345, 3456); let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[0].node_addr_opt = dest_root.node_addr_opt(); let introducer_key = &agrs[0].inner.public_key; @@ -2019,7 +2190,10 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); let introducer_key = &agrs[0].inner.public_key; dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); @@ -2048,10 +2222,14 @@ mod tests { let after = time_t_timestamp(); assert_eq!(qualifies_result, Qualification::Matched); - assert_eq!( - handle_result, - GossipAcceptanceResult::Ban(format!("Introducer {} tried changing immutable characteristic: Updating a NodeRecord must not change its node_addr_opt: 4.5.6.7:4567 -> 2.3.4.5:2345", introducer_key)), + assert_eq!(&handle_result[0], + &GossipAcceptanceResult::Ban(format!("Introducer {} tried changing immutable characteristic: Updating a NodeRecord must not change its node_addr_opt: 4.5.6.7:4567 -> 2.3.4.5:2345", introducer_key)) ); + match &handle_result[1] { + GossipAcceptanceResult::Reply(_, _, _) => (), + other => panic!("Expected Reply, got {:?}", other), + }; + assert_eq!(handle_result.len(), 2); assert_node_records_eq( dest_db.node_by_key_mut(introducer_key).unwrap(), &introducer_before_gossip, @@ -2065,7 +2243,10 @@ mod tests { let (gossip, gossip_source) = make_introduction(2345, 3456); let dest_root = make_node_record_f(7878, false, false, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, &agrs, gossip_source); @@ -2079,7 +2260,10 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); let mut expected_introducer = NodeRecord::from(&agrs[0]); expected_introducer.metadata.country_undesirability = COUNTRY_UNDESIRABILITY_FACTOR; @@ -2109,12 +2293,12 @@ mod tests { .node(dest_db.root().public_key(), true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + handle_result, + vec![GossipAcceptanceResult::Reply( debut, agrs[1].inner.public_key.clone(), agrs[1].node_addr_opt.clone().unwrap(), - ), - handle_result + )], ); let result_introducer: &NodeRecord = dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); @@ -2139,7 +2323,10 @@ mod tests { dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), &key); } let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); let handle_result = subject.handle( @@ -2150,7 +2337,7 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(handle_result, GossipAcceptanceResult::Ignored); + assert_eq!(handle_result, vec![]); } #[test] @@ -2160,7 +2347,10 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); dest_db @@ -2183,12 +2373,12 @@ mod tests { .node(dest_db.root().public_key(), true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + handle_result, + vec![GossipAcceptanceResult::Reply( debut, agrs[1].inner.public_key.clone(), agrs[1].node_addr_opt.clone().unwrap(), - ), - handle_result + )], ); let result_introducer: &NodeRecord = dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); @@ -2197,13 +2387,13 @@ mod tests { expected_introducer.resign(); assert_eq!(result_introducer, &expected_introducer); assert_eq!( - true, dest_db .root() - .has_half_neighbor(expected_introducer.public_key()) + .has_half_neighbor(expected_introducer.public_key()), + true, ); - assert_eq!(1, dest_db.root().version()); - assert_eq!(None, dest_db.node_by_key(&agrs[1].inner.public_key)); + assert_eq!(dest_db.root().version(), 1); + assert_eq!(dest_db.node_by_key(&agrs[1].inner.public_key), None); } #[test] @@ -2220,7 +2410,10 @@ mod tests { dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), half_neighbor_key); } let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let agrs: Vec = gossip.try_into().unwrap(); dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), &agrs[0].inner.public_key); @@ -2239,12 +2432,12 @@ mod tests { .node(dest_db.root().public_key(), true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + handle_result, + vec![GossipAcceptanceResult::Reply( debut, agrs[1].inner.public_key.clone(), agrs[1].node_addr_opt.clone().unwrap(), - ), - handle_result + )], ); let result_introducer: &NodeRecord = @@ -2254,13 +2447,105 @@ mod tests { expected_introducer.resign(); assert_eq!(result_introducer, &expected_introducer); assert_eq!( - true, dest_db .root() - .has_half_neighbor(expected_introducer.public_key()) + .has_half_neighbor(expected_introducer.public_key()), + true, + ); + assert_eq!(dest_db.root().version(), 0); + assert_eq!(dest_db.node_by_key(&agrs[1].inner.public_key), None); + } + + #[test] + fn introducer_that_fails_validation_is_rejected() { + init_test_logging(); + let test_name = "introducer_that_fails_validation_is_rejected"; + let (gossip, gossip_source) = make_introduction(2345, 3456); + let dest_root = make_node_record(7878, true); + let mut dest_db = db_from_node(&dest_root); + // These don't count because they're half-only neighbors. Will they be ignored? + for idx in 0..MAX_DEGREE { + let half_neighbor_key = &dest_db + .add_node(make_node_record(4000 + idx as u16, true)) + .unwrap(); + dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), half_neighbor_key); + } + let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); + let subject = IntroductionHandler::new( + &RatePackLimits::new( + RatePack::new(100, 100, 100, 100), + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + ), + Logger::new(test_name) + ); + let mut agrs: Vec = gossip.try_into().unwrap(); + agrs[0].inner.rate_pack = RatePack::new(0, 0, 0, 0); // Invalid rate pack + dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); + dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), &agrs[0].inner.public_key); + + let handle_result = subject.handle( + &cryptde, + &mut dest_db, + agrs.clone(), + gossip_source, + make_default_neighborhood_metadata(), + ); + + let message = format!("Introducer {} from {} rejected due to rate pack limit violation: ConfiguratorError {{ param_errors: [ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_service_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_service_rate (0) is below the minimum allowed (100)\" }}] }}", agrs[0].inner.public_key, gossip_source); + assert_eq!(handle_result[0], GossipAcceptanceResult::Ban(message.clone())); + match &handle_result[1] { + &GossipAcceptanceResult::Reply(_, _, _) => (), + other => panic!("Expected Reply as second result, but got {:?}", other), + } + assert_eq!(handle_result.len(), 2); + TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); + } + + #[test] + fn introducee_that_fails_validation_is_rejected() { + init_test_logging(); + let test_name = "introducee_that_fails_validation_is_rejected"; + let (gossip, gossip_source) = make_introduction(2345, 3456); + let dest_root = make_node_record(7878, true); + let mut dest_db = db_from_node(&dest_root); + // These don't count because they're half-only neighbors. Will they be ignored? + for idx in 0..MAX_DEGREE { + let half_neighbor_key = &dest_db + .add_node(make_node_record(4000 + idx as u16, true)) + .unwrap(); + dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), half_neighbor_key); + } + let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); + let subject = IntroductionHandler::new( + &RatePackLimits::new( + RatePack::new(100, 100, 100, 100), + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + ), + Logger::new(test_name) + ); + let mut agrs: Vec = gossip.try_into().unwrap(); + agrs[1].inner.rate_pack = RatePack::new(0, 0, 0, 0); // Invalid rate pack + dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); + dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), &agrs[0].inner.public_key); + + let handle_result = subject.handle( + &cryptde, + &mut dest_db, + agrs.clone(), + gossip_source, + make_default_neighborhood_metadata(), + ); + + let message = format!( + "Introducee {} at {} rejected due to rate pack limit violation: ConfiguratorError {{ param_errors: [ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_service_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_service_rate (0) is below the minimum allowed (100)\" }}] }}", + agrs[1].inner.public_key, + agrs[1].node_addr_opt.as_ref().unwrap().ip_addr() + ); + assert_eq!( + handle_result, + vec![GossipAcceptanceResult::Accepted], ); - assert_eq!(0, dest_db.root().version()); - assert_eq!(None, dest_db.node_by_key(&agrs[1].inner.public_key)); + TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); } #[test] @@ -2507,26 +2792,26 @@ mod tests { ); assert_eq!( + 0u32, dest_db .node_by_key(node_a.public_key()) .unwrap() .metadata .country_undesirability, - 0u32 ); assert_eq!( + UNREACHABLE_COUNTRY_PENALTY, 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!(qualifies_result, Qualification::Matched); + assert_eq!(handle_result, vec![GossipAcceptanceResult::Accepted]); assert_eq!( &src_db.root().inner, - &dest_db.node_by_key(src_root.public_key()).unwrap().inner + &dest_db.node_by_key(src_root.public_key()).unwrap().inner, ); assert!(dest_db.has_full_neighbor(dest_db.root().public_key(), src_db.root().public_key())); assert_eq!( @@ -2754,7 +3039,7 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); } fn assert_compute_patch(db_patch_size: u8) { @@ -2819,7 +3104,7 @@ mod tests { assert_eq!(system.run(), 0); let recording = recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); } #[test] @@ -2857,7 +3142,7 @@ mod tests { System::current().stop(); assert_eq!(system.run(), 0); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); let recording = recording_arc.lock().unwrap(); assert_eq!(recording.len(), 1); let received_message = recording.get_record::(0); @@ -2904,7 +3189,7 @@ mod tests { System::current().stop(); assert_eq!(system.run(), 0); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); let recording = recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); } @@ -2945,7 +3230,7 @@ mod tests { System::current().stop(); assert_eq!(system.run(), 0); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); let recording = recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); } @@ -2986,7 +3271,12 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); + } + + #[test] + fn standard_gossip_that_fails_validation_is_rejected() { + todo!("Complete me") } #[test] @@ -3070,7 +3360,7 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(GossipAcceptanceResult::Ignored, result); + assert_eq!(result, vec![]); } #[test] @@ -3108,11 +3398,11 @@ mod tests { ); let after = time_t_timestamp(); - let expected_result = GossipAcceptanceResult::Reply( + let expected_result = vec![GossipAcceptanceResult::Reply( expected_gossip_response, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - ); + )]; assert_eq!(result, expected_result); root_node .add_half_neighbor_key(debut_node.public_key().clone()) @@ -3161,12 +3451,12 @@ mod tests { .node(existing_node_key, true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + result, + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - ), - result + )] ); root_node .add_half_neighbor_key(debut_node.public_key().clone()) @@ -3238,18 +3528,18 @@ mod tests { let debut_node_addr = debut_node.node_addr_opt().as_ref().unwrap().clone(); assert_contains( &[ - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip_1, debut_key.clone(), debut_node_addr.clone(), - ), - GossipAcceptanceResult::Reply( + )], + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip_2, debut_key.clone(), debut_node_addr.clone(), - ), + )], ], - &result, + &result ); root_node .add_half_neighbor_key(debut_node.public_key().clone()) @@ -3328,16 +3618,16 @@ mod tests { .build(); assert_contains( &[ - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip_2, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - ), - GossipAcceptanceResult::Reply( + )], + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip_3, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - ), + )], ], &result, ); @@ -3414,12 +3704,12 @@ mod tests { .node(existing_node_5_key, true) .build(); assert_eq!( - GossipAcceptanceResult::Reply( + result, + vec![GossipAcceptanceResult::Reply( expected_acceptance_gossip, debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - ), - result + )], ); root_node .add_half_neighbor_key(existing_node_1_key.clone()) @@ -3466,13 +3756,13 @@ mod tests { make_default_neighborhood_metadata(), ); - assert_eq!(GossipAcceptanceResult::Ignored, result); + assert_eq!(result, vec![]); assert_eq!( - false, dest_db .node_by_key(src_node.public_key()) .unwrap() - .has_half_neighbor(dest_node.public_key()) + .has_half_neighbor(dest_node.public_key()), + false, ); } @@ -3506,7 +3796,7 @@ mod tests { ); let end_at = time_t_timestamp(); - assert_eq!(GossipAcceptanceResult::Accepted, result); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); let node = dest_db.node_by_key(src_node.public_key()).unwrap(); assert_eq!(node.has_half_neighbor(dest_node.public_key()), true); assert_eq!( @@ -3551,7 +3841,7 @@ mod tests { let debut_gossip = Gossip_0v1 { node_records: vec![gnr], }; - let expected = make_expected_non_introduction_debut_response(&src_node, debut_gossip); + let expected = vec![make_expected_non_introduction_debut_response(&src_node, debut_gossip)]; assert_eq!(result, expected); assert_eq!( dest_db @@ -3600,7 +3890,7 @@ mod tests { let debut_gossip = Gossip_0v1 { node_records: vec![gnr], }; - let expected = make_expected_non_introduction_debut_response(&src_node, debut_gossip); + let expected = vec![make_expected_non_introduction_debut_response(&src_node, debut_gossip)]; assert_eq!(result, expected); assert_eq!( dest_db @@ -3627,7 +3917,10 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); - let subject = IntroductionHandler::new(Logger::new("test")); + let subject = IntroductionHandler::new( + &RatePackLimits::test_default(), + Logger::new("test") + ); let (gossip, gossip_source) = make_introduction(0, 1); let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); @@ -3686,11 +3979,11 @@ mod tests { .build(); assert_eq!( result, - GossipAcceptanceResult::Reply( + vec![GossipAcceptanceResult::Reply( expected_relay_gossip, pass_target.public_key().clone(), pass_target.node_addr_opt().unwrap(), - ) + )] ); assert_eq!(db.keys().len(), 1); } @@ -3717,8 +4010,8 @@ mod tests { ); let final_timestamp = SystemTime::now(); - match result { - GossipAcceptanceResult::Reply(_, _, _) => (), + match &result[0] { + &GossipAcceptanceResult::Reply(_, _, _) => (), other => panic!( "Expected GossipAcceptanceResult::Reply but received {:?}", other @@ -3773,7 +4066,7 @@ mod tests { let final_timestamp = SystemTime::now(); System::current().stop(); assert_eq!(system.run(), 0); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); let recording = recording_arc.lock().unwrap(); let received_message: &ConnectionProgressMessage = recording.get_record(0); assert_eq!( @@ -3814,7 +4107,7 @@ mod tests { System::current().stop(); assert_eq!(system.run(), 0); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); let recording = recording_arc.lock().unwrap(); let received_message: &ConnectionProgressMessage = recording.get_record(0); assert_eq!( @@ -3854,8 +4147,8 @@ mod tests { ); let final_timestamp = SystemTime::now(); - match result { - GossipAcceptanceResult::Reply(_, _, _) => (), + match &result[0] { + &GossipAcceptanceResult::Reply(_, _, _) => (), other => panic!( "Expected GossipAcceptanceResult::Reply but received {:?}", other @@ -3960,7 +4253,7 @@ mod tests { ); let after = time_t_timestamp(); - assert_eq!(GossipAcceptanceResult::Accepted, result); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); let mut expected_dest_db = src_db.clone(); expected_dest_db.remove_arbitrary_half_neighbor(node_e.public_key(), node_a.public_key()); expected_dest_db.remove_arbitrary_half_neighbor(node_f.public_key(), node_a.public_key()); @@ -4121,7 +4414,7 @@ mod tests { .unwrap(); dest_node_mut.increment_version(); dest_node_mut.resign(); - assert_eq!(result, GossipAcceptanceResult::Accepted); + assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); fix_nodes_last_updates(&mut expected_dest_db, &dest_db); assert_node_records_eq( dest_db.node_by_key(third_node.public_key()).unwrap(), @@ -4193,7 +4486,7 @@ mod tests { ); let after = time_t_timestamp(); - assert_eq!(result, GossipAcceptanceResult::Ignored); + assert_eq!(result, vec![]); assert_node_records_eq( dest_db.node_by_key(dest_root.public_key()).unwrap(), original_dest_db @@ -4301,7 +4594,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), other_neighbor_3_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_1_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_2_key); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let excluded = AccessibleGossipRecord::from((&db, excluded_key, true)); let result = subject.find_more_appropriate_neighbor(&db, &excluded); @@ -4325,7 +4618,7 @@ mod tests { db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_1_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_2_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_3_key); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let excluded = AccessibleGossipRecord::from((&db, excluded_key, true)); let result = subject.find_more_appropriate_neighbor(&db, &excluded); @@ -4352,7 +4645,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), other_neighbor_4_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_1_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_2_key); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let excluded = AccessibleGossipRecord::from((&db, excluded_key, true)); let result = subject.find_more_appropriate_neighbor(&db, &excluded); @@ -4374,7 +4667,7 @@ mod tests { db.add_arbitrary_full_neighbor(root_node.public_key(), other_neighbor_3_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_1_key); db.add_arbitrary_full_neighbor(less_connected_neighbor_key, other_neighbor_2_key); - let subject = DebutHandler::new(Logger::new("test")); + let subject = DebutHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let less_connected_neighbor_agr = AccessibleGossipRecord::from((&db, less_connected_neighbor_key, true)); @@ -4547,7 +4840,11 @@ mod tests { } fn make_subject(crypt_de: &dyn CryptDE) -> GossipAcceptorReal { - GossipAcceptorReal::new(crypt_de.dup()) + GossipAcceptorReal::new( + crypt_de.dup(), + &PersistentConfigurationMock::new() + .rate_pack_limits_result(Ok(RatePackLimits::test_default())) + ) } fn assert_node_records_eq(actual: &NodeRecord, expected: &NodeRecord, before: u32, after: u32) { diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 9c685c0aff..da4dcecf54 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -12,11 +12,9 @@ pub mod overall_connection_status; use crate::bootstrapper::{BootstrapperConfig, CryptDEPair}; use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; -use crate::db_config::persistent_configuration::{ - PersistentConfigError, PersistentConfiguration, PersistentConfigurationReal, -}; +use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration, PersistentConfigurationFactory, PersistentConfigurationFactoryReal, PersistentConfigurationInvalid}; use crate::neighborhood::gossip::{AccessibleGossipRecord, DotGossipEndpoint, Gossip_0v1}; -use crate::neighborhood::gossip_acceptor::GossipAcceptanceResult; +use crate::neighborhood::gossip_acceptor::{GossipAcceptanceResult, GossipAcceptorInvalid}; use crate::neighborhood::node_location::get_node_location; use crate::neighborhood::overall_connection_status::{ OverallConnectionStage, OverallConnectionStatus, @@ -43,9 +41,7 @@ use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; -use crate::sub_lib::utils::{ - db_connection_launch_panic, handle_ui_crash_request, NODE_MAILBOX_CAPACITY, -}; +use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::versioned_data::VersionedData; use crate::sub_lib::wallet::Wallet; use actix::Context; @@ -110,7 +106,8 @@ pub struct Neighborhood { chain: Chain, crashable: bool, data_directory: PathBuf, - persistent_config_opt: Option>, + persistent_config_factory: Box, + persistent_config: Box, db_password_opt: Option, logger: Logger, tools: NeighborhoodTools, @@ -423,7 +420,7 @@ impl Neighborhood { hopper_no_lookup_opt: None, connected_signal_opt: None, node_to_ui_recipient_opt: None, - gossip_acceptor: Box::new(GossipAcceptorReal::new(cryptde_pair.main.dup())), + gossip_acceptor: Box::new(GossipAcceptorInvalid::new()), gossip_producer: Box::new(GossipProducerReal::new()), neighborhood_database, consuming_wallet_opt: config.consuming_wallet_opt.clone(), @@ -435,7 +432,8 @@ impl Neighborhood { chain: config.blockchain_bridge_config.chain, crashable: config.crash_point == CrashPoint::Message, data_directory: config.data_directory.clone(), - persistent_config_opt: None, + persistent_config_factory: Box::new(PersistentConfigurationFactoryReal::new(config.data_directory.clone())), + persistent_config: Box::new(PersistentConfigurationInvalid::new()), db_password_opt: config.db_password_opt.clone(), logger: Logger::new("Neighborhood"), tools: NeighborhoodTools::default(), @@ -467,7 +465,11 @@ impl Neighborhood { fn handle_start_message(&mut self) { debug!(self.logger, "Connecting to persistent database"); - self.connect_database(); + self.persistent_config = self.persistent_config_factory.make(); + self.gossip_acceptor = Box::new(GossipAcceptorReal::new( + self.cryptde.dup(), + self.persistent_config.as_ref(), + )); self.validate_or_replace_min_hops_value(); self.send_debut_gossip_to_all_initial_descriptors(); } @@ -519,19 +521,6 @@ impl Neighborhood { } } - fn connect_database(&mut self) { - if self.persistent_config_opt.is_none() { - let db_initializer = DbInitializerReal::default(); - let conn = db_initializer - .initialize( - &self.data_directory, - DbInitializationConfig::panic_on_migration(), - ) - .unwrap_or_else(|err| db_connection_launch_panic(err, &self.data_directory)); - self.persistent_config_opt = Some(Box::new(PersistentConfigurationReal::from(conn))); - } - } - fn handle_config_change_msg(&mut self, msg: ConfigChangeMsg) { match msg.change { ConfigChange::UpdateWallets(wallet_pair) => { @@ -578,22 +567,20 @@ impl Neighborhood { } fn validate_or_replace_min_hops_value(&mut self) { - if let Some(persistent_config) = self.persistent_config_opt.as_ref() { - let value_in_db = persistent_config - .min_hops() - .expect("Min Hops value is not initialized inside Database"); - let value_in_neighborhood = self.min_hops; - if value_in_neighborhood != value_in_db { - info!( - self.logger, - "Database with different min hops value detected; \ - currently set: {:?}, found in db: {:?}; changing to {:?}", - value_in_neighborhood, - value_in_db, - value_in_db - ); - self.min_hops = value_in_db; - } + let value_in_db = self.persistent_config + .min_hops() + .expect("Min Hops value is not initialized inside Database"); + let value_in_neighborhood = self.min_hops; + if value_in_neighborhood != value_in_db { + info!( + self.logger, + "Database with different min hops value detected; \ + currently set: {:?}, found in db: {:?}; changing to {:?}", + value_in_neighborhood, + value_in_db, + value_in_db + ); + self.min_hops = value_in_db; } } @@ -781,37 +768,41 @@ impl Neighborhood { db_patch_size: self.db_patch_size, user_exit_preferences_opt: Some(self.user_exit_preferences.clone()), }; - let acceptance_result = self.gossip_acceptor.handle( + let mut acceptance_results = self.gossip_acceptor.handle( &mut self.neighborhood_database, agrs, gossip_source, neighborhood_metadata, ); - match acceptance_result { - 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) => { - //TODO also ensure init_db_countries on hop change - if self.min_hops == Hops::OneHop { - self.user_exit_preferences.db_countries = self.init_db_countries(); + if acceptance_results.is_empty() { + trace!(self.logger, "Gossip from {} ignored", gossip_source); + self.handle_gossip_ignored(&ignored_node_name, gossip_record_count) + } + else { + acceptance_results.into_iter().for_each(|acceptance_result| { + match acceptance_result { + 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) => { + //TODO also ensure init_db_countries on hop change + if self.min_hops == Hops::OneHop { + 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) => { + self.handle_gossip_failed(failure, &target_key, &target_node_addr) + } + GossipAcceptanceResult::Ban(reason) => { + // TODO in case we introduce Ban machinery we want to reinitialize the db_countries here as well + // That implies new process in init_db_countries to exclude banned node from the result + warning!(self.logger, "Malefactor detected at {}, but malefactor bans not yet implemented; ignoring: {}", gossip_source, reason); + self.handle_gossip_ignored(&ignored_node_name, gossip_record_count); + } } - self.handle_gossip_reply(next_debut, &target_key, &target_node_addr) - } - GossipAcceptanceResult::Failed(failure, target_key, target_node_addr) => { - self.handle_gossip_failed(failure, &target_key, &target_node_addr) - } - GossipAcceptanceResult::Ignored => { - trace!(self.logger, "Gossip from {} ignored", gossip_source); - self.handle_gossip_ignored(ignored_node_name, gossip_record_count) - } - GossipAcceptanceResult::Ban(reason) => { - // TODO in case we introduce Ban machinery we want to reinitialize the db_countries here as well - // That implies new process in init_db_countries to exclude banned node from the result - warning!(self.logger, "Malefactor detected at {}, but malefactor bans not yet implemented; ignoring: {}", gossip_source, reason); - self.handle_gossip_ignored(ignored_node_name, gossip_record_count); - } + }); } } @@ -838,11 +829,7 @@ impl Neighborhood { self.logger, "Saving neighbor list: {:?}", node_descriptors_opt ); - match self - .persistent_config_opt - .as_mut() - .expect("PersistentConfig was not set by StartMessage") - .set_past_neighbors(node_descriptors_opt, db_password) + match self.persistent_config.set_past_neighbors(node_descriptors_opt, db_password) { Ok(_) => info!(self.logger, "Persisted neighbor changes for next run"), Err(PersistentConfigError::DatabaseError(msg)) @@ -1950,7 +1937,7 @@ impl Neighborhood { trace!(self.logger, "Sent GossipFailure_0v1: {}", gossip_failure); } - fn handle_gossip_ignored(&self, _ignored_node_name: String, _gossip_record_count: usize) { + fn handle_gossip_ignored(&self, _ignored_node_name: &str, _gossip_record_count: usize) { // Maybe something here eventually for keeping statistics } @@ -2223,10 +2210,7 @@ mod tests { use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::neighborhood::{ - AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, - NeighborhoodMode, WalletPair, - }; + use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, NeighborhoodMode, RatePackLimits, WalletPair}; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; use crate::sub_lib::peer_actors::PeerActors; @@ -2266,9 +2250,10 @@ mod tests { }; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use crate::test_utils::database_utils::PersistentConfigurationFactoryTest; lazy_static! { - static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); + static ref N_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } impl Neighborhood { @@ -2323,7 +2308,7 @@ mod tests { let neighborhood_config = NeighborhoodConfig { mode, min_hops }; let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( neighborhood_config, make_wallet("earning"), @@ -2423,12 +2408,18 @@ mod tests { #[test] fn introduction_results_in_full_neighborship_in_debutant_db_and_enrich_db_countries_on_one_hop() { - let debut_node = make_global_cryptde_node_record(1111, true, &CRYPTDE_PAIR); - let mut debut_subject = neighborhood_from_nodes(&debut_node, None, &CRYPTDE_PAIR); + let debut_node = make_global_cryptde_node_record(1111, true, &N_CRYPTDE_PAIR); + let mut debut_subject = neighborhood_from_nodes(&debut_node, None, &N_CRYPTDE_PAIR); debut_subject.min_hops = Hops::OneHop; let persistent_config = - PersistentConfigurationMock::new().set_past_neighbors_result(Ok(())); - debut_subject.persistent_config_opt = Some(Box::new(persistent_config)); + PersistentConfigurationMock::new() + .set_past_neighbors_result(Ok(())) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())); + debut_subject.persistent_config = Box::new(persistent_config); + debut_subject.gossip_acceptor = Box::new(GossipAcceptorReal::new( + N_CRYPTDE_PAIR.main.dup(), + debut_subject.persistent_config.as_ref(), + )); let debut_root_key = debut_subject.neighborhood_database.root_key().clone(); let introducer_node = make_node_record_cc(3333, true, "AU"); //AU let introducee = make_node_record_cc(2222, true, "FR"); //FR @@ -2465,7 +2456,7 @@ mod tests { expected = "Neighbor masq://eth-ropsten:AQIDBA@1.2.3.4:1234 is not on the mainnet blockchain" )] fn cant_create_mainnet_neighborhood_with_non_mainnet_neighbors() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let mut bc = bc_from_nc_plus( NeighborhoodConfig { @@ -2482,7 +2473,7 @@ mod tests { ); bc.blockchain_bridge_config.chain = DEFAULT_CHAIN; - let _ = Neighborhood::new(CRYPTDE_PAIR.clone(), &bc); + let _ = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &bc); } #[test] @@ -2490,7 +2481,7 @@ mod tests { expected = "Neighbor masq://eth-mainnet:AQIDBA@1.2.3.4:1234 is on the mainnet blockchain" )] fn cant_create_non_mainnet_neighborhood_with_mainnet_neighbors() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let mut bc = bc_from_nc_plus( NeighborhoodConfig { @@ -2507,16 +2498,16 @@ mod tests { ); bc.blockchain_bridge_config.chain = TEST_DEFAULT_CHAIN; - let _ = Neighborhood::new(CRYPTDE_PAIR.clone(), &bc); + let _ = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &bc); } #[test] fn node_with_zero_hop_config_creates_single_node_database() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, @@ -2537,12 +2528,12 @@ mod tests { #[test] fn node_with_originate_only_config_is_decentralized_with_neighbor_but_not_ip() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let neighbor: NodeRecord = make_node_record(1234, true); let earning_wallet = make_wallet("earning"); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::OriginateOnly( @@ -2583,7 +2574,7 @@ mod tests { let system = System::new("node_with_no_neighbor_configs_ignores_bootstrap_neighborhood_now_message"); let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, @@ -2594,9 +2585,13 @@ mod tests { "node_with_zero_hop_config_ignores_start_message", ), ); - subject.persistent_config_opt = Some(Box::new( - PersistentConfigurationMock::new().min_hops_result(Ok(MIN_HOPS_FOR_TEST)), - )); + subject.persistent_config_factory = Box::new( + PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())) + ) + ); subject.data_directory = data_dir; let addr = subject.start(); let sub = addr.clone().recipient::(); @@ -2616,7 +2611,7 @@ mod tests { #[test] fn neighborhood_adds_nodes_and_links() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let consuming_wallet = Some(make_paying_wallet(b"consuming")); let one_neighbor_node = make_node_record(3456, true); @@ -2624,7 +2619,7 @@ mod tests { let this_node_addr = NodeAddr::new(&IpAddr::from_str("5.4.3.2").unwrap(), &[5678]); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -2723,7 +2718,7 @@ mod tests { }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); - let mut subject = Neighborhood::new(CRYPTDE_PAIR.clone(), &bootstrap_config); + let mut subject = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &bootstrap_config); subject .overall_connection_status .get_connection_progress_by_ip(peer_1) @@ -3191,7 +3186,7 @@ mod tests { min_hops: MIN_HOPS_FOR_TEST, }; let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( neighborhood_config, make_wallet("earning"), @@ -3243,14 +3238,14 @@ mod tests { #[test] fn gossip_failures_eventually_stop_the_neighborhood() { init_test_logging(); - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let one_neighbor_node: NodeRecord = make_node_record(3456, true); let another_neighbor_node: NodeRecord = make_node_record(4567, true); let this_node_addr = NodeAddr::new(&IpAddr::from_str("5.4.3.2").unwrap(), &[5678]); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -3275,14 +3270,14 @@ mod tests { let ecp1 = ExpiredCoresPackage::new( one_neighbor_node.node_addr_opt().unwrap().into(), None, - make_meaningless_route(&CRYPTDE_PAIR), + make_meaningless_route(&N_CRYPTDE_PAIR), GossipFailure_0v1::NoNeighbors, 0, ); let ecp2 = ExpiredCoresPackage::new( another_neighbor_node.node_addr_opt().unwrap().into(), None, - make_meaningless_route(&CRYPTDE_PAIR), + make_meaningless_route(&N_CRYPTDE_PAIR), GossipFailure_0v1::ManualRejection, 0, ); @@ -3335,7 +3330,7 @@ mod tests { #[test] fn route_query_works_when_node_is_set_for_one_hop_and_no_consuming_wallet() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let system = System::new("route_query_works_when_node_is_set_for_one_hop_and_no_consuming_wallet"); @@ -3437,7 +3432,7 @@ mod tests { #[test] fn route_query_responds_with_standard_zero_hop_route_when_requested() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let system = System::new("responds_with_standard_zero_hop_route_when_requested"); let mut subject = make_standard_subject(); subject.mode = NeighborhoodModeLight::ZeroHop; @@ -3508,7 +3503,7 @@ mod tests { #[test] fn route_query_messages() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let system = System::new("route_query_messages"); let mut subject = make_standard_subject(); @@ -3629,7 +3624,7 @@ mod tests { #[test] fn return_route_ids_increase() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let system = System::new("return_route_ids_increase"); let (_, _, _, mut subject) = make_o_r_e_subject(); subject.min_hops = Hops::TwoHops; @@ -5162,7 +5157,7 @@ mod tests { #[test] fn gossips_after_removing_a_neighbor() { let (hopper, hopper_awaiter, hopper_recording) = make_recorder(); - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let consuming_wallet = Some(make_paying_wallet(b"consuming")); let this_node = NodeRecord::new_for_tests( @@ -5185,7 +5180,7 @@ mod tests { thread::spawn(move || { let system = System::new("gossips_after_removing_a_neighbor"); let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -5301,10 +5296,10 @@ mod tests { let handle_params_arc = Arc::new(Mutex::new(vec![])); let gossip_acceptor = GossipAcceptorMock::new() .handle_params(&handle_params_arc) - .handle_result(GossipAcceptanceResult::Ignored); - let mut subject_node = make_global_cryptde_node_record(1234, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + .handle_result(vec![]); + let mut subject_node = make_global_cryptde_node_record(1234, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); subject.gossip_acceptor = Box::new(gossip_acceptor); let gossip = GossipBuilder::new(&subject.neighborhood_database) .node(subject_node.public_key(), true) @@ -5312,7 +5307,7 @@ mod tests { let cores_package = ExpiredCoresPackage { immediate_neighbor: subject_node.node_addr_opt().unwrap().into(), paying_wallet: None, - remaining_route: make_meaningless_route(&CRYPTDE_PAIR), + remaining_route: make_meaningless_route(&N_CRYPTDE_PAIR), payload: gossip.clone(), payload_len: 0, }; @@ -5345,9 +5340,9 @@ mod tests { #[test] fn neighborhood_sends_only_an_acceptance_debut_when_an_acceptance_debut_is_provided() { let introduction_target_node = make_node_record(7345, true); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1050, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(introduction_target_node.clone()) @@ -5361,11 +5356,11 @@ mod tests { .node(subject_node.public_key(), true) .build(); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Reply( + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Reply( debut.clone(), introduction_target_node.public_key().clone(), introduction_target_node.node_addr_opt().unwrap(), - )); + )]); subject.gossip_acceptor = Box::new(gossip_acceptor); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -5396,18 +5391,18 @@ mod tests { #[test] fn neighborhood_transmits_gossip_failure_properly() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let public_key = PublicKey::new(&[1, 2, 3, 4]); let node_addr = NodeAddr::from_str("1.2.3.4:1234").unwrap(); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Failed( + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Failed( GossipFailure_0v1::NoSuitableNeighbors, public_key.clone(), node_addr.clone(), - )); + )]); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let (hopper, _, hopper_recording_arc) = make_recorder(); let system = System::new("neighborhood_transmits_gossip_failure_properly"); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -5451,7 +5446,7 @@ mod tests { _agrs: Vec, _gossip_source: SocketAddr, _neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let non_root_database_keys = database .keys() .into_iter() @@ -5485,7 +5480,7 @@ mod tests { database.add_arbitrary_half_neighbor(k, n); }); }); - GossipAcceptanceResult::Ignored + vec![] } } @@ -5497,10 +5492,10 @@ mod tests { #[test] fn neighborhood_does_not_start_accountant_if_no_route_can_be_made() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let mut replacement_database = subject.neighborhood_database.clone(); replacement_database.add_node(neighbor.clone()).unwrap(); replacement_database @@ -5528,10 +5523,10 @@ mod tests { #[test] fn neighborhood_does_not_start_accountant_if_already_connected() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let replacement_database = subject.neighborhood_database.clone(); subject.gossip_acceptor = Box::new(DatabaseReplacementGossipAcceptor { replacement_database, @@ -5582,12 +5577,12 @@ mod tests { let handle_params_arc = Arc::new(Mutex::new(vec![])); let gossip_acceptor = GossipAcceptorMock::new() .handle_params(&handle_params_arc) - .handle_result(GossipAcceptanceResult::Ignored); + .handle_result(vec![]); let (node_to_ui_recipient, _) = make_node_to_ui_recipient(); let peer_1 = make_node_record(1234, true); let peer_2 = make_node_record(6721, true); - let desc_1 = peer_1.node_descriptor(Chain::Dev, CRYPTDE_PAIR.main.as_ref()); - let desc_2 = peer_2.node_descriptor(Chain::Dev, CRYPTDE_PAIR.main.as_ref()); + let desc_1 = peer_1.node_descriptor(Chain::Dev, N_CRYPTDE_PAIR.main.as_ref()); + let desc_2 = peer_2.node_descriptor(Chain::Dev, N_CRYPTDE_PAIR.main.as_ref()); let this_node = make_node_record(7777, true); let initial_node_descriptors = vec![desc_1, desc_2]; let neighborhood_config = NeighborhoodConfig { @@ -5600,7 +5595,7 @@ mod tests { }; let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, "test"); - let mut subject = Neighborhood::new(CRYPTDE_PAIR.clone(), &bootstrap_config); + let mut subject = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &bootstrap_config); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); subject.gossip_acceptor = Box::new(gossip_acceptor); subject.db_patch_size = 6; @@ -5725,7 +5720,7 @@ mod tests { _agrs: Vec, _gossip_source: SocketAddr, _neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { let half_neighbor_keys = database .root() .half_neighbor_keys() @@ -5740,18 +5735,18 @@ mod tests { database.add_node(nr.clone()).unwrap(); database.add_arbitrary_full_neighbor(&root_key, nr.public_key()); }); - GossipAcceptanceResult::Ignored + vec![] } } #[test] fn neighborhood_updates_past_neighbors_when_neighbor_list_changes() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let old_neighbor = make_node_record(1111, true); let new_neighbor = make_node_record(2222, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(old_neighbor.clone()) @@ -5767,7 +5762,7 @@ mod tests { .set_past_neighbors_params(&set_past_neighbors_params_arc) .set_past_neighbors_result(Ok(())); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.handle_gossip_agrs( vec![], @@ -5792,10 +5787,10 @@ mod tests { #[test] fn neighborhood_removes_past_neighbors_when_neighbor_list_goes_empty() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(neighbor.clone()) @@ -5811,7 +5806,7 @@ mod tests { .set_past_neighbors_params(&set_past_neighbors_params_arc) .set_past_neighbors_result(Ok(())); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.handle_gossip_agrs( vec![], @@ -5827,10 +5822,10 @@ mod tests { #[test] fn neighborhood_does_not_update_past_neighbors_when_neighbor_list_does_not_change() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let steadfast_neighbor = make_node_record(1111, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&steadfast_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&steadfast_neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(steadfast_neighbor.clone()) @@ -5846,7 +5841,7 @@ mod tests { let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.handle_gossip_agrs( vec![], @@ -5861,11 +5856,11 @@ mod tests { #[test] fn neighborhood_does_not_update_past_neighbors_without_password_even_when_neighbor_list_changes( ) { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let old_neighbor = make_node_record(1111, true); let new_neighbor = make_node_record(2222, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(old_neighbor.clone()) @@ -5880,7 +5875,7 @@ mod tests { let persistent_config = PersistentConfigurationMock::new() .set_past_neighbors_params(&set_past_neighbors_params_arc); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.db_password_opt = None; subject.handle_gossip_agrs( @@ -5896,11 +5891,11 @@ mod tests { #[test] fn neighborhood_warns_when_past_neighbors_update_fails_because_of_database_lock() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let old_neighbor = make_node_record(1111, true); let new_neighbor = make_node_record(2222, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(old_neighbor.clone()) @@ -5915,7 +5910,7 @@ mod tests { PersistentConfigError::DatabaseError("database is locked".to_string()), )); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.handle_gossip_agrs( vec![], @@ -5929,11 +5924,11 @@ mod tests { #[test] fn neighborhood_logs_error_when_past_neighbors_update_fails_for_another_reason() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let old_neighbor = make_node_record(1111, true); let new_neighbor = make_node_record(2222, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&old_neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(old_neighbor.clone()) @@ -5948,7 +5943,7 @@ mod tests { PersistentConfigError::DatabaseError("Booga".to_string()), )); subject.gossip_acceptor = Box::new(gossip_acceptor); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); subject.handle_gossip_agrs( vec![], @@ -5962,10 +5957,10 @@ mod tests { #[test] fn handle_new_public_ip_changes_public_ip_and_country_code_nothing_else() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(1234, true, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1234, true, &N_CRYPTDE_PAIR); let neighbor = make_node_record(1050, true); let mut subject: Neighborhood = - neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); subject .neighborhood_database .root_mut() @@ -6013,9 +6008,9 @@ mod tests { #[test] fn neighborhood_sends_from_gossip_producer_when_acceptance_introductions_are_not_provided() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1050, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let full_neighbor = make_node_record(1234, true); let half_neighbor = make_node_record(2345, true); subject @@ -6033,7 +6028,7 @@ mod tests { .neighborhood_database .add_arbitrary_half_neighbor(subject_node.public_key(), half_neighbor.public_key()); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Accepted); + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Accepted]); subject.gossip_acceptor = Box::new(gossip_acceptor); let gossip = Gossip_0v1::new(vec![]); let produce_params_arc = Arc::new(Mutex::new(vec![])); @@ -6065,7 +6060,7 @@ mod tests { ( package .route - .next_hop(CRYPTDE_PAIR.main.as_ref()) + .next_hop(N_CRYPTDE_PAIR.main.as_ref()) .unwrap() .public_key, package.payload, @@ -6077,7 +6072,7 @@ mod tests { ( full_neighbor.public_key().clone(), encodex( - CRYPTDE_PAIR.main.as_ref(), + N_CRYPTDE_PAIR.main.as_ref(), full_neighbor.public_key(), &MessageType::Gossip(gossip.clone().into()), ) @@ -6086,7 +6081,7 @@ mod tests { ( half_neighbor.public_key().clone(), encodex( - CRYPTDE_PAIR.main.as_ref(), + N_CRYPTDE_PAIR.main.as_ref(), half_neighbor.public_key(), &MessageType::Gossip(gossip.into()), ) @@ -6110,19 +6105,19 @@ mod tests { ) .as_str(), ); - let key_as_str = format!("{}", CRYPTDE_PAIR.main.as_ref().public_key()); + let key_as_str = format!("{}", N_CRYPTDE_PAIR.main.as_ref().public_key()); tlh.exists_log_containing(&format!("Sent Gossip: digraph db {{ \"src\" [label=\"Gossip From:\\n{}\\n5.5.5.5\"]; \"dest\" [label=\"Gossip To:\\nAQIDBA\\n1.2.3.4\"]; \"src\" -> \"dest\" [arrowhead=empty]; }}", &key_as_str[..8])); tlh.exists_log_containing(&format!("Sent Gossip: digraph db {{ \"src\" [label=\"Gossip From:\\n{}\\n5.5.5.5\"]; \"dest\" [label=\"Gossip To:\\nAgMEBQ\\n2.3.4.5\"]; \"src\" -> \"dest\" [arrowhead=empty]; }}", &key_as_str[..8])); } #[test] fn neighborhood_sends_no_gossip_when_target_does_not_exist() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A // This is ungossippable not because of any attribute of its own, but because the // GossipProducerMock is set to return None when ordered to target it. let ungossippable = make_node_record(1050, true); let mut subject = - neighborhood_from_nodes(&subject_node, Some(&ungossippable), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&ungossippable), &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(ungossippable.clone()) @@ -6131,7 +6126,7 @@ mod tests { .neighborhood_database .add_arbitrary_full_neighbor(subject_node.public_key(), ungossippable.public_key()); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Accepted); + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Accepted]); subject.gossip_acceptor = Box::new(gossip_acceptor); let produce_params_arc = Arc::new(Mutex::new(vec![])); let gossip_producer = GossipProducerMock::new() @@ -6159,22 +6154,22 @@ mod tests { #[test] fn neighborhood_sends_only_relay_gossip_when_gossip_acceptor_relays() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let mut subject = neighborhood_from_nodes( &subject_node, Some(&make_node_record(1111, true)), - &CRYPTDE_PAIR, + &N_CRYPTDE_PAIR, ); let debut_node = make_node_record(1234, true); let debut_gossip = GossipBuilder::new(&subject.neighborhood_database) .node(subject_node.public_key(), true) .build(); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Reply( + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Reply( debut_gossip.clone(), debut_node.public_key().clone(), debut_node.node_addr_opt().unwrap(), - )); + )]); subject.gossip_acceptor = Box::new(gossip_acceptor); let (hopper, _, hopper_recording_arc) = make_recorder(); let peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -6213,11 +6208,11 @@ mod tests { #[test] fn neighborhood_sends_no_gossip_when_gossip_acceptor_ignores() { - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let gossip_acceptor = - GossipAcceptorMock::new().handle_result(GossipAcceptanceResult::Ignored); + GossipAcceptorMock::new().handle_result(vec![]); subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); @@ -6240,11 +6235,11 @@ mod tests { #[test] fn neighborhood_complains_about_inability_to_ban_when_gossip_acceptor_requests_it() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); - let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); let gossip_acceptor = GossipAcceptorMock::new() - .handle_result(GossipAcceptanceResult::Ban("Bad guy".to_string())); + .handle_result(vec![GossipAcceptanceResult::Ban("Bad guy".to_string())]); subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); @@ -6315,7 +6310,7 @@ mod tests { #[test] fn neighborhood_logs_received_gossip_in_dot_graph_format() { init_test_logging(); - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let this_node = NodeRecord::new_for_tests( &cryptde.public_key(), Some(&NodeAddr::new( @@ -6349,7 +6344,7 @@ mod tests { let cores_package = ExpiredCoresPackage { immediate_neighbor: SocketAddr::from_str("1.2.3.4:1234").unwrap(), paying_wallet: Some(make_paying_wallet(b"consuming")), - remaining_route: make_meaningless_route(&CRYPTDE_PAIR), + remaining_route: make_meaningless_route(&N_CRYPTDE_PAIR), payload: gossip, payload_len: 0, }; @@ -6358,7 +6353,7 @@ mod tests { thread::spawn(move || { let system = System::new(""); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6413,15 +6408,15 @@ mod tests { .initialize(&data_dir, DbInitializationConfig::test_default()) .unwrap(); } - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let debut_target = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), // Used to provide default cryptde + N_CRYPTDE_PAIR.main.as_ref(), // Used to provide default cryptde "masq://eth-ropsten:AQIDBA@1.2.3.4:1234", )) .unwrap(); let (hopper, _, hopper_recording) = make_recorder(); let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6436,9 +6431,13 @@ mod tests { "node_gossips_to_neighbors_on_startup", ), ); - subject.persistent_config_opt = Some(Box::new( - PersistentConfigurationMock::new().min_hops_result(Ok(MIN_HOPS_FOR_TEST)), - )); + subject.persistent_config_factory = Box::new( + PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + ) + ); subject.data_directory = data_dir; subject.logger = Logger::new("node_gossips_to_neighbors_on_startup"); let this_node = subject.neighborhood_database.root().clone(); @@ -6478,7 +6477,7 @@ mod tests { let min_hops_in_neighborhood = Hops::SixHops; let min_hops_in_persistent_configuration = min_hops_in_neighborhood; let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6493,10 +6492,13 @@ mod tests { test_name, ), ); - subject.persistent_config_opt = Some(Box::new( - PersistentConfigurationMock::new() - .min_hops_result(Ok(min_hops_in_persistent_configuration)), - )); + subject.persistent_config_factory = Box::new( + PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(min_hops_in_persistent_configuration)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())) + ) + ); let system = System::new(test_name); let addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); @@ -6521,7 +6523,7 @@ mod tests { let min_hops_in_neighborhood = Hops::SixHops; let min_hops_in_db = Hops::TwoHops; let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6537,9 +6539,13 @@ mod tests { ), ); subject.logger = Logger::new(test_name); - subject.persistent_config_opt = Some(Box::new( - PersistentConfigurationMock::new().min_hops_result(Ok(min_hops_in_db)), - )); + subject.persistent_config_factory = Box::new( + PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(min_hops_in_db)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + ) + ); let system = System::new(test_name); let addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); @@ -6647,13 +6653,13 @@ mod tests { } fn node_record_to_neighbor_config(node_record_ref: &NodeRecord) -> NodeDescriptor { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); NodeDescriptor::from((node_record_ref, Chain::EthRopsten, cryptde)) } #[test] fn neighborhood_sends_node_query_response_with_none_when_initially_configured_with_no_data() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let (recorder, awaiter, recording_arc) = make_recorder(); thread::spawn(move || { let system = System::new("responds_with_none_when_initially_configured_with_no_data"); @@ -6692,7 +6698,7 @@ mod tests { #[test] fn neighborhood_sends_node_query_response_with_none_when_key_query_matches_no_configured_data() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let consuming_wallet = Some(make_paying_wallet(b"consuming")); let (recorder, awaiter, recording_arc) = make_recorder(); @@ -6703,7 +6709,7 @@ mod tests { addr.recipient::(); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6754,7 +6760,7 @@ mod tests { #[test] fn neighborhood_sends_node_query_response_with_result_when_key_query_matches_configured_data() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let consuming_wallet = Some(make_paying_wallet(b"consuming")); let (recorder, awaiter, recording_arc) = make_recorder(); @@ -6773,7 +6779,7 @@ mod tests { let addr: Addr = recorder.start(); let recipient = addr.recipient::(); let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6822,7 +6828,7 @@ mod tests { #[test] fn neighborhood_sends_node_query_response_with_none_when_ip_address_query_matches_no_configured_data( ) { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let earning_wallet = make_wallet("earning"); let consuming_wallet = Some(make_paying_wallet(b"consuming")); let (recorder, awaiter, recording_arc) = make_recorder(); @@ -6832,7 +6838,7 @@ mod tests { let recipient: Recipient = addr.recipient::(); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::Standard( @@ -6884,7 +6890,7 @@ mod tests { #[test] fn neighborhood_sends_node_query_response_with_result_when_ip_address_query_matches_configured_data( ) { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = N_CRYPTDE_PAIR.main.as_ref(); let (recorder, awaiter, recording_arc) = make_recorder(); let node_record = make_node_record(1234, true); let another_node_record = make_node_record(2345, true); @@ -6918,7 +6924,7 @@ mod tests { None, "neighborhood_sends_node_query_response_with_result_when_ip_address_query_matches_configured_data", ); - let mut subject = Neighborhood::new(CRYPTDE_PAIR.clone(), &config); + let mut subject = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &config); subject .neighborhood_database .add_node(another_node_record_a) @@ -6957,9 +6963,9 @@ mod tests { let min_hops = Hops::TwoHops; let one_next_door_neighbor = make_node_record(3333, true); let another_next_door_neighbor = make_node_record(4444, true); - let subject_node = make_global_cryptde_node_record(5555, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let mut subject = - neighborhood_from_nodes(&subject_node, Some(&one_next_door_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&one_next_door_neighbor), &N_CRYPTDE_PAIR); subject.min_hops = min_hops; subject @@ -6996,7 +7002,7 @@ mod tests { Err(format!( "Couldn't find any routes: at least {}-hop from {} to ProxyClient at Unknown", min_hops as usize, - CRYPTDE_PAIR.main.as_ref().public_key() + N_CRYPTDE_PAIR.main.as_ref().public_key() )), result ); @@ -7007,9 +7013,9 @@ mod tests { let next_door_neighbor = make_node_record(3333, true); let exit_node = make_node_record(5, false); - let subject_node = make_global_cryptde_node_record(666, true, &CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A + let subject_node = make_global_cryptde_node_record(666, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let mut subject = - neighborhood_from_nodes(&subject_node, Some(&next_door_neighbor), &CRYPTDE_PAIR); + neighborhood_from_nodes(&subject_node, Some(&next_door_neighbor), &N_CRYPTDE_PAIR); subject.min_hops = Hops::TwoHops; subject @@ -7045,7 +7051,7 @@ mod tests { let hops = result.clone().unwrap().route.hops; let actual_keys: Vec = match hops.as_slice() { [hop, exit, hop_back, origin, empty, _accounting] => vec![ - decodex::(CRYPTDE_PAIR.main.as_ref(), hop) + decodex::(N_CRYPTDE_PAIR.main.as_ref(), hop) .expect("hop") .public_key, decodex::(&next_door_neighbor_cryptde, exit) @@ -7057,7 +7063,7 @@ mod tests { decodex::(&next_door_neighbor_cryptde, origin) .expect("origin") .public_key, - decodex::(CRYPTDE_PAIR.main.as_ref(), empty) + decodex::(N_CRYPTDE_PAIR.main.as_ref(), empty) .expect("empty") .public_key, ], @@ -7076,11 +7082,11 @@ mod tests { fn assert_route_query_message(min_hops: Hops) { let hops = min_hops as usize; let nodes_count = hops + 1; - let root_node = make_global_cryptde_node_record(4242, true, &CRYPTDE_PAIR); + let root_node = make_global_cryptde_node_record(4242, true, &N_CRYPTDE_PAIR); let mut nodes = make_node_records(nodes_count as u16); nodes[0] = root_node; let db = linearly_connect_nodes(&nodes); - let mut subject = neighborhood_from_nodes(db.root(), nodes.get(1), &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(db.root(), nodes.get(1), &N_CRYPTDE_PAIR); subject.min_hops = min_hops; subject.neighborhood_database = db; @@ -7211,7 +7217,7 @@ mod tests { #[test] fn node_record_metadata_message_is_handled_properly() { init_test_logging(); - let subject_node = make_global_cryptde_node_record(1345, true, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1345, true, &N_CRYPTDE_PAIR); let public_key = PublicKey::from(&b"exit_node"[..]); let node_record_inputs = NodeRecordInputs { earning_wallet: make_wallet("earning"), @@ -7222,9 +7228,9 @@ mod tests { location_opt: None, }; let node_record = - NodeRecord::new(&public_key, CRYPTDE_PAIR.main.as_ref(), node_record_inputs); + NodeRecord::new(&public_key, N_CRYPTDE_PAIR.main.as_ref(), node_record_inputs); let unreachable_host = String::from("facebook.com"); - let mut subject = neighborhood_from_nodes(&subject_node, None, &CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); let _ = subject.neighborhood_database.add_node(node_record); let addr = subject.start(); let system = System::new("test"); @@ -7259,8 +7265,8 @@ mod tests { expected = "Neighborhood should never get ShutdownStreamMsg about non-clandestine stream" )] fn handle_stream_shutdown_complains_about_non_clandestine_message() { - let subject_node = make_global_cryptde_node_record(1345, true, &CRYPTDE_PAIR); - let mut subject = neighborhood_from_nodes(&subject_node, None, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1345, true, &N_CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); subject.handle_stream_shutdown_msg(StreamShutdownMsg { peer_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), @@ -7283,8 +7289,8 @@ mod tests { unrecognized_node_addr.ip_addr(), unrecognized_node_addr.ports()[0], ); - let subject_node = make_global_cryptde_node_record(1345, true, &CRYPTDE_PAIR); - let mut subject = neighborhood_from_nodes(&subject_node, None, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1345, true, &N_CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); let peer_actors = peer_actors_builder().hopper(hopper).build(); subject.hopper_opt = Some(peer_actors.hopper.from_hopper_client); @@ -7315,8 +7321,8 @@ mod tests { inactive_neighbor_node_addr.ip_addr(), inactive_neighbor_node_addr.ports()[0], ); - let subject_node = make_global_cryptde_node_record(1345, true, &CRYPTDE_PAIR); - let mut subject = neighborhood_from_nodes(&subject_node, None, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1345, true, &N_CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(gossip_neighbor_node.clone()) @@ -7370,8 +7376,8 @@ mod tests { shutdown_neighbor_node_addr.ip_addr(), shutdown_neighbor_node_addr.ports()[0], ); - let subject_node = make_global_cryptde_node_record(1345, true, &CRYPTDE_PAIR); - let mut subject = neighborhood_from_nodes(&subject_node, None, &CRYPTDE_PAIR); + let subject_node = make_global_cryptde_node_record(1345, true, &N_CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); subject .neighborhood_database .add_node(gossip_neighbor_node.clone()) @@ -7423,7 +7429,7 @@ mod tests { init_test_logging(); let system = System::new("test"); let subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ZeroHop, @@ -7550,7 +7556,7 @@ mod tests { let mut subject = make_standard_subject(); // This mock is completely unprepared: any call to it should cause a panic let persistent_config = PersistentConfigurationMock::new(); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + subject.persistent_config = Box::new(persistent_config); let neighbor_keys_before = vec![PublicKey::new(b"ABCDE"), PublicKey::new(b"FGHIJ")] .into_iter() .collect(); @@ -7572,22 +7578,28 @@ mod tests { let act = |data_dir: &Path| { let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_earning_wallet(make_wallet("earning_wallet")), ); subject.data_directory = data_dir.to_path_buf(); - subject.connect_database(); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryReal::new(subject.data_directory.clone())); + subject.persistent_config_factory.make(); }; assert_on_initialization_with_panic_on_migration(&data_dir, &act); } fn make_standard_subject() -> Neighborhood { - let root_node = make_global_cryptde_node_record(9999, true, &CRYPTDE_PAIR); + let root_node = make_global_cryptde_node_record(9999, true, &N_CRYPTDE_PAIR); let neighbor_node = make_node_record(9998, true); - let mut subject = neighborhood_from_nodes(&root_node, Some(&neighbor_node), &CRYPTDE_PAIR); - let persistent_config = PersistentConfigurationMock::new(); - subject.persistent_config_opt = Some(Box::new(persistent_config)); + let mut subject = neighborhood_from_nodes(&root_node, Some(&neighbor_node), &N_CRYPTDE_PAIR); + let persistent_config = PersistentConfigurationMock::new() + .rate_pack_limits_result(Ok(RatePackLimits::test_default())); + subject.persistent_config = Box::new(persistent_config); + subject.gossip_acceptor = Box::new(GossipAcceptorReal::new( + N_CRYPTDE_PAIR.main.dup(), + subject.persistent_config.as_ref() + )); subject } @@ -7627,7 +7639,7 @@ mod tests { )>, >, >, - handle_results: RefCell>, + handle_results: RefCell>>, } impl GossipAcceptor for GossipAcceptorMock { @@ -7637,9 +7649,10 @@ mod tests { agrs: Vec, gossip_source: SocketAddr, neighborhood_metadata: NeighborhoodMetadata, - ) -> GossipAcceptanceResult { + ) -> Vec { self.handle_params.lock().unwrap().push(( database.clone(), + // TODO: Figure out how to store some represntation of persistent_config agrs, gossip_source, neighborhood_metadata, @@ -7673,7 +7686,7 @@ mod tests { self } - pub fn handle_result(self, result: GossipAcceptanceResult) -> GossipAcceptorMock { + pub fn handle_result(self, result: Vec) -> GossipAcceptorMock { self.handle_results.borrow_mut().push(result); self } @@ -7754,7 +7767,7 @@ mod tests { let bootstrap_config = bc_from_nc_plus(neighborhood_config, make_wallet("earning"), None, test_name); - let mut neighborhood = Neighborhood::new(CRYPTDE_PAIR.clone(), &bootstrap_config); + let mut neighborhood = Neighborhood::new(N_CRYPTDE_PAIR.clone(), &bootstrap_config); let (node_to_ui_recipient, _) = make_node_to_ui_recipient(); neighborhood.node_to_ui_recipient_opt = Some(node_to_ui_recipient); @@ -7769,7 +7782,7 @@ mod tests { ) -> Option { let system = System::new("test"); let mut subject = Neighborhood::new( - CRYPTDE_PAIR.clone(), + N_CRYPTDE_PAIR.clone(), &bc_from_nc_plus( NeighborhoodConfig { mode: NeighborhoodMode::ConsumeOnly(vec![make_node_descriptor(make_ip(1))]), @@ -7808,12 +7821,19 @@ mod tests { } fn make_neighborhood_with_linearly_connected_nodes(nodes_count: u16) -> Neighborhood { - let root_node = make_global_cryptde_node_record(4242, true, &CRYPTDE_PAIR); + let root_node = make_global_cryptde_node_record(4242, true, &N_CRYPTDE_PAIR); let mut nodes = make_node_records(nodes_count); nodes[0] = root_node; let db = linearly_connect_nodes(&nodes); - let mut neighborhood = neighborhood_from_nodes(db.root(), nodes.get(1), &CRYPTDE_PAIR); + let mut neighborhood = neighborhood_from_nodes(db.root(), nodes.get(1), &N_CRYPTDE_PAIR); neighborhood.neighborhood_database = db; + let persistent_config_mock = PersistentConfigurationMock::new() + .rate_pack_limits_result(Ok(RatePackLimits::test_default())); + let gossip_acceptor = GossipAcceptorReal::new( + N_CRYPTDE_PAIR.main.dup(), + &persistent_config_mock + ); + neighborhood.gossip_acceptor = Box::new(gossip_acceptor); neighborhood } diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 7e0f68ef3b..41b76d0113 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -591,36 +591,36 @@ fn configure_rate_pack( |pc: &mut dyn PersistentConfiguration, rate_pack| pc.set_rate_pack(rate_pack), ) { Ok(rate_pack) => { - let (low_limit, high_limit) = match persist_config.rate_pack_limits() { - Ok(pair) => pair, + let rate_pack_limits = match persist_config.rate_pack_limits() { + Ok(rpl) => rpl, Err(e) => return Err(e.into_configurator_error("rate-pack")), }; let mut error = ConfiguratorError::new(vec![]); error = check_min_and_max( rate_pack.routing_byte_rate, - low_limit.routing_byte_rate, - high_limit.routing_byte_rate, + rate_pack_limits.lo.routing_byte_rate, + rate_pack_limits.hi.routing_byte_rate, "routing_byte_rate", error, ); error = check_min_and_max( rate_pack.routing_service_rate, - low_limit.routing_service_rate, - high_limit.routing_service_rate, + rate_pack_limits.lo.routing_service_rate, + rate_pack_limits.hi.routing_service_rate, "routing_service_rate", error, ); error = check_min_and_max( rate_pack.exit_byte_rate, - low_limit.exit_byte_rate, - high_limit.exit_byte_rate, + rate_pack_limits.lo.exit_byte_rate, + rate_pack_limits.hi.exit_byte_rate, "exit_byte_rate", error, ); error = check_min_and_max( rate_pack.exit_service_rate, - low_limit.exit_service_rate, - high_limit.exit_service_rate, + rate_pack_limits.lo.exit_service_rate, + rate_pack_limits.hi.exit_service_rate, "exit_service_rate", error, ); @@ -694,7 +694,7 @@ mod tests { use crate::db_config::persistent_configuration::PersistentConfigurationReal; use crate::sub_lib::accountant::DEFAULT_PAYMENT_THRESHOLDS; use crate::sub_lib::cryptde::{PlainData, PublicKey}; - use crate::sub_lib::neighborhood::{Hops, DEFAULT_RATE_PACK}; + use crate::sub_lib::neighborhood::{Hops, RatePackLimits, DEFAULT_RATE_PACK}; use crate::sub_lib::utils::make_new_multi_config; use crate::sub_lib::wallet::Wallet; use crate::test_utils::neighborhood_test_utils::MIN_HOPS_FOR_TEST; @@ -2494,7 +2494,10 @@ mod tests { let mut persistent_config = PersistentConfigurationMock::new() .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) .set_rate_pack_result(Ok(())) - .rate_pack_limits_result(Ok((RatePack::new(5, 5, 5, 5), RatePack::new(7, 7, 7, 7)))); + .rate_pack_limits_result(Ok(RatePackLimits::new( + RatePack::new(5, 5, 5, 5), + RatePack::new(7, 7, 7, 7) + ))); let result = configure_rate_pack( &make_simplified_multi_config(["--rate-pack", "4|4|4|4"]), @@ -2526,7 +2529,10 @@ mod tests { let mut persistent_config = PersistentConfigurationMock::new() .rate_pack_result(Ok(RatePack::new(0, 0, 0, 0))) .set_rate_pack_result(Ok(())) - .rate_pack_limits_result(Ok((RatePack::new(5, 5, 5, 5), RatePack::new(7, 7, 7, 7)))); + .rate_pack_limits_result(Ok(RatePackLimits::new( + RatePack::new(5, 5, 5, 5), + RatePack::new(7, 7, 7, 7) + ))); let result = configure_rate_pack( &make_simplified_multi_config(["--rate-pack", "8|8|8|8"]), @@ -2793,7 +2799,7 @@ mod tests { .past_neighbors_result(past_neighbors_result) .mapping_protocol_result(Ok(Some(AutomapProtocol::Pcp))) .rate_pack_result(Ok(rate_pack)) - .rate_pack_limits_result(Ok(( + .rate_pack_limits_result(Ok(RatePackLimits::new( RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ))) diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 448c3e50ee..aa37e97d56 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -31,6 +31,7 @@ use std::fmt::{Debug, Display, Formatter}; use std::net::IpAddr; use std::str::FromStr; use std::time::Duration; +use masq_lib::shared_schema::ConfiguratorError; const ASK_ABOUT_GOSSIP_INTERVAL: Duration = Duration::from_secs(10); @@ -83,6 +84,89 @@ impl RatePack { } } +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RatePackLimits { + pub lo: RatePack, + pub hi: RatePack +} + +impl RatePackLimits { + pub fn new(lo: RatePack, hi: RatePack) -> Self { + Self { lo, hi } + } + + pub fn check(&self, rate_pack: &RatePack) -> bool { + match self.analyze(rate_pack) { + Ok(_) => true, + Err(_) => false, + } + } + + pub fn analyze(&self, rate_pack: &RatePack) -> Result<(), ConfiguratorError> { + let check_min_and_max = | + candidate: u64, + min: u64, + max: u64, + name: &str, + error: ConfiguratorError + | -> ConfiguratorError { + let mut result = error; + if candidate < min { + result = result.another_required( + "rate-pack", + &format!( + "Value of {} ({}) is below the minimum allowed ({})", + name, candidate, min + ), + ); + } else if candidate > max { + result = result.another_required( + "rate-pack", + &format!( + "Value of {} ({}) is above the maximum allowed ({})", + name, candidate, max + ), + ); + } + result + }; + let mut error = ConfiguratorError::new(vec![]); + error = check_min_and_max( + rate_pack.routing_byte_rate, + self.lo.routing_byte_rate, + self.hi.routing_byte_rate, + "routing_byte_rate", + error, + ); + error = check_min_and_max( + rate_pack.routing_service_rate, + self.lo.routing_service_rate, + self.hi.routing_service_rate, + "routing_service_rate", + error, + ); + error = check_min_and_max( + rate_pack.exit_byte_rate, + self.lo.exit_byte_rate, + self.hi.exit_byte_rate, + "exit_byte_rate", + error, + ); + error = check_min_and_max( + rate_pack.exit_service_rate, + self.lo.exit_service_rate, + self.hi.exit_service_rate, + "exit_service_rate", + error, + ); + if error.is_empty() { + Ok(()) + } else { + Err(error) + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum NeighborhoodMode { Standard(NodeAddr, Vec, RatePack), diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index 02ba441a4d..06f8ec794a 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -18,6 +18,8 @@ use std::io::Read; use std::iter::once; use std::path::Path; use std::sync::{Arc, Mutex}; +use crate::db_config::persistent_configuration::{PersistentConfiguration, PersistentConfigurationFactory}; +use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; pub fn bring_db_0_back_to_life_and_return_connection(db_path: &Path) -> Connection { match remove_file(db_path) { @@ -302,3 +304,19 @@ pub fn make_external_data() -> ExternalData { db_password_opt: None, } } + +pub struct PersistentConfigurationFactoryTest { + mock_opt: RefCell> +} + +impl PersistentConfigurationFactory for PersistentConfigurationFactoryTest { + fn make(&self) -> Box { + Box::new(self.mock_opt.borrow_mut().take().expect("PersistentConfigurationFactoryTest already used")) + } +} + +impl PersistentConfigurationFactoryTest { + pub fn new(mock: PersistentConfigurationMock) -> Self { + Self { mock_opt: RefCell::new(Some(mock)) } + } +} \ No newline at end of file diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 7c78d99a93..31bc43cfcf 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -507,7 +507,7 @@ pub mod unshared_test_utils { use crate::node_test_utils::DirsWrapperMock; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::CryptDE; - use crate::sub_lib::neighborhood::{ConnectionProgressMessage, RatePack, DEFAULT_RATE_PACK}; + use crate::sub_lib::neighborhood::{ConnectionProgressMessage, RatePack, RatePackLimits, DEFAULT_RATE_PACK}; use crate::sub_lib::proxy_client::ClientResponsePayload_0v1; use crate::sub_lib::proxy_server::{ClientRequestPayload_0v1, ProxyProtocol}; use crate::sub_lib::sequence_buffer::SequencedPacket; @@ -570,13 +570,13 @@ pub mod unshared_test_utils { Either::Left((message_start, message_end)) => { assert!( panic_message_str.contains(message_start), - "We expected this message {} to start with {}", + "We expected this message '{}' to start with '{}'", panic_message_str, message_start ); assert!( panic_message_str.ends_with(message_end), - "We expected this message {} to end with {}", + "We expected this message '{}' to end with '{}'", panic_message_str, message_end ); @@ -631,7 +631,7 @@ pub mod unshared_test_utils { } else { config }; - let config = config.rate_pack_limits_result(Ok(( + let config = config.rate_pack_limits_result(Ok(RatePackLimits::new( RatePack::new(u64::MIN, u64::MIN, u64::MIN, u64::MIN), RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ))); diff --git a/node/src/test_utils/persistent_configuration_mock.rs b/node/src/test_utils/persistent_configuration_mock.rs index afcb7428d7..ac9264a8dd 100644 --- a/node/src/test_utils/persistent_configuration_mock.rs +++ b/node/src/test_utils/persistent_configuration_mock.rs @@ -6,7 +6,7 @@ use crate::database::rusqlite_wrappers::TransactionSafeWrapper; use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration}; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::CryptDE; -use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack}; +use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack, RatePackLimits}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::unshared_test_utils::arbitrary_id_stamp::ArbitraryIdStamp; use crate::{arbitrary_id_stamp_in_trait_impl, set_arbitrary_id_stamp_in_mock_impl}; @@ -76,7 +76,7 @@ pub struct PersistentConfigurationMock { set_payment_thresholds_params: Arc>>, set_payment_thresholds_results: RefCell>>, rate_pack_results: RefCell>>, - rate_pack_limits_results: RefCell>>, + rate_pack_limits_results: RefCell>>, set_rate_pack_params: Arc>>, set_rate_pack_results: RefCell>>, scan_intervals_results: RefCell>>, @@ -404,7 +404,7 @@ impl PersistentConfiguration for PersistentConfigurationMock { self.set_rate_pack_results.borrow_mut().remove(0) } - fn rate_pack_limits(&self) -> Result<(RatePack, RatePack), PersistentConfigError> { + fn rate_pack_limits(&self) -> Result { self.rate_pack_limits_results.borrow_mut().remove(0) } @@ -767,7 +767,7 @@ impl PersistentConfigurationMock { pub fn rate_pack_limits_result( self, - result: Result<(RatePack, RatePack), PersistentConfigError>, + result: Result, ) -> Self { self.rate_pack_limits_results.borrow_mut().push(result); self From 0fc232beda8c553dfa57c7bfd52f08ff02538370 Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Mon, 10 Nov 2025 08:16:36 -0500 Subject: [PATCH 6/9] First Standard Gossip test passing --- node/src/neighborhood/gossip_acceptor.rs | 211 +++++++++++++++++------ node/src/neighborhood/mod.rs | 2 +- 2 files changed, 162 insertions(+), 51 deletions(-) diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index ff47c933e9..ddb59ab890 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -14,6 +14,7 @@ use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::net::{IpAddr, SocketAddr}; use std::time::{Duration, SystemTime}; +use itertools::Itertools; use crate::db_config::persistent_configuration::PersistentConfiguration; /// Note: if you decide to change this, make sure you test thoroughly. Values less than 5 may lead @@ -33,6 +34,13 @@ pub enum GossipAcceptanceResult { // The incoming Gossip was proper, and we tried to accept it, but couldn't. Failed(GossipFailure_0v1, PublicKey, NodeAddr), // Gossip was ignored because it was evil: ban the sender of the Gossip as a malefactor. + // TODO: This is wrong. We don't always want to ban the sender; sometimes we'll want to ban + // some other Node in the Gossip. And we can't refer to that Node by public key and then look + // it up later, because if we're banning it we've probably already pulled it out of the + // neighborhood database. This variant needs to be able to contain information about at least: + // public key, IP address, and earning wallet address. Possibly more. Since this will be + // a collection of Options, perhaps the Options should be collected in a struct, which would + // be an argument to the Ban variant. Ban(String), } @@ -989,6 +997,7 @@ impl IntroductionHandler { } struct StandardGossipHandler { + rate_pack_limits: RatePackLimits, logger: Logger, } @@ -1075,7 +1084,11 @@ impl GossipHandler for StandardGossipHandler { StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); let patch = self.compute_patch(&agrs, database.root(), neighborhood_metadata.db_patch_size); - let filtered_agrs = self.filter_agrs_by_patch(agrs, patch); + let filtered_agrs = agrs + .into_iter() + .filter(|agr| self.contained_by_patch(agr, &patch)) + .filter(|agr| self.allowed_by_rate_pack_limits(agr, gossip_source)) + .collect_vec(); let mut db_changed = self.identify_and_add_non_introductory_new_nodes( database, @@ -1115,8 +1128,8 @@ impl GossipHandler for StandardGossipHandler { } impl StandardGossipHandler { - fn new(logger: Logger) -> StandardGossipHandler { - StandardGossipHandler { logger } + fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> StandardGossipHandler { + StandardGossipHandler { rate_pack_limits: rate_pack_limits.clone(), logger } } fn compute_patch( @@ -1178,20 +1191,37 @@ impl StandardGossipHandler { } } - fn filter_agrs_by_patch( + fn contained_by_patch( &self, - agrs: Vec, - patch: HashSet, - ) -> Vec { - agrs.into_iter() - .filter(|agr| patch.contains(&agr.inner.public_key)) - .collect::>() + agr: &AccessibleGossipRecord, + patch: &HashSet, + ) -> bool { + patch.contains(&agr.inner.public_key) + } + + fn allowed_by_rate_pack_limits( + &self, + agr: &AccessibleGossipRecord, + gossip_source: SocketAddr, + ) -> bool { + match GossipAcceptorReal::validate_new_version( + agr, + format!("Node {} from Standard gossip received from {}", + agr.inner.public_key, + gossip_source + ), + &self.rate_pack_limits, + &self.logger + ) { + Ok(_) => true, + Err(_) => false // TODO: Return a GossipAcceptanceResult::Ban for the rejected Node + } } fn identify_and_add_non_introductory_new_nodes( &self, database: &mut NeighborhoodDatabase, - agrs: &[AccessibleGossipRecord], + agrs: &Vec, gossip_source: SocketAddr, user_exit_preferences_opt: Option<&UserExitPreferences>, ) -> bool { @@ -1433,7 +1463,7 @@ impl GossipAcceptorReal { Box::new(DebutHandler::new(rate_pack_limits, logger.clone())), Box::new(PassHandler::new()), Box::new(IntroductionHandler::new(rate_pack_limits, logger.clone())), - Box::new(StandardGossipHandler::new(logger.clone())), + Box::new(StandardGossipHandler::new(&RatePackLimits::test_default(), logger.clone())), Box::new(RejectHandler::new()), ], cryptde, @@ -1553,7 +1583,7 @@ mod tests { use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; lazy_static! { - static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); + static ref GA_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } #[test] @@ -2606,7 +2636,7 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), false) .build(); - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2632,7 +2662,7 @@ mod tests { .node(node_a.public_key(), false) .node(dest_node.public_key(), false) .build(); - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2667,7 +2697,7 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), true) .build(); - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2705,7 +2735,7 @@ mod tests { &node_a.node_addr_opt().unwrap().ip_addr(), &[4567], )); - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2764,7 +2794,7 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), false) .build(); - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); @@ -2836,7 +2866,7 @@ mod tests { This test proves that E is excluded, because the distance of A and E is more than 3 hops. */ - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -2885,7 +2915,7 @@ mod tests { 2) To find neighbors of neighbors, we'll look into the AGRs. (For Example, B---Y, B---C, and C---D). */ - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -2938,7 +2968,7 @@ mod tests { init_test_logging(); let test_name = "standard_gossip_handler_can_handle_node_for_which_agr_is_not_found_while_computing_patch"; - let subject = StandardGossipHandler::new(Logger::new(test_name)); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -2993,8 +3023,8 @@ mod tests { */ - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let subject = StandardGossipHandler::new(Logger::new("test")); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -3043,7 +3073,7 @@ mod tests { } fn assert_compute_patch(db_patch_size: u8) { - let subject = StandardGossipHandler::new(Logger::new("assert_compute_patch")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("assert_compute_patch")); // one node to finish hops and another node that's outside the patch let nodes_count = db_patch_size as usize + 2; let nodes = make_node_records(nodes_count as u16); @@ -3074,9 +3104,9 @@ mod tests { // This is Standard Gossip, even though it looks like a Debut, // because it's specifically handled by a StandardGossipHandler // instead of the GossipAcceptor (which would identify it as a Debut), - // so the test is unrealistic. Also that the Gossip is ignored because + // so the test is unrealistic. Also, that the Gossip is ignored because // Node B isn't in Node A's patch, which matters to a StandardGossipHandler. - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1111, true); let mut root_db = db_from_node(&root_node); let src_node = make_node_record(2222, true); @@ -3089,7 +3119,7 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3110,7 +3140,7 @@ mod tests { #[test] fn cpm_is_sent_in_case_full_neighborship_doesn_t_exist_and_is_created() { // Received Reply for Acceptance of Debut Gossip - (false, true) - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1111, true); let mut root_db = db_from_node(&root_node); let src_node = make_node_record(2222, true); @@ -3129,7 +3159,7 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3158,7 +3188,7 @@ mod tests { #[test] fn cpm_is_not_sent_in_case_full_neighborship_exists_and_is_destroyed() { // Somebody banned us. (true, false) - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1111, true); let mut root_db = db_from_node(&root_node); let src_node = make_node_record(2222, true); @@ -3176,7 +3206,7 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3197,7 +3227,7 @@ mod tests { #[test] fn cpm_is_not_sent_in_case_full_neighborship_exists_and_continues() { // Standard Gossips received after Neighborship is established (true, true) - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1111, true); let mut root_db = db_from_node(&root_node); let src_node = make_node_record(2222, true); @@ -3217,7 +3247,7 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(Logger::new("test")); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3276,12 +3306,93 @@ mod tests { #[test] fn standard_gossip_that_fails_validation_is_rejected() { - todo!("Complete me") + /* + Destination Node ==> + S---D + + Source Node ==> + A---S---D + | + B + + The source node(S) will gossip about Nodes A and B + to the destination node(D). Node B will be dropped because its + rate pack is outside the limits. + */ + init_test_logging(); + let src_root = make_node_record(1234, true); + let dest_root = make_node_record(2345, true); + let mut src_db = db_from_node(&src_root); + let node_a = make_node_record(5678, true); + let mut node_b = make_node_record(7777u16, true); + node_b.inner.rate_pack = RatePack::new (0, 0, 0, 0); // invalid + node_b.resign(); + let mut dest_db = db_from_node(&dest_root); + dest_db.add_node(src_root.clone()).unwrap(); + dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), src_root.public_key()); + src_db.add_node(dest_db.root().clone()).unwrap(); + src_db.add_node(node_a.clone()).unwrap(); + src_db.add_node(node_b.clone()).unwrap(); + src_db.add_arbitrary_full_neighbor(src_root.public_key(), dest_root.public_key()); + src_db.add_arbitrary_half_neighbor(src_root.public_key(), &node_a.public_key()); + src_db.add_arbitrary_full_neighbor(src_root.public_key(), &node_b.public_key()); + src_db + .node_by_key_mut(src_root.public_key()) + .unwrap() + .increment_version(); + src_db.resign_node(src_root.public_key()); + let gossip = GossipBuilder::new(&src_db) + .node(src_root.public_key(), true) + .node(node_a.public_key(), false) + .node(node_b.public_key(), false) + .build(); + let rate_pack_limits = RatePackLimits::new( + RatePack::new(100, 100, 100, 100), + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), + ); + let subject = StandardGossipHandler::new(&rate_pack_limits, Logger::new("test")); + let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); + let agrs_vec: Vec = gossip.try_into().unwrap(); + let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); + let (cpm_recipient, cpm_recording_arc) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; + let system = System::new("test"); + + let handle_result = subject.handle( + &cryptde, + &mut dest_db, + agrs_vec, + gossip_source, + neighborhood_metadata, + ); + + let analysis = format!("{:?}", rate_pack_limits.analyze(&node_b.inner.rate_pack).err().unwrap()); + let message = format!( + "Node {} from Standard gossip received from {:?} rejected due to rate pack limit violation: {}", + node_b.public_key(), + gossip_source, + analysis + ); + assert_eq!( + handle_result, + vec![ + GossipAcceptanceResult::Accepted, + // GossipAcceptanceResult::Ban(message.clone()) + ] + ); + assert_eq!(dest_db.node_by_key(node_a.public_key()).is_some(), true); + assert_eq!(dest_db.node_by_key(node_b.public_key()).is_none(), true); // rejected + System::current().stop(); + assert_eq!(system.run(), 0); + let recording = cpm_recording_arc.lock().unwrap(); + assert_eq!(recording.len(), 0); + TestLogHandler::new().exists_log_containing(&message); } #[test] fn last_gossip_handler_rejects_everything() { - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let reject_handler = subject.gossip_handlers.last().unwrap(); let db = make_meaningless_db(); let (debut, _, debut_gossip_source) = make_debut(1234, Mode::Standard); @@ -3351,7 +3462,7 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), false) .build(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let result = subject.handle( &mut dest_db, @@ -3747,7 +3858,7 @@ mod tests { .node(src_node.public_key(), true) .build(); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let result = subject.handle( &mut dest_db, @@ -3785,7 +3896,7 @@ mod tests { .build(); let debut_agrs = debut.try_into().unwrap(); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let begin_at = time_t_timestamp(); let result = subject.handle( @@ -3819,7 +3930,7 @@ mod tests { .build(); let debut_agrs = debut.try_into().unwrap(); let gossip_source = src_node.node_addr_opt().unwrap().into(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let result = subject.handle( &mut dest_db, @@ -3836,7 +3947,7 @@ mod tests { let gnr = GossipNodeRecord::from(( root_node.inner.clone(), root_node.node_addr_opt(), - CRYPTDE_PAIR.main.as_ref(), + GA_CRYPTDE_PAIR.main.as_ref(), )); let debut_gossip = Gossip_0v1 { node_records: vec![gnr], @@ -3868,7 +3979,7 @@ mod tests { .build(); let debut_agrs = debut.try_into().unwrap(); let gossip_source = src_node.node_addr_opt().unwrap().into(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let result = subject.handle( &mut dest_db, @@ -3885,7 +3996,7 @@ mod tests { let gnr = GossipNodeRecord::from(( root_node.inner.clone(), root_node.node_addr_opt(), - CRYPTDE_PAIR.main.as_ref(), + GA_CRYPTDE_PAIR.main.as_ref(), )); let debut_gossip = Gossip_0v1 { node_records: vec![gnr], @@ -3914,7 +4025,7 @@ mod tests { #[test] fn introduction_gossip_handler_sends_cpm_for_neighborship_established() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let subject = IntroductionHandler::new( @@ -3965,7 +4076,7 @@ mod tests { let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let (gossip, pass_target, gossip_source) = make_pass(2345); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let result = subject.handle( &mut db, @@ -3990,7 +4101,7 @@ mod tests { #[test] fn handles_a_new_pass_target() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let subject = PassHandler::new(); @@ -4037,7 +4148,7 @@ mod tests { #[test] fn handles_pass_target_that_is_not_yet_expired() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let subject = PassHandler::new(); @@ -4084,7 +4195,7 @@ mod tests { #[test] fn handles_pass_target_that_is_a_part_of_a_different_connection_progress() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let subject = PassHandler::new(); @@ -4121,7 +4232,7 @@ mod tests { #[test] fn handles_pass_target_that_has_expired() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); + let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); let subject = PassHandler::new(); @@ -4242,7 +4353,7 @@ mod tests { .node(node_e.public_key(), true) .node(node_f.public_key(), true) .build(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let before = time_t_timestamp(); let result = subject.handle( @@ -4474,7 +4585,7 @@ mod tests { .node(current_node.public_key(), false) .node(obsolete_node.public_key(), false) .build(); - let subject = make_subject(CRYPTDE_PAIR.main.as_ref()); + let subject = make_subject(GA_CRYPTDE_PAIR.main.as_ref()); let original_dest_db = dest_db.clone(); let before = time_t_timestamp(); diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index da4dcecf54..3c2784a11b 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -768,7 +768,7 @@ impl Neighborhood { db_patch_size: self.db_patch_size, user_exit_preferences_opt: Some(self.user_exit_preferences.clone()), }; - let mut acceptance_results = self.gossip_acceptor.handle( + let acceptance_results = self.gossip_acceptor.handle( &mut self.neighborhood_database, agrs, gossip_source, From cf83017cfb481fe360e3b0cc6bd8f7bee6c7627a Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Fri, 14 Nov 2025 21:24:25 -0500 Subject: [PATCH 7/9] Some tests are passing. Are all tests passing? --- node/Cargo.toml | 2 +- .../src/db_config/persistent_configuration.rs | 22 +- node/src/neighborhood/gossip_acceptor.rs | 850 +++++++++++------- node/src/neighborhood/malefactor.rs | 448 +++++++++ node/src/neighborhood/mod.rs | 142 +-- .../unprivileged_parse_args_configuration.rs | 4 +- node/src/node_test_utils.rs | 15 + node/src/sub_lib/neighborhood.rs | 17 +- node/src/test_utils/database_utils.rs | 21 +- node/src/test_utils/mod.rs | 4 +- 10 files changed, 1110 insertions(+), 415 deletions(-) create mode 100644 node/src/neighborhood/malefactor.rs diff --git a/node/Cargo.toml b/node/Cargo.toml index 9933071d09..ebfba97b8e 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -15,7 +15,7 @@ automap = { path = "../automap"} backtrace = "0.3.57" base64 = "0.13.0" bytes = "0.4.12" -time = {version = "0.3.11", features = [ "macros" ]} +time = { version = "0.3.11", features = ["macros", "local-offset"] } clap = "2.33.3" crossbeam-channel = "0.5.1" dirs = "4.0.0" diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 4e47b2e188..1bb8db9c42 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -3,6 +3,7 @@ use crate::arbitrary_id_stamp_in_trait; use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::bip39::{Bip39, Bip39Error}; +use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; use crate::database::rusqlite_wrappers::{ConnectionWrapper, TransactionSafeWrapper}; use crate::db_config::config_dao::{ConfigDao, ConfigDaoError, ConfigDaoReal, ConfigDaoRecord}; use crate::db_config::secure_config_layer::{SecureConfigLayer, SecureConfigLayerError}; @@ -15,6 +16,7 @@ use crate::sub_lib::cryptde::{CryptDE, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::neighborhood::{Hops, NodeDescriptor, RatePack, RatePackLimits}; +use crate::sub_lib::utils::db_connection_launch_panic; use crate::sub_lib::wallet::Wallet; use lazy_static::lazy_static; use masq_lib::blockchains::chains::Chain; @@ -29,8 +31,6 @@ use std::net::{Ipv4Addr, SocketAddrV4, TcpListener}; use std::path::PathBuf; use std::str::FromStr; use websocket::url::Url; -use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; -use crate::sub_lib::utils::db_connection_launch_panic; lazy_static! { static ref RATE_PACK_LIMIT_FORMAT: Regex = @@ -594,7 +594,7 @@ impl PersistentConfiguration for PersistentConfigurationReal { .expect(format!("Syntax error in rate_pack_limits value '{}': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.", limits_string).as_str()); let candidate = RatePackLimits::new( Self::extract_candidate(&captures, 1), - Self::extract_candidate(&captures, 2) + Self::extract_candidate(&captures, 2), ); Self::check_rate_pack_limit_order( candidate.lo.routing_byte_rate, @@ -778,7 +778,10 @@ impl PersistentConfiguration for PersistentConfigurationInvalid { ) -> Result<(), PersistentConfigError> { PersistentConfigurationInvalid::invalid() } - fn consuming_wallet(&self, _db_password: &str) -> Result, PersistentConfigError> { + fn consuming_wallet( + &self, + _db_password: &str, + ) -> Result, PersistentConfigError> { PersistentConfigurationInvalid::invalid() } fn consuming_wallet_private_key( @@ -864,7 +867,10 @@ impl PersistentConfiguration for PersistentConfigurationInvalid { fn max_block_count(&self) -> Result, PersistentConfigError> { PersistentConfigurationInvalid::invalid() } - fn set_max_block_count(&mut self, _value_opt: Option) -> Result<(), PersistentConfigError> { + fn set_max_block_count( + &mut self, + _value_opt: Option, + ) -> Result<(), PersistentConfigError> { PersistentConfigurationInvalid::invalid() } fn set_start_block_from_txn( @@ -921,7 +927,7 @@ pub trait PersistentConfigurationFactory { } pub struct PersistentConfigurationFactoryReal { - data_directory: PathBuf + data_directory: PathBuf, } impl PersistentConfigurationFactory for PersistentConfigurationFactoryReal { @@ -939,9 +945,7 @@ impl PersistentConfigurationFactory for PersistentConfigurationFactoryReal { impl PersistentConfigurationFactoryReal { pub fn new(data_directory: PathBuf) -> Self { - Self { - data_directory - } + Self { data_directory } } } diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index ddb59ab890..bcdb64cfa1 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -1,21 +1,25 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::db_config::persistent_configuration::PersistentConfiguration; use crate::neighborhood::gossip::{ AccessibleGossipRecord, GossipBuilder, GossipNodeRecord, Gossip_0v1, }; +use crate::neighborhood::malefactor::Malefactor; use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, NeighborhoodDatabaseError}; use crate::neighborhood::node_record::NodeRecord; use crate::neighborhood::UserExitPreferences; use crate::sub_lib::cryptde::{CryptDE, PublicKey}; -use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, RatePackLimits}; +use crate::sub_lib::neighborhood::{ + ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, + RatePackLimits, +}; use crate::sub_lib::node_addr::NodeAddr; +use itertools::Itertools; use masq_lib::logger::Logger; use std::cell::RefCell; use std::collections::{HashMap, HashSet}; use std::net::{IpAddr, SocketAddr}; use std::time::{Duration, SystemTime}; -use itertools::Itertools; -use crate::db_config::persistent_configuration::PersistentConfiguration; /// Note: if you decide to change this, make sure you test thoroughly. Values less than 5 may lead /// to inability to grow the network beyond a very small size; values greater than 5 may lead to @@ -41,13 +45,14 @@ pub enum GossipAcceptanceResult { // public key, IP address, and earning wallet address. Possibly more. Since this will be // a collection of Options, perhaps the Options should be collected in a struct, which would // be an argument to the Ban variant. - Ban(String), + Ban(Malefactor), } #[derive(Clone, PartialEq, Eq, Debug)] enum Qualification { Matched, Unmatched, + // TODO: Malformed will need to be modified to carry a Malefactor instead of a String Malformed(String), } @@ -99,23 +104,6 @@ impl GossipHandler for DebutHandler { if database.node_by_key(&agrs[0].inner.public_key).is_some() { return Qualification::Unmatched; } - //TODO: Create optimization card to drive in the following logic: - // Imagine a brand-new network, consisting only of Node A. - // When Node B debuts, Node A cannot respond with an Introduction, - // since there's nobody to introduce. Therefore, Node A must - // respond with a single-Node Gossip that will currently be - // interpreted as a Debut by Node B, resulting in another - // single-Node Gossip from B to A. This last Gossip isn't a - // problem, because Node A will ignore it since B is already - // in its database. However, there is a possible optimization: - // drive in the code below, and Node B will no longer interpret - // Node A's acceptance Gossip as another Debut, because it will - // see that Node A already has Node B as a neighbor. This means - // Node A's original response will be interpreted as Standard - // Gossip. - // if agrs[0].inner.neighbors.contains(database.root_key()) { - // return Qualification::Unmatched; - // } match &agrs[0].node_addr_opt { None => { if agrs[0].inner.accepts_connections { @@ -166,11 +154,25 @@ impl GossipHandler for DebutHandler { }; if let Err(message) = GossipAcceptorReal::validate_new_version( &source_agr, - format!("Debut from {} at {}", source_agr.inner.public_key, gossip_source), + format!( + "Debut from {} at {}", + source_agr.inner.public_key, gossip_source + ), &self.rate_pack_limits, - &self.logger + &self.logger, ) { - return vec![GossipAcceptanceResult::Ban(message)]; + return vec![GossipAcceptanceResult::Ban(Malefactor::new( + Some(source_agr.inner.public_key.clone()), + Some( + source_agr + .node_addr_opt + .expect("IP address disappeared") + .ip_addr(), + ), + Some(source_agr.inner.earning_wallet), + None, + message, + ))]; } let source_key = source_agr.inner.public_key.clone(); let source_node_addr = source_agr @@ -256,7 +258,7 @@ impl DebutHandler { fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> DebutHandler { DebutHandler { rate_pack_limits: rate_pack_limits.clone(), - logger + logger, } } @@ -348,7 +350,7 @@ impl DebutHandler { ip_addr_str); Ok(GossipAcceptanceResult::Accepted) } else { - match self.make_introduction(database, debuting_agr, gossip_source) { + match self.try_to_make_introduction(database, debuting_agr, gossip_source) { Some((introduction, target_key, target_node_addr)) => { Ok(GossipAcceptanceResult::Reply( introduction, @@ -368,7 +370,7 @@ impl DebutHandler { "DebutHandler database state: {}", &database.to_dot_graph(), ); - let debut_gossip = Self::create_debut_gossip_response( + let debut_gossip = Self::create_second_node_gossip_response( cryptde, database, debut_node_key, @@ -390,12 +392,15 @@ impl DebutHandler { } } - fn create_debut_gossip_response( + fn create_second_node_gossip_response( cryptde: &dyn CryptDE, database: &NeighborhoodDatabase, debut_node_key: PublicKey, ) -> Gossip_0v1 { let mut root_node = database.root().clone(); + // Several "second" Nodes may debut before the real second Node is fully established. If + // they do, we want to make sure each of them is isolated from the others and thinks that + // it is the real second Node. root_node.clear_half_neighbors(); root_node .add_half_neighbor_key(debut_node_key.clone()) @@ -412,7 +417,7 @@ impl DebutHandler { } } - fn make_introduction( + fn try_to_make_introduction( &self, database: &NeighborhoodDatabase, debuting_agr: &AccessibleGossipRecord, @@ -720,98 +725,139 @@ impl GossipHandler for IntroductionHandler { neighborhood_metadata: NeighborhoodMetadata, ) -> Vec { if database.root().full_neighbor_keys(database).len() >= MAX_DEGREE { - vec![] - } else { - let (introducer, introducee) = Self::identify_players(agrs, gossip_source) - .expect("Introduction not properly qualified"); - let mut introducer_ban_message = if let Err(message) = GossipAcceptorReal::validate_new_version( - &introducer, - format!( - "Introducer {} from {}", - introducer.inner.public_key, gossip_source - ), - &self.rate_pack_limits, - &self.logger + return vec![]; + } + let (introducer, introducee) = Self::identify_players(agrs, gossip_source) + .expect("Introduction not properly qualified"); + let mut introducer_ban_message = GossipAcceptorReal::validate_new_version( + &introducer, + format!( + "Introducer {} from {}", + introducer.inner.public_key, gossip_source + ), + &self.rate_pack_limits, + &self.logger, + ) + .err(); + let introducee_ban_message = GossipAcceptorReal::validate_new_version( + &introducee, + format!( + "Introducee {} at {}", + introducee.inner.public_key, + introducee + .node_addr_opt + .as_ref() + .expect("NodeAddr disappeared") + .ip_addr(), + ), + &self.rate_pack_limits, + &self.logger, + ) + .err(); + let introducer_key = introducer.inner.public_key.clone(); + let introducer_wallet = introducer.inner.earning_wallet.clone(); + let introducer_ip_addr = introducer + .node_addr_opt + .as_ref() + .expect("Introducer IP address disappeared") + .ip_addr(); + let introducee_ip_addr = introducee + .node_addr_opt + .as_ref() + .expect("Introducee IP address disappeared") + .ip_addr(); + let introducer_updated = if introducer_ban_message.is_none() { + match self.update_database( + database, + cryptde, + introducer, + neighborhood_metadata.user_exit_preferences_opt, ) { - Some(message) - } - else { - None - }; - let ignore_introducee = GossipAcceptorReal::validate_new_version( - &introducee, - format!( - "Introducee {} at {}", - introducee.inner.public_key, - introducee.node_addr_opt.as_ref().expect("NodeAddr disappeared").ip_addr(), - ), - &self.rate_pack_limits, - &self.logger - ).is_err(); - let introducer_key = introducer.inner.public_key.clone(); - let introducer_ip_addr = introducer - .node_addr_opt - .as_ref() - .expect("IP Address not found for the Node Addr.") - .ip_addr(); - let introducee_ip_addr = introducee - .node_addr_opt - .as_ref() - .expect("IP Address not found for the Node Addr.") - .ip_addr(); - if introducer_ban_message.is_none() { - match self.update_database( - database, - cryptde, - introducer, - neighborhood_metadata.user_exit_preferences_opt, - ) { - Ok(_) => (), - Err(e) => { - introducer_ban_message = Some(format!( - "Introducer {} tried changing immutable characteristic: {}", - introducer_key, e - )); - } + Ok(updated) => updated, + Err(e) => { + introducer_ban_message = Some(format!( + "Introducer {} tried changing immutable characteristic: {}", + introducer_key, e + )); + false } } + } else { + false + }; - if !ignore_introducee { - let connection_progress_message = ConnectionProgressMessage { - peer_addr: introducer_ip_addr, - event: ConnectionProgressEvent::IntroductionGossipReceived(introducee_ip_addr), - }; - neighborhood_metadata - .cpm_recipient - .try_send(connection_progress_message) - .expect("Neighborhood is dead"); - } + if introducee_ban_message.is_none() { + let connection_progress_message = ConnectionProgressMessage { + peer_addr: introducer_ip_addr, + event: ConnectionProgressEvent::IntroductionGossipReceived(introducee_ip_addr), + }; + neighborhood_metadata + .cpm_recipient + .try_send(connection_progress_message) + .expect("Neighborhood is dead"); + } - if ignore_introducee { - if let Some(message) = introducer_ban_message { - vec![GossipAcceptanceResult::Ban(message)] - } else { - vec![GossipAcceptanceResult::Accepted] - } + let introducer_ban_opt = introducer_ban_message.as_ref().map(|message| { + GossipAcceptanceResult::Ban(Malefactor::new( + Some(introducer_key), + Some(introducer_ip_addr), + Some(introducer_wallet), + None, + message.clone(), + )) + }); + let introducee_ban_opt = introducee_ban_message.as_ref().map(|message| { + GossipAcceptanceResult::Ban(Malefactor::new( + Some(introducee.inner.public_key.clone()), + Some(introducee_ip_addr), + Some(introducee.inner.earning_wallet.clone()), + None, + message.clone(), + )) + }); + match (introducer_updated, introducer_ban_opt, introducee_ban_opt) { + // Nothing new about the introducer, but we want to debut to the introducee + (false, None, None) => { + let (debut, target_key, target_node_addr) = + GossipAcceptorReal::make_debut_triple(database, &introducee) + .expect("Introduction not properly qualified"); + vec![GossipAcceptanceResult::Reply( + debut, + target_key, + target_node_addr, + )] } - else { + // Both the introducer and introducee are interesting. Gossip the introducer + // changes and debut to the introducee. + (true, None, None) => { let (debut, target_key, target_node_addr) = GossipAcceptorReal::make_debut_triple(database, &introducee) .expect("Introduction not properly qualified"); - let mut result = vec![]; - if let Some(message) = introducer_ban_message { - result.push(GossipAcceptanceResult::Ban(message)); - } - result.push(GossipAcceptanceResult::Reply(debut, target_key, target_node_addr)); - result + vec![ + GossipAcceptanceResult::Accepted, + GossipAcceptanceResult::Reply(debut, target_key, target_node_addr), + ] } + // Ban the introducer and distrust (ignore) but don't ban the introducee + (_, Some(introducer_ban), None) => vec![introducer_ban], + // Gossip the introducer changes, but ban the introducee + (true, None, Some(introducee_ban)) => { + vec![GossipAcceptanceResult::Accepted, introducee_ban] + } + // No introducer changes, but ban the introducee + (false, None, Some(introducee_ban)) => vec![introducee_ban], + // Ban both of them + (_, Some(introducer_ban), Some(introducee_ban)) => vec![introducer_ban, introducee_ban], } } } impl IntroductionHandler { fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> IntroductionHandler { - IntroductionHandler { rate_pack_limits: rate_pack_limits.clone(), logger } + IntroductionHandler { + rate_pack_limits: rate_pack_limits.clone(), + logger, + } } fn verify_size(agrs: &[AccessibleGossipRecord]) -> Option { @@ -1010,6 +1056,7 @@ impl NamedType for StandardGossipHandler { impl GossipHandler for StandardGossipHandler { // Standard Gossip must not be a Debut, Pass, or Introduction. There must be no record in the // Gossip describing the local Node (although there may be records that reference the local Node as a neighbor). + // There must be no Node in the Gossip that claims to reside at this Node's IP address. fn qualifies( &self, database: &NeighborhoodDatabase, @@ -1017,37 +1064,40 @@ impl GossipHandler for StandardGossipHandler { gossip_source: SocketAddr, ) -> Qualification { // must-not-be-debut-pass-or-introduction is assured by StandardGossipHandler's placement in the gossip_handlers list - let agrs_next_door = agrs + // Check to make sure no record claims this Node's IP address + let agrs_with_ips = agrs .iter() .filter(|agr| agr.node_addr_opt.is_some()) .collect::>(); let root_node = database.root(); if root_node.accepts_connections() { - if let Some(impostor) = agrs_next_door.iter().find(|agr| { - Self::ip_of(agr) + if let Some(impostor) = agrs_with_ips.iter().find(|agr_with_ip| { + Self::ip_of(agr_with_ip) == root_node .node_addr_opt() .expect("Root Node that accepts connections must have NodeAddr") .ip_addr() }) { return Qualification::Malformed( - format!("Standard Gossip from {} contains a record claiming that {} has this Node's IP address", + format!("Standard Gossip from {} contains a record claiming that {} resides at this Node's IP address", gossip_source, impostor.inner.public_key)); } } + // Check to make sure no record claims this Node's public key if agrs .iter() .any(|agr| &agr.inner.public_key == root_node.public_key()) { return Qualification::Malformed(format!( - "Standard Gossip from {} contains a record with this Node's public key", + "Standard Gossip from {} contains a record claiming this Node's public key", gossip_source )); } + // Check for duplicate IP addresses in the Gossip let init_addr_set: HashSet = HashSet::new(); let init_dup_set: HashSet = HashSet::new(); - let dup_set = agrs_next_door + let dup_set = agrs_with_ips .into_iter() .fold((init_addr_set, init_dup_set), |so_far, agr| { let (addr_set, dup_set) = so_far; @@ -1082,28 +1132,46 @@ impl GossipHandler for StandardGossipHandler { ) -> Vec { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); - + let gossip_source_agr = agrs + .iter() + .find(|agr| { + agr.node_addr_opt.as_ref().map(|na| na.ip_addr()) == Some(gossip_source.ip()) + }) + .expect(&format!( + "Standard Gossip from {} lacks source AGR", + gossip_source + )) + .clone(); let patch = self.compute_patch(&agrs, database.root(), neighborhood_metadata.db_patch_size); - let filtered_agrs = agrs + let in_patch_agrs = agrs .into_iter() .filter(|agr| self.contained_by_patch(agr, &patch)) - .filter(|agr| self.allowed_by_rate_pack_limits(agr, gossip_source)) .collect_vec(); - let mut db_changed = self.identify_and_add_non_introductory_new_nodes( + let (worthy_agrs, malefactor_bans) = + self.extract_malefactors(in_patch_agrs, database, &gossip_source_agr); + let mut db_changed = false; + + let (new_agrs, obsolete_agrs) = + self.identify_non_introductory_new_and_obsolete_nodes(database, worthy_agrs); + + db_changed |= !new_agrs.is_empty(); + self.add_new_nodes( database, - &filtered_agrs, - gossip_source, + new_agrs, neighborhood_metadata.user_exit_preferences_opt.as_ref(), ); - db_changed = self.identify_and_update_obsolete_nodes(database, filtered_agrs) || db_changed; - db_changed = + + db_changed |= !obsolete_agrs.is_empty(); + self.update_obsolete_nodes(database, obsolete_agrs); + + db_changed |= self.add_src_node_as_half_neighbor(cryptde, database, gossip_source) || db_changed; let final_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); // If no Nodes need updating, return ::Ignored and don't change the database. // Otherwise, return ::Accepted. - if db_changed { + let mut response = if db_changed { trace!(self.logger, "Current database: {}", database.to_dot_graph()); if (initial_neighborship_status, final_neighborship_status) == (false, true) { // Received Reply for Acceptance of Debut Gossip (false, true) @@ -1123,13 +1191,20 @@ impl GossipHandler for StandardGossipHandler { "Gossip contained nothing new: StandardGossipHandler is ignoring it" ); vec![] + }; + if !malefactor_bans.is_empty() { + response.extend(malefactor_bans); } + response } } impl StandardGossipHandler { fn new(rate_pack_limits: &RatePackLimits, logger: Logger) -> StandardGossipHandler { - StandardGossipHandler { rate_pack_limits: rate_pack_limits.clone(), logger } + StandardGossipHandler { + rate_pack_limits: rate_pack_limits.clone(), + logger, + } } fn compute_patch( @@ -1191,95 +1266,161 @@ impl StandardGossipHandler { } } - fn contained_by_patch( - &self, - agr: &AccessibleGossipRecord, - patch: &HashSet, - ) -> bool { + fn contained_by_patch(&self, agr: &AccessibleGossipRecord, patch: &HashSet) -> bool { patch.contains(&agr.inner.public_key) } - fn allowed_by_rate_pack_limits( + /* + // TODO: A node that tells us the IP Address of the node that isn't in our database should be malefactor banned + // Note: The below code doesn't really do what the above comment says + .filter(|agr| match &agr.node_addr_opt { + None => true, + Some(node_addr) => { + let socket_addrs: Vec = node_addr.clone().into(); + socket_addrs.contains(&gossip_source) + } + }) + */ + + fn extract_malefactors( &self, - agr: &AccessibleGossipRecord, - gossip_source: SocketAddr, - ) -> bool { - match GossipAcceptorReal::validate_new_version( - agr, - format!("Node {} from Standard gossip received from {}", - agr.inner.public_key, - gossip_source - ), - &self.rate_pack_limits, - &self.logger - ) { - Ok(_) => true, - Err(_) => false // TODO: Return a GossipAcceptanceResult::Ban for the rejected Node - } + agrs: Vec, + database: &NeighborhoodDatabase, + gossip_source_agr: &AccessibleGossipRecord, + ) -> (Vec, Vec) { + let gossip_source_ip = gossip_source_agr + .node_addr_opt + .as_ref() + .expect("NodeAddr on gossip source disappeared") + .ip_addr(); + // TODO: This would be more consistent with identify_non_introductory_new_and_obsolete_nodes + // below if it used a for loop with mutation. + let (valid_agrs, bans) = agrs.into_iter().fold((vec![], vec![]), |so_far, agr| { + let (mut valid_agrs, mut bans) = so_far; + match GossipAcceptorReal::validate_new_version( + &agr, + format!( + "Node {} from Standard gossip received from {}", + agr.inner.public_key, gossip_source_ip, + ), + &self.rate_pack_limits, + &self.logger, + ) { + Ok(_) => valid_agrs.push(agr), + Err(ban_message) => { + bans.push(GossipAcceptanceResult::Ban(Malefactor::new( + Some(agr.inner.public_key.clone()), + agr.node_addr_opt.map(|na| na.ip_addr()), + Some(agr.inner.earning_wallet.clone()), + None, + ban_message, + ))); + } + } + (valid_agrs, bans) + }); + let next_door_neighbor_keys = database + .root() + .inner + .neighbors + .iter() + .collect::>(); + let (valid_agrs, bans) = + valid_agrs.into_iter().fold((vec![], bans), |so_far, agr| { + let (mut valid_agrs, mut bans) = so_far; + if &agr.inner.public_key == database.root_key() { + // Shouldn't ever happen; but an evil Node could try it + valid_agrs.push(agr) + } + else if (agr.node_addr_opt.as_ref().map(|addr| addr.ip_addr()) != Some(gossip_source_ip)) && !next_door_neighbor_keys.contains(&&agr.inner.public_key) && agr.node_addr_opt.is_some() { + bans.push(GossipAcceptanceResult::Ban(Malefactor::new( + Some(gossip_source_agr.inner.public_key.clone()), + Some(gossip_source_ip), + Some(gossip_source_agr.inner.earning_wallet.clone()), + None, + format!( + "Node {} at {:?} sent Standard gossip that contained an IP address for victim Node {} that we should not have known", + gossip_source_agr.inner.public_key, + gossip_source_ip, + agr.inner.public_key, + ), + ))); + } + else { + valid_agrs.push(agr) + } + (valid_agrs, bans) + }); + (valid_agrs, bans) } - fn identify_and_add_non_introductory_new_nodes( + fn identify_non_introductory_new_and_obsolete_nodes( &self, database: &mut NeighborhoodDatabase, - agrs: &Vec, - gossip_source: SocketAddr, - user_exit_preferences_opt: Option<&UserExitPreferences>, - ) -> bool { + agrs: Vec, + ) -> (Vec, Vec) { let all_keys = database .keys() .into_iter() .cloned() .collect::>(); - agrs.iter() - .filter(|agr| !all_keys.contains(&agr.inner.public_key)) - // TODO: A node that tells us the IP Address of the node that isn't in our database should be malefactor banned - .filter(|agr| match &agr.node_addr_opt { - None => true, - Some(node_addr) => { - let socket_addrs: Vec = node_addr.clone().into(); - socket_addrs.contains(&gossip_source) - } - }) - .for_each(|agr| { - let mut node_record = NodeRecord::from(agr); - match user_exit_preferences_opt { - Some(user_exit_preferences) => { - user_exit_preferences.assign_nodes_country_undesirability(&mut node_record) - } - None => (), + let mut new_nodes = vec![]; + let mut obsolete_nodes = vec![]; + for agr in agrs { + if !all_keys.contains(&agr.inner.public_key) { + new_nodes.push(agr); + } else if let Some(existing_node) = database.node_by_key(&agr.inner.public_key) { + if agr.inner.version > existing_node.version() { + obsolete_nodes.push(agr); } - trace!( - self.logger, - "Discovered new Node {:?}: {:?}", - node_record.public_key(), - node_record.full_neighbor_keys(database) - ); - database - .add_node(node_record) - .expect("List of new Nodes contained existing Nodes"); - }); - database.keys().len() != all_keys.len() + } + } + (new_nodes, obsolete_nodes) } - fn identify_and_update_obsolete_nodes( + fn add_new_nodes( &self, database: &mut NeighborhoodDatabase, agrs: Vec, - ) -> bool { - agrs.into_iter().fold(false, |b, agr| { - match database.node_by_key(&agr.inner.public_key) { - Some(existing_node) if agr.inner.version > existing_node.version() => { - trace!( - self.logger, - "Updating Node {:?} from v{} to v{}", - existing_node.public_key(), - existing_node.version(), - agr.inner.version - ); - self.update_database_record(database, agr) || b + user_exit_preferences_opt: Option<&UserExitPreferences>, + ) { + agrs.into_iter().for_each(|agr| { + let mut node_record = NodeRecord::from(agr); + match user_exit_preferences_opt { + Some(user_exit_preferences) => { + user_exit_preferences.assign_nodes_country_undesirability(&mut node_record) } - _ => b, + None => (), } + trace!( + self.logger, + "Discovered new Node {:?}: {:?}", + node_record.public_key(), + node_record.full_neighbor_keys(database) + ); + database + .add_node(node_record) + .expect("List of new Nodes contained existing Nodes"); + }); + } + + fn update_obsolete_nodes( + &self, + database: &mut NeighborhoodDatabase, + agrs: Vec, + ) { + agrs.into_iter().for_each(|agr| { + let existing_node = database + .node_by_key(&agr.inner.public_key) + .expect("Node magically disappeared from neighborhood database"); + trace!( + self.logger, + "Updating Node {:?} from v{} to v{}", + existing_node.public_key(), + existing_node.version(), + agr.inner.version + ); + self.update_database_record(database, agr); }) } @@ -1444,7 +1585,9 @@ impl GossipAcceptor for GossipAcceptorReal { Qualification::Unmatched => { panic!("Nothing in gossip_handlers returned Matched or Malformed") } - Qualification::Malformed(reason) => vec![GossipAcceptanceResult::Ban(reason)], + Qualification::Malformed(reason) => vec![GossipAcceptanceResult::Ban(Malefactor::new( + None, None, None, None, reason, + ))], } } } @@ -1452,9 +1595,10 @@ impl GossipAcceptor for GossipAcceptorReal { impl GossipAcceptorReal { pub fn new( cryptde: Box, - persistent_config: &dyn PersistentConfiguration + persistent_config: &dyn PersistentConfiguration, ) -> GossipAcceptorReal { - let rate_pack_limits = &persistent_config.rate_pack_limits() + let rate_pack_limits = &persistent_config + .rate_pack_limits() .expect("RatePackLimits should be set"); let logger = Logger::new("GossipAcceptor"); @@ -1463,7 +1607,10 @@ impl GossipAcceptorReal { Box::new(DebutHandler::new(rate_pack_limits, logger.clone())), Box::new(PassHandler::new()), Box::new(IntroductionHandler::new(rate_pack_limits, logger.clone())), - Box::new(StandardGossipHandler::new(&RatePackLimits::test_default(), logger.clone())), + Box::new(StandardGossipHandler::new( + &RatePackLimits::test_default(), + logger.clone(), + )), Box::new(RejectHandler::new()), ], cryptde, @@ -1559,13 +1706,16 @@ mod tests { UNREACHABLE_COUNTRY_PENALTY, }; use crate::sub_lib::cryptde_null::CryptDENull; - use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, RatePack}; + use crate::sub_lib::neighborhood::{ + ConnectionProgressEvent, ConnectionProgressMessage, RatePack, + }; 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, make_meaningless_db, make_node_record, make_node_record_cc, make_node_record_f, make_node_records, public_keys_from_node_records, DB_PATCH_SIZE_FOR_TEST, }; + use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use crate::test_utils::unshared_test_utils::make_cpm_recipient; use crate::test_utils::{assert_contains, vec_to_set}; use actix::System; @@ -1580,7 +1730,6 @@ mod tests { use std::ops::{Add, Sub}; use std::str::FromStr; use std::time::Duration; - use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; lazy_static! { static ref GA_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); @@ -1887,11 +2036,8 @@ mod tests { .try_into() .unwrap(); let subject = DebutHandler::new( - &RatePackLimits::new( - RatePack::new(0, 0, 0, 0), - RatePack::new(0, 0, 0, 0) - ), - Logger::new(test_name) + &RatePackLimits::new(RatePack::new(0, 0, 0, 0), RatePack::new(0, 0, 0, 0)), + Logger::new(test_name), ); let result = subject.handle( @@ -1903,8 +2049,18 @@ mod tests { ); let message = r#"Debut from AQIDBA at 1.2.3.4:1234 rejected due to rate pack limit violation: ConfiguratorError { param_errors: [ParamError { parameter: "rate-pack", reason: "Value of routing_byte_rate (1235) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of routing_service_rate (1434) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of exit_byte_rate (1237) is above the maximum allowed (0)" }, ParamError { parameter: "rate-pack", reason: "Value of exit_service_rate (1634) is above the maximum allowed (0)" }] }"#.to_string(); - assert_eq!(result, vec![GossipAcceptanceResult::Ban(message.clone())]); - TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); + assert_eq!( + result, + vec![GossipAcceptanceResult::Ban(Malefactor::new( + Some(root_node.public_key().clone()), + Some(root_node.node_addr_opt().unwrap().ip_addr()), + Some(root_node.earning_wallet()), + None, + message.clone() + ))] + ); + TestLogHandler::new() + .exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); } #[test] @@ -2013,10 +2169,8 @@ mod tests { #[test] fn gossip_containing_other_than_two_records_is_not_an_introduction() { let (gossip, _, gossip_source) = make_debut(2345, Mode::Standard); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&make_meaningless_db(), agrs_vec.as_slice(), gossip_source); @@ -2031,10 +2185,8 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); dest_db.add_node(not_introducee.clone()).unwrap(); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2048,10 +2200,8 @@ mod tests { let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); gossip.node_records[0].node_addr_opt = None; - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2066,10 +2216,8 @@ mod tests { let dest_db = db_from_node(&dest_root); gossip.node_records[0].node_addr_opt = Some(NodeAddr::new(&IpAddr::from_str("2.3.4.5").unwrap(), &[])); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2086,10 +2234,8 @@ mod tests { let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); gossip.node_records[1].node_addr_opt = None; - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2104,10 +2250,8 @@ mod tests { let dest_db = db_from_node(&dest_root); gossip.node_records[1].node_addr_opt = Some(NodeAddr::new(&IpAddr::from_str("3.4.5.6").unwrap(), &[])); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2130,10 +2274,8 @@ mod tests { &IpAddr::from_str("4.5.6.7").unwrap(), &[4567], )); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2150,10 +2292,8 @@ mod tests { &IpAddr::from_str("2.3.4.5").unwrap(), &[2345], )); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs_vec: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, agrs_vec.as_slice(), gossip_source); @@ -2171,10 +2311,8 @@ mod tests { let (gossip, gossip_source) = make_introduction(2345, 3456); let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[0].inner.public_key = dest_root.public_key().clone(); let introducer_key = &agrs[0].inner.public_key; @@ -2195,10 +2333,8 @@ mod tests { let (gossip, _) = make_introduction(2345, 3456); let dest_root = make_node_record(7878, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[0].node_addr_opt = dest_root.node_addr_opt(); let introducer_key = &agrs[0].inner.public_key; @@ -2220,10 +2356,8 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); let introducer_key = &agrs[0].inner.public_key; dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); @@ -2252,14 +2386,15 @@ mod tests { let after = time_t_timestamp(); assert_eq!(qualifies_result, Qualification::Matched); - assert_eq!(&handle_result[0], - &GossipAcceptanceResult::Ban(format!("Introducer {} tried changing immutable characteristic: Updating a NodeRecord must not change its node_addr_opt: 4.5.6.7:4567 -> 2.3.4.5:2345", introducer_key)) - ); - match &handle_result[1] { - GossipAcceptanceResult::Reply(_, _, _) => (), - other => panic!("Expected Reply, got {:?}", other), - }; - assert_eq!(handle_result.len(), 2); + // Again: we don't trust the introducer, so we don't accept the introducee this time. + // But we don't ban him, in case he's introduced later by a good guy. + assert_eq!(handle_result, vec![GossipAcceptanceResult::Ban(Malefactor::new( + Some(agrs[0].inner.public_key.clone()), + Some(agrs[0].node_addr_opt.as_ref().unwrap().ip_addr()), + Some(agrs[0].inner.earning_wallet.clone()), + None, + format!("Introducer {} tried changing immutable characteristic: Updating a NodeRecord must not change its node_addr_opt: 4.5.6.7:4567 -> 2.3.4.5:2345", introducer_key) + ))]); assert_node_records_eq( dest_db.node_by_key_mut(introducer_key).unwrap(), &introducer_before_gossip, @@ -2273,10 +2408,8 @@ mod tests { let (gossip, gossip_source) = make_introduction(2345, 3456); let dest_root = make_node_record_f(7878, false, false, true); let dest_db = db_from_node(&dest_root); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); let result = subject.qualifies(&dest_db, &agrs, gossip_source); @@ -2290,10 +2423,8 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); let mut expected_introducer = NodeRecord::from(&agrs[0]); expected_introducer.metadata.country_undesirability = COUNTRY_UNDESIRABILITY_FACTOR; @@ -2324,11 +2455,14 @@ mod tests { .build(); assert_eq!( handle_result, - vec![GossipAcceptanceResult::Reply( - debut, - agrs[1].inner.public_key.clone(), - agrs[1].node_addr_opt.clone().unwrap(), - )], + vec![ + GossipAcceptanceResult::Accepted, + GossipAcceptanceResult::Reply( + debut, + agrs[1].inner.public_key.clone(), + agrs[1].node_addr_opt.clone().unwrap(), + ) + ], ); let result_introducer: &NodeRecord = dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); @@ -2353,10 +2487,8 @@ mod tests { dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), &key); } let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); let handle_result = subject.handle( @@ -2377,10 +2509,8 @@ mod tests { let dest_root = make_node_record(7878, true); let mut dest_db = db_from_node(&dest_root); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); dest_db @@ -2404,11 +2534,14 @@ mod tests { .build(); assert_eq!( handle_result, - vec![GossipAcceptanceResult::Reply( - debut, - agrs[1].inner.public_key.clone(), - agrs[1].node_addr_opt.clone().unwrap(), - )], + vec![ + GossipAcceptanceResult::Accepted, + GossipAcceptanceResult::Reply( + debut, + agrs[1].inner.public_key.clone(), + agrs[1].node_addr_opt.clone().unwrap(), + ) + ], ); let result_introducer: &NodeRecord = dest_db.node_by_key(&agrs[0].inner.public_key).unwrap(); @@ -2440,10 +2573,8 @@ mod tests { dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), half_neighbor_key); } let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let agrs: Vec = gossip.try_into().unwrap(); dest_db.add_node(NodeRecord::from(&agrs[0])).unwrap(); dest_db.add_arbitrary_half_neighbor(dest_root.public_key(), &agrs[0].inner.public_key); @@ -2504,9 +2635,9 @@ mod tests { let subject = IntroductionHandler::new( &RatePackLimits::new( RatePack::new(100, 100, 100, 100), - RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ), - Logger::new(test_name) + Logger::new(test_name), ); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[0].inner.rate_pack = RatePack::new(0, 0, 0, 0); // Invalid rate pack @@ -2522,13 +2653,21 @@ mod tests { ); let message = format!("Introducer {} from {} rejected due to rate pack limit violation: ConfiguratorError {{ param_errors: [ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of routing_service_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_byte_rate (0) is below the minimum allowed (100)\" }}, ParamError {{ parameter: \"rate-pack\", reason: \"Value of exit_service_rate (0) is below the minimum allowed (100)\" }}] }}", agrs[0].inner.public_key, gossip_source); - assert_eq!(handle_result[0], GossipAcceptanceResult::Ban(message.clone())); - match &handle_result[1] { - &GossipAcceptanceResult::Reply(_, _, _) => (), - other => panic!("Expected Reply as second result, but got {:?}", other), - } - assert_eq!(handle_result.len(), 2); - TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); + // If we decide the introducer is a malefactor, we won't accept the introducee from him. + // However, the introducee may be perfectly innocent; so we don't want to ban the + // introducee in case he's introduced later by somebody we do trust. + assert_eq!( + handle_result, + vec![GossipAcceptanceResult::Ban(Malefactor::new( + Some(agrs[0].inner.public_key.clone()), + Some(agrs[0].node_addr_opt.as_ref().unwrap().ip_addr()), + Some(agrs[0].inner.earning_wallet.clone()), + None, + message.clone() + ))] + ); + TestLogHandler::new() + .exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); } #[test] @@ -2549,9 +2688,9 @@ mod tests { let subject = IntroductionHandler::new( &RatePackLimits::new( RatePack::new(100, 100, 100, 100), - RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX) + RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ), - Logger::new(test_name) + Logger::new(test_name), ); let mut agrs: Vec = gossip.try_into().unwrap(); agrs[1].inner.rate_pack = RatePack::new(0, 0, 0, 0); // Invalid rate pack @@ -2573,9 +2712,16 @@ mod tests { ); assert_eq!( handle_result, - vec![GossipAcceptanceResult::Accepted], + vec![GossipAcceptanceResult::Ban(Malefactor::new( + Some(agrs[1].inner.public_key.clone()), + Some(agrs[1].node_addr_opt.as_ref().unwrap().ip_addr()), + Some(agrs[1].inner.earning_wallet.clone()), + None, + message.clone() + ))], ); - TestLogHandler::new().exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); + TestLogHandler::new() + .exists_log_containing(format!("WARN: {}: {}", test_name, message).as_str()); } #[test] @@ -2636,7 +2782,8 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), false) .build(); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2662,7 +2809,8 @@ mod tests { .node(node_a.public_key(), false) .node(dest_node.public_key(), false) .build(); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2671,7 +2819,7 @@ mod tests { assert_eq!( result, Qualification::Malformed( - "Standard Gossip from 1.2.3.4:1234 contains a record with this Node's public key" + "Standard Gossip from 1.2.3.4:1234 contains a record claiming this Node's public key" .to_string() ), ); @@ -2697,7 +2845,8 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), true) .build(); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2706,7 +2855,7 @@ mod tests { assert_eq!( result, Qualification::Malformed(format!( - "Standard Gossip from 1.2.3.4:1234 contains a record claiming that {} has this Node's IP address", + "Standard Gossip from 1.2.3.4:1234 contains a record claiming that {} resides at this Node's IP address", node_b.public_key() )), ); @@ -2735,7 +2884,8 @@ mod tests { &node_a.node_addr_opt().unwrap().ip_addr(), &[4567], )); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let gossip_source: SocketAddr = src_node.node_addr_opt().unwrap().into(); let gossip_vec: Vec = gossip.try_into().unwrap(); @@ -2794,7 +2944,8 @@ mod tests { .node(node_a.public_key(), false) .node(node_b.public_key(), false) .build(); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); @@ -2866,7 +3017,8 @@ mod tests { This test proves that E is excluded, because the distance of A and E is more than 3 hops. */ - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -2915,7 +3067,8 @@ mod tests { 2) To find neighbors of neighbors, we'll look into the AGRs. (For Example, B---Y, B---C, and C---D). */ - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -2968,7 +3121,8 @@ mod tests { init_test_logging(); let test_name = "standard_gossip_handler_can_handle_node_for_which_agr_is_not_found_while_computing_patch"; - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -3024,7 +3178,8 @@ mod tests { */ let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let node_a = make_node_record(1111, true); let node_b = make_node_record(2222, true); let node_c = make_node_record(3333, false); @@ -3073,7 +3228,10 @@ mod tests { } fn assert_compute_patch(db_patch_size: u8) { - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("assert_compute_patch")); + let subject = StandardGossipHandler::new( + &RatePackLimits::test_default(), + Logger::new("assert_compute_patch"), + ); // one node to finish hops and another node that's outside the patch let nodes_count = db_patch_size as usize + 2; let nodes = make_node_records(nodes_count as u16); @@ -3119,7 +3277,8 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3159,7 +3318,8 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3206,7 +3366,8 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3247,7 +3408,8 @@ mod tests { let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); neighborhood_metadata.cpm_recipient = cpm_recipient; - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let system = System::new("test"); let result = subject.handle( @@ -3325,7 +3487,7 @@ mod tests { let mut src_db = db_from_node(&src_root); let node_a = make_node_record(5678, true); let mut node_b = make_node_record(7777u16, true); - node_b.inner.rate_pack = RatePack::new (0, 0, 0, 0); // invalid + node_b.inner.rate_pack = RatePack::new(0, 0, 0, 0); // invalid node_b.resign(); let mut dest_db = db_from_node(&dest_root); dest_db.add_node(src_root.clone()).unwrap(); @@ -3367,18 +3529,30 @@ mod tests { neighborhood_metadata, ); - let analysis = format!("{:?}", rate_pack_limits.analyze(&node_b.inner.rate_pack).err().unwrap()); + let analysis = format!( + "{:?}", + rate_pack_limits + .analyze(&node_b.inner.rate_pack) + .err() + .unwrap() + ); let message = format!( "Node {} from Standard gossip received from {:?} rejected due to rate pack limit violation: {}", node_b.public_key(), - gossip_source, + gossip_source.ip(), analysis ); assert_eq!( handle_result, vec![ GossipAcceptanceResult::Accepted, - // GossipAcceptanceResult::Ban(message.clone()) + GossipAcceptanceResult::Ban(Malefactor::new( + Some(node_b.public_key().clone()), + None, + Some(node_b.earning_wallet().clone()), + None, + message.clone() + )), ] ); assert_eq!(dest_db.node_by_key(node_a.public_key()).is_some(), true); @@ -3650,7 +3824,7 @@ mod tests { debut_node_addr.clone(), )], ], - &result + &result, ); root_node .add_half_neighbor_key(debut_node.public_key().clone()) @@ -3952,7 +4126,10 @@ mod tests { let debut_gossip = Gossip_0v1 { node_records: vec![gnr], }; - let expected = vec![make_expected_non_introduction_debut_response(&src_node, debut_gossip)]; + let expected = vec![make_expected_non_introduction_debut_response( + &src_node, + debut_gossip, + )]; assert_eq!(result, expected); assert_eq!( dest_db @@ -4001,7 +4178,10 @@ mod tests { let debut_gossip = Gossip_0v1 { node_records: vec![gnr], }; - let expected = vec![make_expected_non_introduction_debut_response(&src_node, debut_gossip)]; + let expected = vec![make_expected_non_introduction_debut_response( + &src_node, + debut_gossip, + )]; assert_eq!(result, expected); assert_eq!( dest_db @@ -4028,10 +4208,8 @@ mod tests { let cryptde = GA_CRYPTDE_PAIR.main.as_ref(); let root_node = make_node_record(1234, true); let mut db = db_from_node(&root_node); - let subject = IntroductionHandler::new( - &RatePackLimits::test_default(), - Logger::new("test") - ); + let subject = + IntroductionHandler::new(&RatePackLimits::test_default(), Logger::new("test")); let (gossip, gossip_source) = make_introduction(0, 1); let (cpm_recipient, recording_arc) = make_cpm_recipient(); let mut neighborhood_metadata = make_default_neighborhood_metadata(); @@ -4282,7 +4460,7 @@ mod tests { } #[test] - fn standard_gossip_containing_unfamiliar_node_addrs_leads_to_them_being_ignored() { + fn standard_gossip_containing_unfamiliar_node_addrs_leads_to_them_being_banned() { /* <---- Databases before the gossip ----> @@ -4364,7 +4542,17 @@ mod tests { ); let after = time_t_timestamp(); - assert_eq!(result, vec![GossipAcceptanceResult::Accepted]); + assert_eq!(result, vec![ + GossipAcceptanceResult::Accepted, + GossipAcceptanceResult::Ban(Malefactor::from(( + &node_a, + "Node AgMEBQ at 2.3.4.5 sent Standard gossip that contained an IP address for victim Node BgcICQ that we should not have known".to_string()) + )), + GossipAcceptanceResult::Ban(Malefactor::from(( + &node_a, + "Node AgMEBQ at 2.3.4.5 sent Standard gossip that contained an IP address for victim Node BwgJAA that we should not have known".to_string()) + )), + ]); let mut expected_dest_db = src_db.clone(); expected_dest_db.remove_arbitrary_half_neighbor(node_e.public_key(), node_a.public_key()); expected_dest_db.remove_arbitrary_half_neighbor(node_f.public_key(), node_a.public_key()); @@ -4468,9 +4656,9 @@ mod tests { let mut dest_db = db_from_node(&dest_node); let src_node = make_node_record(2345, true); let mut src_db = db_from_node(&src_node); - let third_node = make_node_record(3456, true); - let disconnected_node = make_node_record(4567, true); // Why does this have an Ip Address? - // These are only half neighbors. Will they be ignored in degree calculation? + let third_node = make_node_record(3456, false); + let disconnected_node = make_node_record(4567, false); + // These are only half neighbors. Will they be ignored in degree calculation? for idx in 0..MAX_DEGREE { let failed_node_key = &dest_db .add_node(make_node_record(4000 + idx as u16, true)) @@ -4492,6 +4680,7 @@ mod tests { .node_by_key_mut(third_node.public_key()) .unwrap() .increment_version(); + // Why are we resigning dest_node? resign_nodes(&mut src_db, vec![&src_node, &dest_node, &third_node]); let gossip = GossipBuilder::new(&src_db) .node(src_node.public_key(), true) @@ -4510,6 +4699,7 @@ mod tests { let after = time_t_timestamp(); let mut expected_dest_db = src_db.clone(); + // why half neighborship? expected_dest_db.add_arbitrary_half_neighbor(dest_node.public_key(), src_node.public_key()); expected_dest_db .remove_neighbor(disconnected_node.public_key()) @@ -4954,7 +5144,7 @@ mod tests { GossipAcceptorReal::new( crypt_de.dup(), &PersistentConfigurationMock::new() - .rate_pack_limits_result(Ok(RatePackLimits::test_default())) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), ) } diff --git a/node/src/neighborhood/malefactor.rs b/node/src/neighborhood/malefactor.rs new file mode 100644 index 0000000000..79f791b965 --- /dev/null +++ b/node/src/neighborhood/malefactor.rs @@ -0,0 +1,448 @@ +use crate::neighborhood::gossip::AccessibleGossipRecord; +use crate::neighborhood::node_record::NodeRecord; +use crate::sub_lib::cryptde::PublicKey; +use crate::sub_lib::wallet::Wallet; +use lazy_static::lazy_static; +use std::fmt::{Display, Formatter}; +use std::net::IpAddr; +use time::{OffsetDateTime, PrimitiveDateTime}; + +lazy_static! { + pub static ref FUDGE_FACTOR: time::Duration = time::Duration::seconds(1); +} + +#[derive(Clone, Debug, Eq)] +pub struct Malefactor { + pub public_key_opt: Option, + pub ip_address_opt: Option, + pub earning_wallet_opt: Option, + pub consuming_wallet_opt: Option, + pub timestamp: PrimitiveDateTime, + pub reason: String, +} + +impl Display for Malefactor { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Malefactor{}{}{} detected at {}: {}", + match &self.public_key_opt { + Some(pk) => format!(" {}", pk), + None => "".to_string(), + }, + match &self.ip_address_opt { + Some(ip) => format!(" at {}", ip), + None => "".to_string(), + }, + match &self.earning_wallet_opt { + Some(wallet) => format!(" with earning wallet {}", wallet), + None => "".to_string(), + } + &match ( + &self.earning_wallet_opt.is_some(), + &self.consuming_wallet_opt + ) { + (true, Some(wallet)) => format!(", consuming wallet {}", wallet), + (false, Some(wallet)) => format!(" with consuming wallet {}", wallet), + (_, None) => "".to_string(), + }, + self.timestamp, + self.reason + ) + } +} + +impl PartialEq for Malefactor { + // Logic behind this custom implementation: + // The only place we will ever compare Malefactors for equality is in tests. In tests, + // the Malefactor that is generated in the production code and the Malefactor that is used + // for assertion will frequently be created a few microseconds apart, and therefore their + // timestamps will not be identical and the equality assertion will fail, even when the two + // are logically the same. Therefore, this custom implementation will consider two Malefactors + // equal if their timestamps are within FUDGE_FACTOR of each other and all their other fields are + // equal. + fn eq(&self, other: &Self) -> bool { + let equal = self.public_key_opt == other.public_key_opt + && self.ip_address_opt == other.ip_address_opt + && self.earning_wallet_opt == other.earning_wallet_opt + && self.consuming_wallet_opt == other.consuming_wallet_opt + && self.reason == other.reason; + let plus_one_second = self.timestamp.saturating_add(FUDGE_FACTOR.clone()); + let minus_one_second = self.timestamp.saturating_sub(FUDGE_FACTOR.clone()); + equal && (other.timestamp >= minus_one_second && other.timestamp <= plus_one_second) + } +} + +impl From<(&NodeRecord, String)> for Malefactor { + fn from(pair: (&NodeRecord, String)) -> Self { + let (node_record, reason) = pair; + Self::new( + Some(node_record.public_key().clone()), + node_record + .metadata + .node_addr_opt + .as_ref() + .map(|na| na.ip_addr()), + Some(node_record.inner.earning_wallet.clone()), + None, + reason, + ) + } +} + +impl From<(&AccessibleGossipRecord, String)> for Malefactor { + fn from(pair: (&AccessibleGossipRecord, String)) -> Self { + let (agr, reason) = pair; + Self::new( + Some(agr.inner.public_key.clone()), + agr.node_addr_opt.as_ref().map(|na| na.ip_addr()), + Some(agr.inner.earning_wallet.clone()), + None, + reason, + ) + } +} + +impl Malefactor { + pub fn new( + public_key_opt: Option, + ip_address_opt: Option, + earning_wallet_opt: Option, + consuming_wallet_opt: Option, + reason: String, + ) -> Self { + if public_key_opt.is_none() + && ip_address_opt.is_none() + && earning_wallet_opt.is_none() + && consuming_wallet_opt.is_none() + { + panic!("Malefactor must have at least one identifying attribute"); + } + Self { + public_key_opt, + ip_address_opt, + earning_wallet_opt, + consuming_wallet_opt, + timestamp: Self::timestamp(), + reason, + } + } + + fn timestamp() -> PrimitiveDateTime { + let odt = OffsetDateTime::now_local().unwrap_or_else(|_| OffsetDateTime::now_utc()); + PrimitiveDateTime::new(odt.date(), odt.time()) + } +} + +mod tests { + use super::*; + use std::str::FromStr; + + #[should_panic(expected = "Malefactor must have at least one identifying attribute")] + #[test] + fn doesnt_tolerate_all_nones() { + let _ = Malefactor::new(None, None, None, None, "Bad Smell".to_string()); + } + + #[test] + fn timestamps_properly() { + let before = Malefactor::timestamp(); + + let malefactor = Malefactor::new( + Some(PublicKey::from(&b"Booga"[..])), + None, + None, + None, + "Bad Smell".to_string(), + ); + + let after = Malefactor::timestamp(); + assert!(malefactor.timestamp >= before); + assert!(malefactor.timestamp <= after); + } + + #[test] + fn displays_public_key() { + let public_key = PublicKey::from(&b"Booga"[..]); + let malefactor = Malefactor { + public_key_opt: Some(public_key), + ip_address_opt: None, + earning_wallet_opt: None, + consuming_wallet_opt: None, + timestamp: PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ), + reason: "Bad Smell".to_string(), + }; + + let string = format!("{}", malefactor); + + assert_eq!( + string, + "Malefactor Qm9vZ2E detected at 2024-06-01 12:34:56.0: Bad Smell".to_string() + ); + } + + #[test] + fn displays_ip_address() { + let ip_address = IpAddr::from_str("12.34.56.78").unwrap(); + let malefactor = Malefactor { + public_key_opt: None, + ip_address_opt: Some(ip_address), + earning_wallet_opt: None, + consuming_wallet_opt: None, + timestamp: PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ), + reason: "Bad Smell".to_string(), + }; + + let string = format!("{}", malefactor); + + assert_eq!( + string, + "Malefactor at 12.34.56.78 detected at 2024-06-01 12:34:56.0: Bad Smell".to_string() + ); + } + + #[test] + fn displays_earning_wallet() { + let earning_wallet = + Wallet::from_str("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let malefactor = Malefactor { + public_key_opt: None, + ip_address_opt: None, + earning_wallet_opt: Some(earning_wallet), + consuming_wallet_opt: None, + timestamp: PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ), + reason: "Bad Smell".to_string(), + }; + + let string = format!("{}", malefactor); + + assert_eq!( + string, + "Malefactor with earning wallet 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee detected at 2024-06-01 12:34:56.0: Bad Smell".to_string() + ); + } + + #[test] + fn displays_consuming_wallet() { + let consuming_wallet = + Wallet::from_str("0xcccccccccccccccccccccccccccccccccccccccc").unwrap(); + let malefactor = Malefactor { + public_key_opt: None, + ip_address_opt: None, + earning_wallet_opt: None, + consuming_wallet_opt: Some(consuming_wallet), + timestamp: PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ), + reason: "Bad Smell".to_string(), + }; + + let string = format!("{}", malefactor); + + assert_eq!( + string, + "Malefactor with consuming wallet 0xcccccccccccccccccccccccccccccccccccccccc detected at 2024-06-01 12:34:56.0: Bad Smell".to_string() + ); + } + + #[test] + fn displays_all_fields() { + let public_key = PublicKey::from(&b"Booga"[..]); + let ip_address = IpAddr::from_str("12.34.56.78").unwrap(); + let earning_wallet = + Wallet::from_str("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let consuming_wallet = + Wallet::from_str("0xcccccccccccccccccccccccccccccccccccccccc").unwrap(); + let malefactor = Malefactor { + public_key_opt: Some(public_key), + ip_address_opt: Some(ip_address), + earning_wallet_opt: Some(earning_wallet), + consuming_wallet_opt: Some(consuming_wallet), + timestamp: PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ), + reason: "Bad Smell".to_string(), + }; + + let string = format!("{}", malefactor); + + assert_eq!( + string, + "Malefactor Qm9vZ2E at 12.34.56.78 with earning wallet 0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee, consuming wallet 0xcccccccccccccccccccccccccccccccccccccccc detected at 2024-06-01 12:34:56.0: Bad Smell".to_string() + ); + } + + #[test] + fn eq_works_for_equal_timestamps() { + let public_key = PublicKey::from(&b"Booga"[..]); + let ip_address = IpAddr::from_str("12.34.56.78").unwrap(); + let earning_wallet = + Wallet::from_str("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let consuming_wallet = + Wallet::from_str("0xcccccccccccccccccccccccccccccccccccccccc").unwrap(); + let timestamp = PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ); + let reason = "Bad Smell".to_string(); + let a = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: reason.clone(), + }; + let b = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: reason.clone(), + }; + let c = Malefactor { + public_key_opt: None, + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: reason.clone(), + }; + let d = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: None, + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: reason.clone(), + }; + let e = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: None, + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: reason.clone(), + }; + let f = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: None, + timestamp, + reason: reason.clone(), + }; + let g = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp, + reason: "".to_string(), + }; + + assert_eq!(a, a); + assert_eq!(a, b); + assert_ne!(a, c); + assert_ne!(a, d); + assert_ne!(a, e); + assert_ne!(a, f); + assert_ne!(a, g); + } + + #[test] + fn eq_says_true_for_timestamps_exactly_one_second_apart() { + let public_key = PublicKey::from(&b"Booga"[..]); + let ip_address = IpAddr::from_str("12.34.56.78").unwrap(); + let earning_wallet = + Wallet::from_str("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let consuming_wallet = + Wallet::from_str("0xcccccccccccccccccccccccccccccccccccccccc").unwrap(); + let timestamp_early = PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ); + let timestamp_late = timestamp_early.saturating_add(FUDGE_FACTOR.clone()); + let reason = "Bad Smell".to_string(); + let a = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp_early, + reason: reason.clone(), + }; + let b = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp_late, + reason: reason.clone(), + }; + + assert_eq!(a, b); + assert_eq!(b, a); + } + + #[test] + fn eq_says_false_for_timestamps_more_than_one_second_apart() { + let public_key = PublicKey::from(&b"Booga"[..]); + let ip_address = IpAddr::from_str("12.34.56.78").unwrap(); + let earning_wallet = + Wallet::from_str("0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee").unwrap(); + let consuming_wallet = + Wallet::from_str("0xcccccccccccccccccccccccccccccccccccccccc").unwrap(); + let timestamp_early = PrimitiveDateTime::new( + time::Date::from_calendar_date(2024, time::Month::June, 1).unwrap(), + time::Time::from_hms(12, 34, 56).unwrap(), + ); + let timestamp_middle = timestamp_early + .saturating_add(FUDGE_FACTOR.saturating_add(time::Duration::milliseconds(1))); + let timestamp_late = timestamp_middle + .saturating_add(FUDGE_FACTOR.saturating_add(time::Duration::milliseconds(1))); + let reason = "Bad Smell".to_string(); + let a = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp_early, + reason: reason.clone(), + }; + let b = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp_middle, + reason: reason.clone(), + }; + let c = Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address.clone()), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp_late, + reason: reason.clone(), + }; + + assert_ne!(a, b); + assert_ne!(b, a); + assert_ne!(b, c); + assert_ne!(c, b); + assert_ne!(a, c); + assert_ne!(c, a); + } +} diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 3c2784a11b..1a2f47d31f 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -4,6 +4,7 @@ pub mod dot_graph; pub mod gossip; pub mod gossip_acceptor; pub mod gossip_producer; +mod malefactor; pub mod neighborhood_database; pub mod node_location; pub mod node_record; @@ -12,7 +13,10 @@ pub mod overall_connection_status; use crate::bootstrapper::{BootstrapperConfig, CryptDEPair}; use crate::database::db_initializer::DbInitializationConfig; use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; -use crate::db_config::persistent_configuration::{PersistentConfigError, PersistentConfiguration, PersistentConfigurationFactory, PersistentConfigurationFactoryReal, PersistentConfigurationInvalid}; +use crate::db_config::persistent_configuration::{ + PersistentConfigError, PersistentConfiguration, PersistentConfigurationFactory, + PersistentConfigurationFactoryReal, PersistentConfigurationInvalid, +}; use crate::neighborhood::gossip::{AccessibleGossipRecord, DotGossipEndpoint, Gossip_0v1}; use crate::neighborhood::gossip_acceptor::{GossipAcceptanceResult, GossipAcceptorInvalid}; use crate::neighborhood::node_location::get_node_location; @@ -432,7 +436,9 @@ impl Neighborhood { chain: config.blockchain_bridge_config.chain, crashable: config.crash_point == CrashPoint::Message, data_directory: config.data_directory.clone(), - persistent_config_factory: Box::new(PersistentConfigurationFactoryReal::new(config.data_directory.clone())), + persistent_config_factory: Box::new(PersistentConfigurationFactoryReal::new( + config.data_directory.clone(), + )), persistent_config: Box::new(PersistentConfigurationInvalid::new()), db_password_opt: config.db_password_opt.clone(), logger: Logger::new("Neighborhood"), @@ -567,7 +573,8 @@ impl Neighborhood { } fn validate_or_replace_min_hops_value(&mut self) { - let value_in_db = self.persistent_config + let value_in_db = self + .persistent_config .min_hops() .expect("Min Hops value is not initialized inside Database"); let value_in_neighborhood = self.min_hops; @@ -777,8 +784,7 @@ impl Neighborhood { if acceptance_results.is_empty() { trace!(self.logger, "Gossip from {} ignored", gossip_source); self.handle_gossip_ignored(&ignored_node_name, gossip_record_count) - } - else { + } else { acceptance_results.into_iter().for_each(|acceptance_result| { match acceptance_result { GossipAcceptanceResult::Accepted => { @@ -829,7 +835,9 @@ impl Neighborhood { self.logger, "Saving neighbor list: {:?}", node_descriptors_opt ); - match self.persistent_config.set_past_neighbors(node_descriptors_opt, db_password) + match self + .persistent_config + .set_past_neighbors(node_descriptors_opt, db_password) { Ok(_) => info!(self.logger, "Persisted neighbor changes for next run"), Err(PersistentConfigError::DatabaseError(msg)) @@ -2188,6 +2196,7 @@ mod tests { use std::thread; use std::time::Duration; use std::time::Instant; + use time::{Date, Month, PrimitiveDateTime, Time}; use tokio::prelude::Future; use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; @@ -2210,7 +2219,10 @@ mod tests { use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, NeighborhoodMode, RatePackLimits, WalletPair}; + use crate::sub_lib::neighborhood::{ + AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, + NeighborhoodMode, RatePackLimits, WalletPair, + }; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; use crate::sub_lib::peer_actors::PeerActors; @@ -2242,15 +2254,16 @@ mod tests { use super::*; use crate::accountant::test_utils::bc_from_earning_wallet; use crate::bootstrapper::CryptDEPair; + use crate::neighborhood::malefactor::Malefactor; use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, }; use crate::neighborhood::overall_connection_status::{ ConnectionProgress, ConnectionStage, OverallConnectionStage, }; + use crate::test_utils::database_utils::PersistentConfigurationFactoryTest; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use crate::test_utils::database_utils::PersistentConfigurationFactoryTest; lazy_static! { static ref N_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); @@ -2411,10 +2424,9 @@ mod tests { let debut_node = make_global_cryptde_node_record(1111, true, &N_CRYPTDE_PAIR); let mut debut_subject = neighborhood_from_nodes(&debut_node, None, &N_CRYPTDE_PAIR); debut_subject.min_hops = Hops::OneHop; - let persistent_config = - PersistentConfigurationMock::new() - .set_past_neighbors_result(Ok(())) - .rate_pack_limits_result(Ok(RatePackLimits::test_default())); + let persistent_config = PersistentConfigurationMock::new() + .set_past_neighbors_result(Ok(())) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())); debut_subject.persistent_config = Box::new(persistent_config); debut_subject.gossip_acceptor = Box::new(GossipAcceptorReal::new( N_CRYPTDE_PAIR.main.dup(), @@ -2585,13 +2597,11 @@ mod tests { "node_with_zero_hop_config_ignores_start_message", ), ); - subject.persistent_config_factory = Box::new( - PersistentConfigurationFactoryTest::new( - PersistentConfigurationMock::new() - .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) - .rate_pack_limits_result(Ok(RatePackLimits::test_default())) - ) - ); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + )); subject.data_directory = data_dir; let addr = subject.start(); let sub = addr.clone().recipient::(); @@ -6113,8 +6123,8 @@ mod tests { #[test] fn neighborhood_sends_no_gossip_when_target_does_not_exist() { let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A - // This is ungossippable not because of any attribute of its own, but because the - // GossipProducerMock is set to return None when ordered to target it. + // This is ungossippable not because of any attribute of its own, but because the + // GossipProducerMock is set to return None when ordered to target it. let ungossippable = make_node_record(1050, true); let mut subject = neighborhood_from_nodes(&subject_node, Some(&ungossippable), &N_CRYPTDE_PAIR); @@ -6211,8 +6221,7 @@ mod tests { let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); - let gossip_acceptor = - GossipAcceptorMock::new().handle_result(vec![]); + let gossip_acceptor = GossipAcceptorMock::new().handle_result(vec![]); subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); @@ -6238,8 +6247,26 @@ mod tests { let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A let neighbor = make_node_record(1111, true); let mut subject = neighborhood_from_nodes(&subject_node, Some(&neighbor), &N_CRYPTDE_PAIR); - let gossip_acceptor = GossipAcceptorMock::new() - .handle_result(vec![GossipAcceptanceResult::Ban("Bad guy".to_string())]); + let public_key = PublicKey::from(&b"BadGuyPublicKey"[..]); + let ip_address = IpAddr::from_str("1.3.2.4").unwrap(); + let earning_wallet = make_wallet("BadGuyEarningWallet"); + let consuming_wallet = make_wallet("BadGuyConsumingWallet"); + let timestamp = PrimitiveDateTime::new( + Date::from_calendar_date(2024, Month::April, 1).unwrap(), + Time::from_hms(3, 4, 5).unwrap(), + ); + let reason = "Bad guy".to_string(); + let gossip_acceptor = + GossipAcceptorMock::new().handle_result(vec![GossipAcceptanceResult::Ban( + Malefactor { + public_key_opt: Some(public_key.clone()), + ip_address_opt: Some(ip_address), + earning_wallet_opt: Some(earning_wallet.clone()), + consuming_wallet_opt: Some(consuming_wallet.clone()), + timestamp: timestamp.clone(), + reason: reason.clone(), + }, + )]); subject.gossip_acceptor = Box::new(gossip_acceptor); let subject_node = subject.neighborhood_database.root().clone(); let (hopper, _, hopper_recording_arc) = make_recorder(); @@ -6431,13 +6458,11 @@ mod tests { "node_gossips_to_neighbors_on_startup", ), ); - subject.persistent_config_factory = Box::new( - PersistentConfigurationFactoryTest::new( - PersistentConfigurationMock::new() - .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) - .rate_pack_limits_result(Ok(RatePackLimits::test_default())), - ) - ); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + )); subject.data_directory = data_dir; subject.logger = Logger::new("node_gossips_to_neighbors_on_startup"); let this_node = subject.neighborhood_database.root().clone(); @@ -6492,13 +6517,11 @@ mod tests { test_name, ), ); - subject.persistent_config_factory = Box::new( - PersistentConfigurationFactoryTest::new( - PersistentConfigurationMock::new() - .min_hops_result(Ok(min_hops_in_persistent_configuration)) - .rate_pack_limits_result(Ok(RatePackLimits::test_default())) - ) - ); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(min_hops_in_persistent_configuration)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + )); let system = System::new(test_name); let addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); @@ -6539,13 +6562,11 @@ mod tests { ), ); subject.logger = Logger::new(test_name); - subject.persistent_config_factory = Box::new( - PersistentConfigurationFactoryTest::new( - PersistentConfigurationMock::new() - .min_hops_result(Ok(min_hops_in_db)) - .rate_pack_limits_result(Ok(RatePackLimits::test_default())), - ) - ); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryTest::new( + PersistentConfigurationMock::new() + .min_hops_result(Ok(min_hops_in_db)) + .rate_pack_limits_result(Ok(RatePackLimits::test_default())), + )); let system = System::new(test_name); let addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); @@ -6964,8 +6985,11 @@ mod tests { let one_next_door_neighbor = make_node_record(3333, true); let another_next_door_neighbor = make_node_record(4444, true); let subject_node = make_global_cryptde_node_record(5555, true, &N_CRYPTDE_PAIR); // 9e7p7un06eHs6frl5A - let mut subject = - neighborhood_from_nodes(&subject_node, Some(&one_next_door_neighbor), &N_CRYPTDE_PAIR); + let mut subject = neighborhood_from_nodes( + &subject_node, + Some(&one_next_door_neighbor), + &N_CRYPTDE_PAIR, + ); subject.min_hops = min_hops; subject @@ -7227,8 +7251,11 @@ mod tests { version: 0, location_opt: None, }; - let node_record = - NodeRecord::new(&public_key, N_CRYPTDE_PAIR.main.as_ref(), node_record_inputs); + let node_record = NodeRecord::new( + &public_key, + N_CRYPTDE_PAIR.main.as_ref(), + node_record_inputs, + ); let unreachable_host = String::from("facebook.com"); let mut subject = neighborhood_from_nodes(&subject_node, None, &N_CRYPTDE_PAIR); let _ = subject.neighborhood_database.add_node(node_record); @@ -7582,7 +7609,9 @@ mod tests { &bc_from_earning_wallet(make_wallet("earning_wallet")), ); subject.data_directory = data_dir.to_path_buf(); - subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryReal::new(subject.data_directory.clone())); + subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryReal::new( + subject.data_directory.clone(), + )); subject.persistent_config_factory.make(); }; @@ -7592,13 +7621,14 @@ mod tests { fn make_standard_subject() -> Neighborhood { let root_node = make_global_cryptde_node_record(9999, true, &N_CRYPTDE_PAIR); let neighbor_node = make_node_record(9998, true); - let mut subject = neighborhood_from_nodes(&root_node, Some(&neighbor_node), &N_CRYPTDE_PAIR); + let mut subject = + neighborhood_from_nodes(&root_node, Some(&neighbor_node), &N_CRYPTDE_PAIR); let persistent_config = PersistentConfigurationMock::new() .rate_pack_limits_result(Ok(RatePackLimits::test_default())); subject.persistent_config = Box::new(persistent_config); subject.gossip_acceptor = Box::new(GossipAcceptorReal::new( N_CRYPTDE_PAIR.main.dup(), - subject.persistent_config.as_ref() + subject.persistent_config.as_ref(), )); subject } @@ -7829,10 +7859,8 @@ mod tests { neighborhood.neighborhood_database = db; let persistent_config_mock = PersistentConfigurationMock::new() .rate_pack_limits_result(Ok(RatePackLimits::test_default())); - let gossip_acceptor = GossipAcceptorReal::new( - N_CRYPTDE_PAIR.main.dup(), - &persistent_config_mock - ); + let gossip_acceptor = + GossipAcceptorReal::new(N_CRYPTDE_PAIR.main.dup(), &persistent_config_mock); neighborhood.gossip_acceptor = Box::new(gossip_acceptor); neighborhood diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 41b76d0113..8329f814f3 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -2496,7 +2496,7 @@ mod tests { .set_rate_pack_result(Ok(())) .rate_pack_limits_result(Ok(RatePackLimits::new( RatePack::new(5, 5, 5, 5), - RatePack::new(7, 7, 7, 7) + RatePack::new(7, 7, 7, 7), ))); let result = configure_rate_pack( @@ -2531,7 +2531,7 @@ mod tests { .set_rate_pack_result(Ok(())) .rate_pack_limits_result(Ok(RatePackLimits::new( RatePack::new(5, 5, 5, 5), - RatePack::new(7, 7, 7, 7) + RatePack::new(7, 7, 7, 7), ))); let result = configure_rate_pack( diff --git a/node/src/node_test_utils.rs b/node/src/node_test_utils.rs index f4c1a8d29c..5c8270231c 100644 --- a/node/src/node_test_utils.rs +++ b/node/src/node_test_utils.rs @@ -6,11 +6,13 @@ use crate::discriminator::DiscriminatorFactory; use crate::discriminator::UnmaskedChunk; use crate::masquerader::MasqueradeError; use crate::masquerader::Masquerader; +use crate::neighborhood::node_record::NodeRecord; use crate::node_configurator::DirsWrapper; use crate::null_masquerader::NullMasquerader; use crate::privilege_drop::IdWrapper; use crate::stream_handler_pool::StreamHandlerPoolSubs; use crate::stream_messages::*; +use crate::sub_lib::cryptde::{CryptData, PlainData}; use crate::sub_lib::framer::FramedChunk; use crate::sub_lib::framer::Framer; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; @@ -317,3 +319,16 @@ impl Masquerader for FailingMasquerader { )) } } + +impl NodeRecord { + pub fn but_no_node_addr(&self) -> NodeRecord { + let mut modified = NodeRecord { + inner: self.inner.clone(), + metadata: self.metadata.clone(), + signed_gossip: PlainData::new(&[]), + signature: CryptData::new(&[]), + }; + modified.resign(); + modified + } +} diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index aa37e97d56..ad9738d7fe 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -23,6 +23,7 @@ use lazy_static::lazy_static; use masq_lib::blockchains::blockchain_records::CHAINS; use masq_lib::blockchains::chains::{chain_from_chain_identifier_opt, Chain}; use masq_lib::constants::{CENTRAL_DELIMITER, CHAIN_IDENTIFIER_DELIMITER, MASQ_URL_PREFIX}; +use masq_lib::shared_schema::ConfiguratorError; use masq_lib::ui_gateway::NodeFromUiMessage; use masq_lib::utils::NeighborhoodModeLight; use serde_derive::{Deserialize, Serialize}; @@ -31,7 +32,6 @@ use std::fmt::{Debug, Display, Formatter}; use std::net::IpAddr; use std::str::FromStr; use std::time::Duration; -use masq_lib::shared_schema::ConfiguratorError; const ASK_ABOUT_GOSSIP_INTERVAL: Duration = Duration::from_secs(10); @@ -87,7 +87,7 @@ impl RatePack { #[derive(Clone, Debug, PartialEq, Eq)] pub struct RatePackLimits { pub lo: RatePack, - pub hi: RatePack + pub hi: RatePack, } impl RatePackLimits { @@ -103,13 +103,12 @@ impl RatePackLimits { } pub fn analyze(&self, rate_pack: &RatePack) -> Result<(), ConfiguratorError> { - let check_min_and_max = | - candidate: u64, - min: u64, - max: u64, - name: &str, - error: ConfiguratorError - | -> ConfiguratorError { + let check_min_and_max = |candidate: u64, + min: u64, + max: u64, + name: &str, + error: ConfiguratorError| + -> ConfiguratorError { let mut result = error; if candidate < min { result = result.another_required( diff --git a/node/src/test_utils/database_utils.rs b/node/src/test_utils/database_utils.rs index 06f8ec794a..d4e31941d4 100644 --- a/node/src/test_utils/database_utils.rs +++ b/node/src/test_utils/database_utils.rs @@ -7,6 +7,10 @@ use crate::database::db_initializer::ExternalData; use crate::database::rusqlite_wrappers::ConnectionWrapper; use crate::database::db_migrations::db_migrator::DbMigrator; +use crate::db_config::persistent_configuration::{ + PersistentConfiguration, PersistentConfigurationFactory, +}; +use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; use masq_lib::logger::Logger; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; use masq_lib::utils::{to_string, NeighborhoodModeLight}; @@ -18,8 +22,6 @@ use std::io::Read; use std::iter::once; use std::path::Path; use std::sync::{Arc, Mutex}; -use crate::db_config::persistent_configuration::{PersistentConfiguration, PersistentConfigurationFactory}; -use crate::test_utils::persistent_configuration_mock::PersistentConfigurationMock; pub fn bring_db_0_back_to_life_and_return_connection(db_path: &Path) -> Connection { match remove_file(db_path) { @@ -306,17 +308,24 @@ pub fn make_external_data() -> ExternalData { } pub struct PersistentConfigurationFactoryTest { - mock_opt: RefCell> + mock_opt: RefCell>, } impl PersistentConfigurationFactory for PersistentConfigurationFactoryTest { fn make(&self) -> Box { - Box::new(self.mock_opt.borrow_mut().take().expect("PersistentConfigurationFactoryTest already used")) + Box::new( + self.mock_opt + .borrow_mut() + .take() + .expect("PersistentConfigurationFactoryTest already used"), + ) } } impl PersistentConfigurationFactoryTest { pub fn new(mock: PersistentConfigurationMock) -> Self { - Self { mock_opt: RefCell::new(Some(mock)) } + Self { + mock_opt: RefCell::new(Some(mock)), + } } -} \ No newline at end of file +} diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 31bc43cfcf..3b3b17975e 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -507,7 +507,9 @@ pub mod unshared_test_utils { use crate::node_test_utils::DirsWrapperMock; use crate::sub_lib::accountant::{PaymentThresholds, ScanIntervals}; use crate::sub_lib::cryptde::CryptDE; - use crate::sub_lib::neighborhood::{ConnectionProgressMessage, RatePack, RatePackLimits, DEFAULT_RATE_PACK}; + use crate::sub_lib::neighborhood::{ + ConnectionProgressMessage, RatePack, RatePackLimits, DEFAULT_RATE_PACK, + }; use crate::sub_lib::proxy_client::ClientResponsePayload_0v1; use crate::sub_lib::proxy_server::{ClientRequestPayload_0v1, ProxyProtocol}; use crate::sub_lib::sequence_buffer::SequencedPacket; From a0a450337a222bf23bb099b55db9f119715f687a Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Sun, 16 Nov 2025 20:55:26 -0500 Subject: [PATCH 8/9] Do the multinode tests pass in Actions? --- .../src/masq_node_cluster.rs | 9 ++ node/src/bootstrapper.rs | 14 +-- node/src/database/db_initializer.rs | 6 +- .../migrations/migration_11_to_12.rs | 2 +- node/src/db_config/config_dao_null.rs | 2 +- .../src/db_config/persistent_configuration.rs | 10 +- node/src/neighborhood/gossip_acceptor.rs | 101 +++++++++++++++--- node/src/neighborhood/malefactor.rs | 5 +- node/src/neighborhood/mod.rs | 19 ++-- node/src/sub_lib/neighborhood.rs | 70 +++++++----- 10 files changed, 171 insertions(+), 67 deletions(-) diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index 86a94af541..80850e01d8 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -306,6 +306,15 @@ impl MASQNodeCluster { } fn create_network() -> Result<(), String> { + let mut command = Command::new( + "docker", + Command::strings(vec!["network", "rm", "integration_net"]), + ); + match command.stdout_or_stderr() { + Ok(_) => println!("Removed existing integration_net network"), + Err(msg) if msg.contains("network integration_net not found") => println!("No existing integration_net network to remove: cool!"), + Err(msg) => return Err(format!("Error removing existing integration_net network: {}", msg)), + } let mut command = Command::new( "docker", Command::strings(vec![ diff --git a/node/src/bootstrapper.rs b/node/src/bootstrapper.rs index 688d143368..0a4798a547 100644 --- a/node/src/bootstrapper.rs +++ b/node/src/bootstrapper.rs @@ -748,7 +748,7 @@ mod tests { use tokio::prelude::Async; lazy_static! { - static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); + static ref BS_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); pub static ref INITIALIZATION: Mutex = Mutex::new(false); } @@ -1534,13 +1534,13 @@ mod tests { ); let cryptde_ref = { let descriptor = Bootstrapper::make_local_descriptor( - CRYPTDE_PAIR.main.as_ref(), + BS_CRYPTDE_PAIR.main.as_ref(), Some(node_addr), TEST_DEFAULT_CHAIN, ); - Bootstrapper::report_local_descriptor(CRYPTDE_PAIR.main.as_ref(), &descriptor); + Bootstrapper::report_local_descriptor(BS_CRYPTDE_PAIR.main.as_ref(), &descriptor); - CRYPTDE_PAIR.main.as_ref() + BS_CRYPTDE_PAIR.main.as_ref() }; let expected_descriptor = format!( "masq://base-sepolia:{}@2.3.4.5:3456/4567", @@ -1576,13 +1576,13 @@ mod tests { init_test_logging(); let cryptdes = { let descriptor = Bootstrapper::make_local_descriptor( - CRYPTDE_PAIR.main.as_ref(), + BS_CRYPTDE_PAIR.main.as_ref(), None, TEST_DEFAULT_CHAIN, ); - Bootstrapper::report_local_descriptor(CRYPTDE_PAIR.main.as_ref(), &descriptor); + Bootstrapper::report_local_descriptor(BS_CRYPTDE_PAIR.main.as_ref(), &descriptor); - CRYPTDE_PAIR.clone() + BS_CRYPTDE_PAIR.clone() }; let expected_descriptor = format!( "masq://base-sepolia:{}@:", diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index 0673a3035f..85cd09819a 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -259,7 +259,7 @@ impl DbInitializerReal { Self::set_config_value( conn, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS), + Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), false, "rate pack limits", ); @@ -969,7 +969,7 @@ mod tests { verify( &mut config_vec, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS), + Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), false, ); verify( @@ -1087,7 +1087,7 @@ mod tests { verify( &mut config_vec, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS), + Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), false, ); verify( diff --git a/node/src/database/db_migrations/migrations/migration_11_to_12.rs b/node/src/database/db_migrations/migrations/migration_11_to_12.rs index 87261688c2..61182a5220 100644 --- a/node/src/database/db_migrations/migrations/migration_11_to_12.rs +++ b/node/src/database/db_migrations/migrations/migration_11_to_12.rs @@ -14,7 +14,7 @@ impl DatabaseMigration for Migrate_11_to_12 { ) -> rusqlite::Result<()> { declaration_utils.execute_upon_transaction(&[&format!( "INSERT INTO config (name, value, encrypted) VALUES ('rate_pack_limits', '{}', 0)", - DEFAULT_RATE_PACK_LIMITS + DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter() )]) } diff --git a/node/src/db_config/config_dao_null.rs b/node/src/db_config/config_dao_null.rs index 32a2f249e3..a38d5d20ef 100644 --- a/node/src/db_config/config_dao_null.rs +++ b/node/src/db_config/config_dao_null.rs @@ -140,7 +140,7 @@ impl Default for ConfigDaoNull { ); data.insert( "rate_pack_limits".to_string(), - (Some(DEFAULT_RATE_PACK_LIMITS.to_string()), false), + (Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter()), false), ); data.insert( "scan_intervals".to_string(), diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 1bb8db9c42..7251ff7491 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -591,7 +591,7 @@ impl PersistentConfiguration for PersistentConfigurationReal { "Required value rate_pack_limits is NULL in CONFIG table: database is corrupt!", ); let captures = RATE_PACK_LIMIT_FORMAT.captures(limits_string.as_str()) - .expect(format!("Syntax error in rate_pack_limits value '{}': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.", limits_string).as_str()); + .unwrap_or_else(|| panic!("Syntax error in rate_pack_limits value '{}': should be -|-|-|- where L is low, H is high, R is routing, E is exit, BR is byte rate, and SR is service rate. All numbers should be in wei.", limits_string)); let candidate = RatePackLimits::new( Self::extract_candidate(&captures, 1), Self::extract_candidate(&captures, 2), @@ -922,6 +922,12 @@ impl PersistentConfigurationInvalid { } } +impl Default for PersistentConfigurationInvalid { + fn default() -> Self { + Self::new() + } +} + pub trait PersistentConfigurationFactory { fn make(&self) -> Box; } @@ -935,7 +941,7 @@ impl PersistentConfigurationFactory for PersistentConfigurationFactoryReal { let db_initializer: &dyn DbInitializer = &DbInitializerReal::default(); let conn = db_initializer .initialize( - &self.data_directory.as_path(), + self.data_directory.as_path(), DbInitializationConfig::panic_on_migration(), ) .unwrap_or_else(|err| db_connection_launch_panic(err, &self.data_directory)); diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index bcdb64cfa1..172bd61078 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -9,10 +9,7 @@ use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, Neighborh use crate::neighborhood::node_record::NodeRecord; use crate::neighborhood::UserExitPreferences; use crate::sub_lib::cryptde::{CryptDE, PublicKey}; -use crate::sub_lib::neighborhood::{ - ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, - RatePackLimits, -}; +use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, RatePackLimits, DEFAULT_RATE_PACK_LIMITS}; use crate::sub_lib::node_addr::NodeAddr; use itertools::Itertools; use masq_lib::logger::Logger; @@ -1132,16 +1129,23 @@ impl GossipHandler for StandardGossipHandler { ) -> Vec { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); - let gossip_source_agr = agrs - .iter() - .find(|agr| { - agr.node_addr_opt.as_ref().map(|na| na.ip_addr()) == Some(gossip_source.ip()) - }) - .expect(&format!( - "Standard Gossip from {} lacks source AGR", - gossip_source - )) - .clone(); + let gossip_source_agr = match agrs.iter().find(|agr| agr.node_addr_opt.as_ref().map(|na| na.ip_addr()) == Some(gossip_source.ip())) { + Some(agr) => agr.clone(), + None => { + let message = format!( + "Node at {} sent Standard gossip without a record describing itself", + gossip_source.ip() + ); + warning!(self.logger, "{}", message); + return vec![GossipAcceptanceResult::Ban(Malefactor::new( + None, + Some(gossip_source.ip()), + None, + None, + message, + ))] + } + }; let patch = self.compute_patch(&agrs, database.root(), neighborhood_metadata.db_patch_size); let in_patch_agrs = agrs .into_iter() @@ -1608,7 +1612,7 @@ impl GossipAcceptorReal { Box::new(PassHandler::new()), Box::new(IntroductionHandler::new(rate_pack_limits, logger.clone())), Box::new(StandardGossipHandler::new( - &RatePackLimits::test_default(), + &DEFAULT_RATE_PACK_LIMITS, logger.clone(), )), Box::new(RejectHandler::new()), @@ -1694,6 +1698,12 @@ impl GossipAcceptorInvalid { } } +impl Default for GossipAcceptorInvalid { + fn default() -> Self { + Self::new() + } +} + #[cfg(test)] mod tests { use super::*; @@ -3466,6 +3476,62 @@ mod tests { assert_eq!(result, vec![]); } + #[test] + fn standard_gossip_that_does_not_describe_gossip_source_is_rejected() { + /* + Destination Node ==> + S---D + + Source Node ==> + S---D + + The source node(S) will send Gossip containing no information about + itself, which will get it banned by IP. + */ + init_test_logging(); + let test_name = "standard_gossip_that_does_not_describe_gossip_source_is_rejected"; + let src_root = make_node_record(1234, true); + let dest_root = make_node_record(2345, true); + let mut src_db = db_from_node(&src_root); + let mut dest_db = db_from_node(&dest_root); + dest_db.add_node(src_root.clone()).unwrap(); + dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), src_root.public_key()); + src_db.add_node(dest_db.root().clone()).unwrap(); + src_db.add_arbitrary_full_neighbor(src_root.public_key(), dest_root.public_key()); + let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); + let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); + let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); + let (cpm_recipient, _) = make_cpm_recipient(); + let mut neighborhood_metadata = make_default_neighborhood_metadata(); + neighborhood_metadata.cpm_recipient = cpm_recipient; + + let handle_result = subject.handle( + &cryptde, + &mut dest_db, + vec![], + gossip_source, + neighborhood_metadata, + ); + + let message = format!( + "Node at {} sent Standard gossip without a record describing itself", + gossip_source.ip(), + ); + assert_eq!( + handle_result, + vec![ + GossipAcceptanceResult::Ban(Malefactor::new( + None, + Some(gossip_source.ip()), + None, + None, + message.clone() + )), + ] + ); + TestLogHandler::new().exists_log_containing(&format!("WARN: {}: {}", test_name, message)); + } + #[test] fn standard_gossip_that_fails_validation_is_rejected() { /* @@ -3482,6 +3548,7 @@ mod tests { rate pack is outside the limits. */ init_test_logging(); + let test_name = "standard_gossip_that_fails_validation_is_rejected"; let src_root = make_node_record(1234, true); let dest_root = make_node_record(2345, true); let mut src_db = db_from_node(&src_root); @@ -3512,7 +3579,7 @@ mod tests { RatePack::new(100, 100, 100, 100), RatePack::new(u64::MAX, u64::MAX, u64::MAX, u64::MAX), ); - let subject = StandardGossipHandler::new(&rate_pack_limits, Logger::new("test")); + let subject = StandardGossipHandler::new(&rate_pack_limits, Logger::new(test_name)); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let agrs_vec: Vec = gossip.try_into().unwrap(); let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); @@ -3561,7 +3628,7 @@ mod tests { assert_eq!(system.run(), 0); let recording = cpm_recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); - TestLogHandler::new().exists_log_containing(&message); + TestLogHandler::new().exists_log_containing(&format!("WARN: {}: {}", test_name, message)); } #[test] diff --git a/node/src/neighborhood/malefactor.rs b/node/src/neighborhood/malefactor.rs index 79f791b965..b4abb6d11b 100644 --- a/node/src/neighborhood/malefactor.rs +++ b/node/src/neighborhood/malefactor.rs @@ -66,8 +66,8 @@ impl PartialEq for Malefactor { && self.earning_wallet_opt == other.earning_wallet_opt && self.consuming_wallet_opt == other.consuming_wallet_opt && self.reason == other.reason; - let plus_one_second = self.timestamp.saturating_add(FUDGE_FACTOR.clone()); - let minus_one_second = self.timestamp.saturating_sub(FUDGE_FACTOR.clone()); + let plus_one_second = self.timestamp.saturating_add(*FUDGE_FACTOR); + let minus_one_second = self.timestamp.saturating_sub(*FUDGE_FACTOR); equal && (other.timestamp >= minus_one_second && other.timestamp <= plus_one_second) } } @@ -133,6 +133,7 @@ impl Malefactor { } } +#[cfg(test)] mod tests { use super::*; use std::str::FromStr; diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 1a2f47d31f..6f64cf4b68 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -11,8 +11,6 @@ pub mod node_record; pub mod overall_connection_status; use crate::bootstrapper::{BootstrapperConfig, CryptDEPair}; -use crate::database::db_initializer::DbInitializationConfig; -use crate::database::db_initializer::{DbInitializer, DbInitializerReal}; use crate::db_config::persistent_configuration::{ PersistentConfigError, PersistentConfiguration, PersistentConfigurationFactory, PersistentConfigurationFactoryReal, PersistentConfigurationInvalid, @@ -79,7 +77,7 @@ use std::collections::HashSet; use std::convert::TryFrom; use std::fmt::Debug; use std::net::{IpAddr, SocketAddr}; -use std::path::PathBuf; +// use std::path::PathBuf; use std::string::ToString; pub const CRASH_KEY: &str = "NEIGHBORHOOD"; @@ -109,7 +107,7 @@ pub struct Neighborhood { overall_connection_status: OverallConnectionStatus, chain: Chain, crashable: bool, - data_directory: PathBuf, + // data_directory: PathBuf, persistent_config_factory: Box, persistent_config: Box, db_password_opt: Option, @@ -435,7 +433,7 @@ impl Neighborhood { overall_connection_status, chain: config.blockchain_bridge_config.chain, crashable: config.crash_point == CrashPoint::Message, - data_directory: config.data_directory.clone(), + // data_directory: config.data_directory.clone(), persistent_config_factory: Box::new(PersistentConfigurationFactoryReal::new( config.data_directory.clone(), )), @@ -2264,6 +2262,7 @@ mod tests { use crate::test_utils::database_utils::PersistentConfigurationFactoryTest; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; + use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; lazy_static! { static ref N_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); @@ -2602,7 +2601,7 @@ mod tests { .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) .rate_pack_limits_result(Ok(RatePackLimits::test_default())), )); - subject.data_directory = data_dir; + // subject.data_directory = data_dir; let addr = subject.start(); let sub = addr.clone().recipient::(); let (hopper, _, hopper_recording_arc) = make_recorder(); @@ -6285,7 +6284,7 @@ mod tests { let hopper_recording = hopper_recording_arc.lock().unwrap(); assert_eq!(0, hopper_recording.len()); let tlh = TestLogHandler::new(); - tlh.exists_log_containing("WARN: Neighborhood: Malefactor detected at 5.5.5.5:5555, but malefactor bans not yet implemented; ignoring: Bad guy"); + tlh.exists_log_containing("WARN: Neighborhood: Malefactor detected at 5.5.5.5:5555, but malefactor bans not yet implemented; ignoring: Malefactor QmFkR3V5UHVibGljS2V5 at 1.3.2.4 with earning wallet 0x004261644775794561726e696e6757616c6c6574, consuming wallet 0x426164477579436f6e73756d696e6757616c6c65 detected at 2024-04-01 3:04:05.0: Bad guy"); } #[test] @@ -6463,7 +6462,7 @@ mod tests { .min_hops_result(Ok(MIN_HOPS_FOR_TEST)) .rate_pack_limits_result(Ok(RatePackLimits::test_default())), )); - subject.data_directory = data_dir; + // subject.data_directory = data_dir; subject.logger = Logger::new("node_gossips_to_neighbors_on_startup"); let this_node = subject.neighborhood_database.root().clone(); let system = System::new("node_gossips_to_neighbors_on_startup"); @@ -7608,9 +7607,9 @@ mod tests { N_CRYPTDE_PAIR.clone(), &bc_from_earning_wallet(make_wallet("earning_wallet")), ); - subject.data_directory = data_dir.to_path_buf(); + // subject.data_directory = data_dir.to_path_buf(); subject.persistent_config_factory = Box::new(PersistentConfigurationFactoryReal::new( - subject.data_directory.clone(), + data_dir.to_path_buf(), )); subject.persistent_config_factory.make(); }; diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index ad9738d7fe..29204cb494 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -49,8 +49,20 @@ pub const ZERO_RATE_PACK: RatePack = RatePack { exit_service_rate: 0, }; -pub const DEFAULT_RATE_PACK_LIMITS: &str = - "100-100000000000000|100-100000000000000|100-100000000000000|100-100000000000000"; +pub const DEFAULT_RATE_PACK_LIMITS: RatePackLimits = RatePackLimits { + lo: RatePack { + routing_byte_rate: 100, + routing_service_rate: 100, + exit_byte_rate: 100, + exit_service_rate: 100, + }, + hi: RatePack { + routing_byte_rate: 100_000_000_000_000, + routing_service_rate: 100_000_000_000_000, + exit_byte_rate: 100_000_000_000_000, + exit_service_rate: 100_000_000_000_000, + }, +}; #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)] pub struct RatePack { @@ -96,10 +108,7 @@ impl RatePackLimits { } pub fn check(&self, rate_pack: &RatePack) -> bool { - match self.analyze(rate_pack) { - Ok(_) => true, - Err(_) => false, - } + self.analyze(rate_pack).is_ok() } pub fn analyze(&self, rate_pack: &RatePack) -> Result<(), ConfiguratorError> { @@ -164,6 +173,19 @@ impl RatePackLimits { Err(error) } } + + pub fn rate_pack_limits_parameter(&self) -> String { + format!("{}-{}|{}-{}|{}-{}|{}-{}", + self.lo.routing_byte_rate, + self.hi.routing_byte_rate, + self.lo.routing_service_rate, + self.hi.routing_service_rate, + self.lo.exit_byte_rate, + self.hi.exit_byte_rate, + self.lo.exit_service_rate, + self.hi.exit_service_rate, + ) + } } #[derive(Clone, Debug, PartialEq, Eq)] @@ -679,8 +701,8 @@ pub enum GossipFailure_0v1 { Unknown, } -impl fmt::Display for GossipFailure_0v1 { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { +impl Display for GossipFailure_0v1 { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { let msg = match self { GossipFailure_0v1::NoNeighbors => "No neighbors for Introduction or Pass", GossipFailure_0v1::NoSuitableNeighbors => { @@ -730,7 +752,7 @@ mod tests { use std::str::FromStr; lazy_static! { - static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); + static ref NB_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } #[test] @@ -962,7 +984,7 @@ mod tests { #[test] fn from_str_complains_about_bad_base_64() { let result = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:bad_key@1.2.3.4:1234;2345", )); @@ -1005,7 +1027,7 @@ mod tests { #[test] fn from_str_complains_about_blank_public_key() { let result = - NodeDescriptor::try_from((CRYPTDE_PAIR.main.as_ref(), "masq://dev:@1.2.3.4:1234/2345")); + NodeDescriptor::try_from((NB_CRYPTDE_PAIR.main.as_ref(), "masq://dev:@1.2.3.4:1234/2345")); assert_eq!(result, Err(String::from("Public key cannot be empty"))); } @@ -1013,7 +1035,7 @@ mod tests { #[test] fn from_str_complains_about_bad_node_addr() { let result = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:R29vZEtleQ==@BadNodeAddr", )); @@ -1023,7 +1045,7 @@ mod tests { #[test] fn from_str_handles_the_happy_path_with_node_addr() { let result = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-ropsten:R29vZEtleQ@1.2.3.4:1234/2345/3456", )); @@ -1043,7 +1065,7 @@ mod tests { #[test] fn from_str_handles_the_happy_path_without_node_addr() { let result = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:R29vZEtleQ@:", )); @@ -1087,7 +1109,7 @@ mod tests { #[test] fn node_descriptor_from_key_node_addr_and_mainnet_flag_works() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = NB_CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[1, 2, 3, 4, 5, 6, 7, 8]); let node_addr = NodeAddr::new(&IpAddr::from_str("123.45.67.89").unwrap(), &[2345, 3456]); @@ -1105,7 +1127,7 @@ mod tests { #[test] fn node_descriptor_to_string_works_for_mainnet() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = NB_CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[1, 2, 3, 4, 5, 6, 7, 8]); let node_addr = NodeAddr::new(&IpAddr::from_str("123.45.67.89").unwrap(), &[2345, 3456]); let subject = NodeDescriptor::from((&public_key, &node_addr, Chain::EthMainnet, cryptde)); @@ -1120,7 +1142,7 @@ mod tests { #[test] fn node_descriptor_to_string_works_for_not_mainnet() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = NB_CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[1, 2, 3, 4, 5, 6, 7, 8]); let node_addr = NodeAddr::new(&IpAddr::from_str("123.45.67.89").unwrap(), &[2345, 3456]); let subject = NodeDescriptor::from((&public_key, &node_addr, Chain::EthRopsten, cryptde)); @@ -1135,7 +1157,7 @@ mod tests { #[test] fn first_part_of_node_descriptor_must_not_be_longer_than_required() { - let cryptde: &dyn CryptDE = CRYPTDE_PAIR.main.as_ref(); + let cryptde: &dyn CryptDE = NB_CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, @@ -1177,12 +1199,12 @@ mod tests { #[test] fn standard_mode_results() { let one_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:AQIDBA@1.2.3.4:1234", )) .unwrap(); let another_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:AgMEBQ@2.3.4.5:2345", )) .unwrap(); @@ -1212,12 +1234,12 @@ mod tests { #[test] fn originate_only_mode_results() { let one_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-ropsten:AQIDBA@1.2.3.4:1234", )) .unwrap(); let another_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-ropsten:AgMEBQ@2.3.4.5:2345", )) .unwrap(); @@ -1243,12 +1265,12 @@ mod tests { #[test] fn consume_only_mode_results() { let one_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:AQIDBA@1.2.3.4:1234", )) .unwrap(); let another_neighbor = NodeDescriptor::try_from(( - CRYPTDE_PAIR.main.as_ref(), + NB_CRYPTDE_PAIR.main.as_ref(), "masq://eth-mainnet:AgMEBQ@2.3.4.5:2345", )) .unwrap(); From c33fe6d936411d3a3e59c0b77745dc9ca2dd7e8d Mon Sep 17 00:00:00 2001 From: Dan Wiebe Date: Sun, 16 Nov 2025 21:04:32 -0500 Subject: [PATCH 9/9] Formatting --- .../src/masq_node_cluster.rs | 11 ++++- node/src/database/db_initializer.rs | 18 +++++++-- node/src/db_config/config_dao_null.rs | 5 ++- node/src/neighborhood/gossip_acceptor.rs | 40 ++++++++++--------- node/src/neighborhood/mod.rs | 4 +- node/src/sub_lib/neighborhood.rs | 9 +++-- 6 files changed, 59 insertions(+), 28 deletions(-) diff --git a/multinode_integration_tests/src/masq_node_cluster.rs b/multinode_integration_tests/src/masq_node_cluster.rs index 80850e01d8..67ea025ae9 100644 --- a/multinode_integration_tests/src/masq_node_cluster.rs +++ b/multinode_integration_tests/src/masq_node_cluster.rs @@ -312,8 +312,15 @@ impl MASQNodeCluster { ); match command.stdout_or_stderr() { Ok(_) => println!("Removed existing integration_net network"), - Err(msg) if msg.contains("network integration_net not found") => println!("No existing integration_net network to remove: cool!"), - Err(msg) => return Err(format!("Error removing existing integration_net network: {}", msg)), + Err(msg) if msg.contains("network integration_net not found") => { + println!("No existing integration_net network to remove: cool!") + } + Err(msg) => { + return Err(format!( + "Error removing existing integration_net network: {}", + msg + )) + } } let mut command = Command::new( "docker", diff --git a/node/src/database/db_initializer.rs b/node/src/database/db_initializer.rs index 85cd09819a..d23eef2c45 100644 --- a/node/src/database/db_initializer.rs +++ b/node/src/database/db_initializer.rs @@ -259,7 +259,11 @@ impl DbInitializerReal { Self::set_config_value( conn, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), + Some( + DEFAULT_RATE_PACK_LIMITS + .rate_pack_limits_parameter() + .as_str(), + ), false, "rate pack limits", ); @@ -969,7 +973,11 @@ mod tests { verify( &mut config_vec, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), + Some( + DEFAULT_RATE_PACK_LIMITS + .rate_pack_limits_parameter() + .as_str(), + ), false, ); verify( @@ -1087,7 +1095,11 @@ mod tests { verify( &mut config_vec, "rate_pack_limits", - Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter().as_str()), + Some( + DEFAULT_RATE_PACK_LIMITS + .rate_pack_limits_parameter() + .as_str(), + ), false, ); verify( diff --git a/node/src/db_config/config_dao_null.rs b/node/src/db_config/config_dao_null.rs index a38d5d20ef..d03dda444f 100644 --- a/node/src/db_config/config_dao_null.rs +++ b/node/src/db_config/config_dao_null.rs @@ -140,7 +140,10 @@ impl Default for ConfigDaoNull { ); data.insert( "rate_pack_limits".to_string(), - (Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter()), false), + ( + Some(DEFAULT_RATE_PACK_LIMITS.rate_pack_limits_parameter()), + false, + ), ); data.insert( "scan_intervals".to_string(), diff --git a/node/src/neighborhood/gossip_acceptor.rs b/node/src/neighborhood/gossip_acceptor.rs index 172bd61078..c68ba6b8a2 100644 --- a/node/src/neighborhood/gossip_acceptor.rs +++ b/node/src/neighborhood/gossip_acceptor.rs @@ -9,7 +9,10 @@ use crate::neighborhood::neighborhood_database::{NeighborhoodDatabase, Neighborh use crate::neighborhood::node_record::NodeRecord; use crate::neighborhood::UserExitPreferences; use crate::sub_lib::cryptde::{CryptDE, PublicKey}; -use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, RatePackLimits, DEFAULT_RATE_PACK_LIMITS}; +use crate::sub_lib::neighborhood::{ + ConnectionProgressEvent, ConnectionProgressMessage, GossipFailure_0v1, NeighborhoodMetadata, + RatePackLimits, DEFAULT_RATE_PACK_LIMITS, +}; use crate::sub_lib::node_addr::NodeAddr; use itertools::Itertools; use masq_lib::logger::Logger; @@ -1129,7 +1132,9 @@ impl GossipHandler for StandardGossipHandler { ) -> Vec { let initial_neighborship_status = StandardGossipHandler::check_full_neighbor(database, gossip_source.ip()); - let gossip_source_agr = match agrs.iter().find(|agr| agr.node_addr_opt.as_ref().map(|na| na.ip_addr()) == Some(gossip_source.ip())) { + let gossip_source_agr = match agrs.iter().find(|agr| { + agr.node_addr_opt.as_ref().map(|na| na.ip_addr()) == Some(gossip_source.ip()) + }) { Some(agr) => agr.clone(), None => { let message = format!( @@ -1138,12 +1143,12 @@ impl GossipHandler for StandardGossipHandler { ); warning!(self.logger, "{}", message); return vec![GossipAcceptanceResult::Ban(Malefactor::new( - None, - Some(gossip_source.ip()), - None, - None, - message, - ))] + None, + Some(gossip_source.ip()), + None, + None, + message, + ))]; } }; let patch = self.compute_patch(&agrs, database.root(), neighborhood_metadata.db_patch_size); @@ -3498,7 +3503,8 @@ mod tests { dest_db.add_arbitrary_full_neighbor(dest_root.public_key(), src_root.public_key()); src_db.add_node(dest_db.root().clone()).unwrap(); src_db.add_arbitrary_full_neighbor(src_root.public_key(), dest_root.public_key()); - let subject = StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); + let subject = + StandardGossipHandler::new(&RatePackLimits::test_default(), Logger::new(test_name)); let cryptde = CryptDENull::from(dest_db.root().public_key(), TEST_DEFAULT_CHAIN); let gossip_source: SocketAddr = src_root.node_addr_opt().unwrap().into(); let (cpm_recipient, _) = make_cpm_recipient(); @@ -3519,15 +3525,13 @@ mod tests { ); assert_eq!( handle_result, - vec![ - GossipAcceptanceResult::Ban(Malefactor::new( - None, - Some(gossip_source.ip()), - None, - None, - message.clone() - )), - ] + vec![GossipAcceptanceResult::Ban(Malefactor::new( + None, + Some(gossip_source.ip()), + None, + None, + message.clone() + )),] ); TestLogHandler::new().exists_log_containing(&format!("WARN: {}: {}", test_name, message)); } diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 6f64cf4b68..e679a18976 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -2252,6 +2252,9 @@ mod tests { use super::*; use crate::accountant::test_utils::bc_from_earning_wallet; use crate::bootstrapper::CryptDEPair; + use crate::database::db_initializer::{ + DbInitializationConfig, DbInitializer, DbInitializerReal, + }; use crate::neighborhood::malefactor::Malefactor; use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, @@ -2262,7 +2265,6 @@ mod tests { use crate::test_utils::database_utils::PersistentConfigurationFactoryTest; use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; - use crate::database::db_initializer::{DbInitializationConfig, DbInitializer, DbInitializerReal}; lazy_static! { static ref N_CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 29204cb494..fae6f4b3c4 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -175,7 +175,8 @@ impl RatePackLimits { } pub fn rate_pack_limits_parameter(&self) -> String { - format!("{}-{}|{}-{}|{}-{}|{}-{}", + format!( + "{}-{}|{}-{}|{}-{}|{}-{}", self.lo.routing_byte_rate, self.hi.routing_byte_rate, self.lo.routing_service_rate, @@ -1026,8 +1027,10 @@ mod tests { #[test] fn from_str_complains_about_blank_public_key() { - let result = - NodeDescriptor::try_from((NB_CRYPTDE_PAIR.main.as_ref(), "masq://dev:@1.2.3.4:1234/2345")); + let result = NodeDescriptor::try_from(( + NB_CRYPTDE_PAIR.main.as_ref(), + "masq://dev:@1.2.3.4:1234/2345", + )); assert_eq!(result, Err(String::from("Public key cannot be empty"))); }