From e6163afac599449b9b1a783074f1809c156cb525 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Sat, 3 Sep 2022 14:39:00 +0200 Subject: [PATCH] GH-613: cutting back on too many report consume messages jamming Accountant...version 2 with a fix (#167) * GH-613: half of messages to accrue debts handled * GH-613: preparing tests for handling reports to acountant regarding the return route * GH-613: continuing building structures for agreggated reporting; now at the response side...finalizing tests that will cover this change * GH-613: before trying to appeace tests with meaningless expected services * GH-613: old tests in proxy server passing; two todos left * GH-497: Accountant one more test fixed * GH-613: last tests in accountant were caressed and now sleep well * GH-613: last todos knocked out * GH-613: autoreview * GH-613: Revamped msg_id() * GH-613: wrote a test that could detect the wrong timestamp...and much more than just timestamp * GH-613: one version to fix it, will test alternatives * GH-613: slightly lighter version * GH-613: interim commit...I need to undress recorder and instead get it the assert message * GH-613: the lack of tests is gone * GH-613: ready for Actions * GH-613: save point before an experiment * GH-613-since-first-review: blind alley; though the coflict of mutability is nicely refactored now * GH-613-since-first-review: it works and looks prettier, let's go to have a party time * GH-613-since-first-review: finished refactoring of handle_IBCD * GH-613-since-first-review: fixed a couple of tests * GH-613-since-first-review: knocked out a few more requests from the review * GH-613-since-first-review: a few more features fixed; msg id generator edhancemend * GH-613-since-first-review: everything requested is fixed; now I might do some last refactoring before the review 2 * GH-613-since-first-review: finished, now review? * Gh 613 since first review (#168) * GH-613: save point before an experiment * GH-613-since-first-review: blind alley; though the coflict of mutability is nicely refactored now * GH-613-since-first-review: it works and looks prettier, let's go to have a party time * GH-613-since-first-review: finished refactoring of handle_IBCD * GH-613-since-first-review: fixed a couple of tests * GH-613-since-first-review: knocked out a few more requests from the review * GH-613-since-first-review: a few more features fixed; msg id generator edhancemend * GH-613-since-first-review: everything requested is fixed; now I might do some last refactoring before the review 2 * GH-613-since-first-review: finished, now review? Co-authored-by: Bert * GH-613-second-review: repaired tests in proxy_server/mod.rs which were sensitive on is_decentralized * GH-613-second-review: some minor changes, sweeping; should be done all * GH-613-second-review: changed versioning of crates round the codebase Co-authored-by: Bert Co-authored-by: Dan Wiebe --- automap/Cargo.lock | 4 +- automap/Cargo.toml | 2 +- dns_utility/Cargo.lock | 4 +- dns_utility/Cargo.toml | 2 +- masq/Cargo.toml | 2 +- masq_lib/Cargo.toml | 2 +- masq_lib/src/logger.rs | 2 +- masq_lib/src/utils.rs | 20 + multinode_integration_tests/Cargo.toml | 2 +- node/Cargo.lock | 10 +- node/Cargo.toml | 2 +- node/src/accountant/mod.rs | 604 +++--- node/src/accountant/test_utils.rs | 22 +- node/src/accountant/tools.rs | 21 +- node/src/actor_system_factory.rs | 2 +- node/src/blockchain/blockchain_bridge.rs | 14 +- node/src/neighborhood/mod.rs | 2 + .../client_request_payload_factory.rs | 47 +- node/src/proxy_server/mod.rs | 1796 +++++++++-------- node/src/proxy_server/protocol_pack.rs | 31 +- node/src/proxy_server/utils.rs | 71 + node/src/sub_lib/accountant.rs | 77 +- node/src/sub_lib/utils.rs | 2 +- node/src/test_utils/recorder.rs | 9 +- port_exposer/Cargo.lock | 2 +- port_exposer/Cargo.toml | 2 +- 26 files changed, 1579 insertions(+), 1175 deletions(-) create mode 100644 node/src/proxy_server/utils.rs diff --git a/automap/Cargo.lock b/automap/Cargo.lock index eb3891677..c877524c0 100644 --- a/automap/Cargo.lock +++ b/automap/Cargo.lock @@ -125,7 +125,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "automap" -version = "0.6.1" +version = "0.6.2" dependencies = [ "crossbeam-channel 0.5.1", "flexi_logger", @@ -908,7 +908,7 @@ dependencies = [ [[package]] name = "masq_lib" -version = "0.6.1" +version = "0.6.2" dependencies = [ "actix", "clap", diff --git a/automap/Cargo.toml b/automap/Cargo.toml index eb1b85635..2cbc0fb0c 100644 --- a/automap/Cargo.toml +++ b/automap/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "automap" -version = "0.6.1" +version = "0.6.2" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" copyright = "Copyright (c) 2019-2021, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/dns_utility/Cargo.lock b/dns_utility/Cargo.lock index 69a7c3fef..fcf776fd9 100644 --- a/dns_utility/Cargo.lock +++ b/dns_utility/Cargo.lock @@ -407,7 +407,7 @@ dependencies = [ [[package]] name = "dns_utility" -version = "0.6.1" +version = "0.6.2" dependencies = [ "core-foundation", "ipconfig 0.2.2", @@ -811,7 +811,7 @@ dependencies = [ [[package]] name = "masq_lib" -version = "0.6.1" +version = "0.6.2" dependencies = [ "actix", "clap", diff --git a/dns_utility/Cargo.toml b/dns_utility/Cargo.toml index b860bbe57..b1937a5f3 100644 --- a/dns_utility/Cargo.toml +++ b/dns_utility/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "dns_utility" -version = "0.6.1" +version = "0.6.2" license = "GPL-3.0-only" authors = ["Dan Wiebe ", "MASQ"] copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/masq/Cargo.toml b/masq/Cargo.toml index 5a2769123..463517145 100644 --- a/masq/Cargo.toml +++ b/masq/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masq" -version = "0.6.1" +version = "0.6.2" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/masq_lib/Cargo.toml b/masq_lib/Cargo.toml index 636824281..ac04b12b2 100644 --- a/masq_lib/Cargo.toml +++ b/masq_lib/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "masq_lib" -version = "0.6.1" +version = "0.6.2" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/masq_lib/src/logger.rs b/masq_lib/src/logger.rs index 01c72cbc1..c2cde53b6 100644 --- a/masq_lib/src/logger.rs +++ b/masq_lib/src/logger.rs @@ -263,7 +263,7 @@ impl Logger { level <= self.level_limit } - pub fn set_level_for_a_test(&mut self, level: Level) { + pub fn set_level_for_test(&mut self, level: Level) { self.level_limit = level } } diff --git a/masq_lib/src/utils.rs b/masq_lib/src/utils.rs index 9f24cf706..35dbc6ce1 100644 --- a/masq_lib/src/utils.rs +++ b/masq_lib/src/utils.rs @@ -337,6 +337,26 @@ pub fn type_name_of(_examined: T) -> &'static str { std::any::type_name::() } +pub trait MutabilityConflictHelper +where + T: 'static, +{ + type Result; + + //note: you should not write your own impl of this defaulted method + fn help(&mut self, closure: F) -> Self::Result + where + F: FnOnce(&T, &mut Self) -> Self::Result, + { + let helper = self.helper_access().take().expectv("helper"); + let result = closure(&helper, self); + self.helper_access().replace(helper); + result + } + + fn helper_access(&mut self) -> &mut Option; +} + #[macro_export] macro_rules! short_writeln { ($dst:expr) => ( diff --git a/multinode_integration_tests/Cargo.toml b/multinode_integration_tests/Cargo.toml index adf0e4e52..43eeae2a5 100644 --- a/multinode_integration_tests/Cargo.toml +++ b/multinode_integration_tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "multinode_integration_tests" -version = "0.6.1" +version = "0.6.2" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/node/Cargo.lock b/node/Cargo.lock index 27fd0591a..a853de615 100644 --- a/node/Cargo.lock +++ b/node/Cargo.lock @@ -182,7 +182,7 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "automap" -version = "0.6.1" +version = "0.6.2" dependencies = [ "crossbeam-channel 0.5.1", "flexi_logger 0.17.1", @@ -1827,7 +1827,7 @@ dependencies = [ [[package]] name = "masq" -version = "0.6.1" +version = "0.6.2" dependencies = [ "clap", "crossbeam-channel 0.5.1", @@ -1844,7 +1844,7 @@ dependencies = [ [[package]] name = "masq_lib" -version = "0.6.1" +version = "0.6.2" dependencies = [ "actix", "clap", @@ -2020,7 +2020,7 @@ dependencies = [ [[package]] name = "multinode_integration_tests" -version = "0.6.1" +version = "0.6.2" dependencies = [ "base64 0.13.0", "crossbeam-channel 0.5.1", @@ -2114,7 +2114,7 @@ dependencies = [ [[package]] name = "node" -version = "0.6.1" +version = "0.6.2" dependencies = [ "actix", "automap", diff --git a/node/Cargo.toml b/node/Cargo.toml index 7e2180112..1a3b8f366 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "node" -version = "0.6.1" +version = "0.6.2" license = "GPL-3.0-only" authors = ["Dan Wiebe ", "MASQ"] copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved." diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f4de58fdf..297784c9c 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -24,12 +24,12 @@ use crate::blockchain::blockchain_interface::{BlockchainError, BlockchainTransac use crate::bootstrapper::BootstrapperConfig; use crate::database::dao_utils::DaoFactoryReal; use crate::database::db_migrations::MigratorConfig; -use crate::sub_lib::accountant::AccountantSubs; -use crate::sub_lib::accountant::ReportExitServiceConsumedMessage; -use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; -use crate::sub_lib::accountant::ReportRoutingServiceConsumedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; use crate::sub_lib::accountant::{AccountantConfig, FinancialStatistics, PaymentThresholds}; +use crate::sub_lib::accountant::{AccountantSubs, ReportServicesConsumedMessage}; +use crate::sub_lib::accountant::{ + MessageIdGenerator, MessageIdGeneratorReal, ReportExitServiceProvidedMessage, +}; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; use crate::sub_lib::peer_actors::{BindMessage, StartMessage}; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; @@ -73,7 +73,7 @@ pub struct Accountant { banned_dao: Box, crashable: bool, scanners: Scanners, - tools: TransactionConfirmationTools, + confirmation_tools: TransactionConfirmationTools, financial_statistics: FinancialStatistics, report_accounts_payable_sub: Option>, retrieve_transactions_sub: Option>, @@ -81,6 +81,7 @@ pub struct Accountant { report_sent_payments_sub: Option>, ui_message_sub: Option>, payable_threshold_tools: Box, + message_id_generator: Box, logger: Logger, } @@ -270,27 +271,15 @@ impl Handler for Accountant { } } -impl Handler for Accountant { - type Result = (); - - fn handle( - &mut self, - msg: ReportRoutingServiceConsumedMessage, - _ctx: &mut Self::Context, - ) -> Self::Result { - self.handle_report_routing_service_consumed_message(msg); - } -} - -impl Handler for Accountant { +impl Handler for Accountant { type Result = (); fn handle( &mut self, - msg: ReportExitServiceConsumedMessage, + msg: ReportServicesConsumedMessage, _ctx: &mut Self::Context, ) -> Self::Result { - self.handle_report_exit_service_consumed_message(msg); + self.handle_report_services_consumed_message(msg); } } @@ -420,13 +409,14 @@ impl Accountant { banned_dao: banned_dao_factory.make(), crashable: config.crash_point == CrashPoint::Message, scanners: Scanners::default(), - tools: TransactionConfirmationTools::default(), financial_statistics: FinancialStatistics::default(), report_accounts_payable_sub: None, retrieve_transactions_sub: None, report_new_payments_sub: None, report_sent_payments_sub: None, ui_message_sub: None, + confirmation_tools: TransactionConfirmationTools::default(), + message_id_generator: Box::new(MessageIdGeneratorReal::default()), payable_threshold_tools: Box::new(PayableExceedThresholdToolsReal::default()), logger: Logger::new("Accountant"), } @@ -438,8 +428,7 @@ impl Accountant { start: recipient!(addr, StartMessage), report_routing_service_provided: recipient!(addr, ReportRoutingServiceProvidedMessage), report_exit_service_provided: recipient!(addr, ReportExitServiceProvidedMessage), - report_routing_service_consumed: recipient!(addr, ReportRoutingServiceConsumedMessage), - report_exit_service_consumed: recipient!(addr, ReportExitServiceConsumedMessage), + report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_new_payments: recipient!(addr, ReceivedPayments), pending_payable_fingerprint: recipient!(addr, PendingPayableFingerprint), report_transaction_receipts: recipient!(addr, ReportTransactionReceipts), @@ -557,7 +546,7 @@ impl Accountant { "Found {} pending payables to process", filtered_pending_payable.len() ); - self.tools + self.confirmation_tools .request_transaction_receipts_subs_opt .as_ref() .expect("BlockchainBridge is unbound") @@ -639,9 +628,10 @@ impl Accountant { Err(e)=> panic!("Recording services provided for {} but has hit fatal database error: {:?}", wallet, e) }; } else { - info!( + warning!( self.logger, - "Not recording service provided for our wallet {}", wallet + "Declining to record a receivable against our wallet {} for service we provided", + wallet ); } } @@ -673,9 +663,10 @@ impl Accountant { Err(e) => panic!("Recording services consumed from {} but has hit fatal database error: {:?}", wallet, e) }; } else { - info!( + warning!( self.logger, - "Not recording service consumed to our wallet {}", wallet + "Declining to record a payable against our wallet {} for service we provided", + wallet ); } } @@ -689,6 +680,7 @@ impl Accountant { //for debugging only fn investigate_debt_extremes(all_non_pending_payables: &[PayableAccount]) -> String { + let now = SystemTime::now(); if all_non_pending_payables.is_empty() { "Payable scan found no debts".to_string() } else { @@ -696,7 +688,6 @@ impl Accountant { balance: i64, age: Duration, } - let now = SystemTime::now(); let init = ( PayableInfo { balance: 0, @@ -774,7 +765,8 @@ impl Accountant { self.report_new_payments_sub = Some(msg.peer_actors.accountant.report_new_payments); self.report_sent_payments_sub = Some(msg.peer_actors.accountant.report_sent_payments); self.ui_message_sub = Some(msg.peer_actors.ui_gateway.node_to_ui_message_sub); - self.tools.request_transaction_receipts_subs_opt = Some( + self.confirmation_tools + .request_transaction_receipts_subs_opt = Some( msg.peer_actors .blockchain_bridge .request_transaction_receipts, @@ -883,42 +875,46 @@ impl Accountant { ); } - fn handle_report_routing_service_consumed_message( - &mut self, - msg: ReportRoutingServiceConsumedMessage, - ) { - debug!( - self.logger, - "Accruing debt to wallet {} for consuming routing service {} bytes", - msg.earning_wallet, - msg.payload_size - ); - self.record_service_consumed( - msg.service_rate, - msg.byte_rate, - msg.timestamp, - msg.payload_size, - &msg.earning_wallet, - ); + fn msg_id(&self) -> u32 { + if self.logger.debug_enabled() { + self.message_id_generator.id() + } else { + 0 + } } - fn handle_report_exit_service_consumed_message( - &mut self, - msg: ReportExitServiceConsumedMessage, - ) { + fn handle_report_services_consumed_message(&mut self, msg: ReportServicesConsumedMessage) { + let msg_id = self.msg_id(); debug!( self.logger, - "Accruing debt to wallet {} for consuming exit service {} bytes", - msg.earning_wallet, - msg.payload_size + "MsgId {}: Accruing debt to {} for consuming {} exited bytes", + msg_id, + msg.exit.earning_wallet, + msg.exit.payload_size ); self.record_service_consumed( - msg.service_rate, - msg.byte_rate, + msg.exit.service_rate, + msg.exit.byte_rate, msg.timestamp, - msg.payload_size, - &msg.earning_wallet, + msg.exit.payload_size, + &msg.exit.earning_wallet, ); + msg.routing.iter().for_each(|routing_service| { + debug!( + self.logger, + "MsgId {}: Accruing debt to {} for consuming {} routed bytes", + msg_id, + routing_service.earning_wallet, + msg.routing_payload_size + ); + self.record_service_consumed( + routing_service.service_rate, + routing_service.byte_rate, + msg.timestamp, + msg.routing_payload_size, + &routing_service.earning_wallet, + ); + }) } fn handle_financials(&mut self, client_id: u64, context_id: u64) { @@ -1168,7 +1164,7 @@ impl Accountant { transaction_id: PendingPayableId, ctx: &mut Context, ) { - self.tools + self.confirmation_tools .notify_cancel_failed_transaction .notify(CancelFailedPendingTransaction { id: transaction_id }, ctx) } @@ -1178,7 +1174,7 @@ impl Accountant { pending_payable_fingerprint: PendingPayableFingerprint, ctx: &mut Context, ) { - self.tools.notify_confirm_transaction.notify( + self.confirmation_tools.notify_confirm_transaction.notify( ConfirmPendingTransaction { pending_payable_fingerprint, }, @@ -1286,6 +1282,7 @@ mod tests { use actix::{Arbiter, System}; use ethereum_types::{BigEndianHash, U64}; use ethsign_crypto::Keccak256; + use log::Level; use masq_lib::constants::SCAN_ERROR; use web3::types::U256; @@ -1299,9 +1296,9 @@ mod tests { use crate::accountant::receivable_dao::ReceivableAccount; use crate::accountant::test_utils::{ bc_from_ac_plus_earning_wallet, bc_from_ac_plus_wallets, make_pending_payable_fingerprint, - make_receivable_account, BannedDaoFactoryMock, PayableDaoFactoryMock, PayableDaoMock, - PendingPayableDaoFactoryMock, PendingPayableDaoMock, ReceivableDaoFactoryMock, - ReceivableDaoMock, + make_receivable_account, BannedDaoFactoryMock, MessageIdGeneratorMock, + PayableDaoFactoryMock, PayableDaoMock, PendingPayableDaoFactoryMock, PendingPayableDaoMock, + ReceivableDaoFactoryMock, ReceivableDaoMock, }; use crate::accountant::test_utils::{AccountantBuilder, BannedDaoMock}; use crate::accountant::tools::accountant_tools::{NullScanner, ReceivablesScanner}; @@ -1315,7 +1312,7 @@ mod tests { use crate::database::dao_utils::from_time_t; use crate::database::dao_utils::to_time_t; use crate::sub_lib::accountant::{ - ReportRoutingServiceConsumedMessage, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, + ExitServiceConsumed, RoutingServiceConsumed, ScanIntervals, DEFAULT_PAYMENT_THRESHOLDS, }; use crate::sub_lib::blockchain_bridge::ReportAccountsPayable; use crate::sub_lib::utils::{NotifyHandleReal, NotifyLaterHandleReal}; @@ -1471,7 +1468,7 @@ mod tests { banned_dao_factory, ); - let transaction_confirmation_tools = result.tools; + let transaction_confirmation_tools = result.confirmation_tools; transaction_confirmation_tools .notify_confirm_transaction .as_any() @@ -1512,6 +1509,11 @@ mod tests { assert_eq!(result.crashable, false); assert_eq!(result.financial_statistics.total_paid_receivable, 0); assert_eq!(result.financial_statistics.total_paid_payable, 0); + result + .message_id_generator + .as_any() + .downcast_ref::() + .unwrap(); } #[test] @@ -2277,7 +2279,7 @@ mod tests { .build(); subject.scanners.pending_payables = Box::new(NullScanner); subject.scanners.payables = Box::new(NullScanner); - subject.tools.notify_later_scan_for_receivable = Box::new( + subject.confirmation_tools.notify_later_scan_for_receivable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_receivable_params_arc) .permit_to_send_out(), @@ -2393,7 +2395,9 @@ mod tests { .build(); subject.scanners.receivables = Box::new(NullScanner); //skipping subject.scanners.payables = Box::new(NullScanner); //skipping - subject.tools.notify_later_scan_for_pending_payable = Box::new( + subject + .confirmation_tools + .notify_later_scan_for_pending_payable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_pending_payable_params_arc) .permit_to_send_out(), @@ -2494,7 +2498,7 @@ mod tests { .build(); subject.scanners.pending_payables = Box::new(NullScanner); //skipping subject.scanners.receivables = Box::new(NullScanner); //skipping - subject.tools.notify_later_scan_for_payable = Box::new( + subject.confirmation_tools.notify_later_scan_for_payable = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_payables_params_arc) .permit_to_send_out(), @@ -2836,8 +2840,9 @@ mod tests { .bootstrapper_config(config) .build(); let blockchain_bridge_addr = blockchain_bridge.start(); - subject.tools.request_transaction_receipts_subs_opt = - Some(blockchain_bridge_addr.recipient()); + subject + .confirmation_tools + .request_transaction_receipts_subs_opt = Some(blockchain_bridge_addr.recipient()); let account_addr = subject.start(); let _ = account_addr @@ -2956,7 +2961,7 @@ mod tests { .is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service provided for our wallet {}", + "WARN: Accountant: Declining to record a receivable against our wallet {} for service we provided", consuming_wallet, )); } @@ -3004,147 +3009,11 @@ mod tests { .is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service provided for our wallet {}", + "WARN: Accountant: Declining to record a receivable against our wallet {} for service we provided", earning_wallet, )); } - #[test] - fn report_routing_service_consumed_message_is_received() { - init_test_logging(); - let now = SystemTime::now(); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - make_wallet("hi"), - ); - let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); - let payable_dao_mock = PayableDaoMock::new() - .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()) - .more_money_payable_result(Ok(())); - let subject = AccountantBuilder::default() - .bootstrapper_config(config) - .payable_dao(payable_dao_mock) - .build(); - let system = System::new("report_routing_service_consumed_message_is_received"); - let subject_addr: Addr = subject.start(); - subject_addr - .try_send(BindMessage { - peer_actors: peer_actors_builder().build(), - }) - .unwrap(); - - let earning_wallet = make_wallet("booga"); - subject_addr - .try_send(ReportRoutingServiceConsumedMessage { - timestamp: now, - earning_wallet: earning_wallet.clone(), - payload_size: 1234, - service_rate: 42, - byte_rate: 24, - }) - .unwrap(); - - System::current().stop_with_code(0); - system.run(); - let more_money_payable_parameters = more_money_payable_parameters_arc.lock().unwrap(); - assert_eq!( - more_money_payable_parameters[0], - (now, make_wallet("booga"), (1 * 42) + (1234 * 24)) - ); - TestLogHandler::new().exists_log_containing( - &format!("DEBUG: Accountant: Accruing debt to wallet {} for consuming routing service 1234 bytes", earning_wallet), - ); - } - - #[test] - fn report_routing_service_consumed_message_is_received_for_our_consuming_wallet() { - init_test_logging(); - let consuming_wallet = make_wallet("the consuming wallet"); - let config = bc_from_ac_plus_wallets( - make_populated_accountant_config_with_defaults(), - consuming_wallet.clone(), - make_wallet("the earning wallet"), - ); - let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); - let payable_dao_mock = PayableDaoMock::new() - .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()); - let subject = AccountantBuilder::default() - .bootstrapper_config(config) - .payable_dao(payable_dao_mock) - .build(); - let system = System::new("report_routing_service_consumed_message_is_received"); - let subject_addr: Addr = subject.start(); - subject_addr - .try_send(BindMessage { - peer_actors: peer_actors_builder().build(), - }) - .unwrap(); - - subject_addr - .try_send(ReportRoutingServiceConsumedMessage { - timestamp: SystemTime::now(), - earning_wallet: consuming_wallet.clone(), - payload_size: 1234, - service_rate: 42, - byte_rate: 24, - }) - .unwrap(); - - System::current().stop_with_code(0); - system.run(); - assert!(more_money_payable_parameters_arc.lock().unwrap().is_empty()); - - TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service consumed to our wallet {}", - consuming_wallet, - )); - } - - #[test] - fn report_routing_service_consumed_message_is_received_for_our_earning_wallet() { - init_test_logging(); - let earning_wallet = make_wallet("the earning wallet"); - let config = bc_from_ac_plus_earning_wallet( - make_populated_accountant_config_with_defaults(), - earning_wallet.clone(), - ); - let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); - let payable_dao_mock = PayableDaoMock::new() - .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()); - let subject = AccountantBuilder::default() - .bootstrapper_config(config) - .payable_dao(payable_dao_mock) - .build(); - let system = System::new("report_routing_service_consumed_message_is_received"); - let subject_addr: Addr = subject.start(); - subject_addr - .try_send(BindMessage { - peer_actors: peer_actors_builder().build(), - }) - .unwrap(); - - subject_addr - .try_send(ReportRoutingServiceConsumedMessage { - timestamp: SystemTime::now(), - earning_wallet: earning_wallet.clone(), - payload_size: 1234, - service_rate: 42, - byte_rate: 24, - }) - .unwrap(); - - System::current().stop_with_code(0); - system.run(); - assert!(more_money_payable_parameters_arc.lock().unwrap().is_empty()); - TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service consumed to our wallet {}", - earning_wallet - )); - } - #[test] fn report_exit_service_provided_message_is_received() { init_test_logging(); @@ -3239,7 +3108,7 @@ mod tests { .is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service provided for our wallet {}", + "WARN: Accountant: Declining to record a receivable against our wallet {} for service we provided", consuming_wallet )); } @@ -3285,76 +3154,118 @@ mod tests { .is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service provided for our wallet {}", + "WARN: Accountant: Declining to record a receivable against our wallet {} for service we provided", earning_wallet, )); } #[test] - fn report_exit_service_consumed_message_is_received() { + fn report_services_consumed_message_is_received() { init_test_logging(); - let now = SystemTime::now(); - let config = - bc_from_ac_plus_earning_wallet(make_accountant_config_null(), make_wallet("hi")); - let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); + let config = bc_from_ac_plus_earning_wallet( + make_populated_accountant_config_with_defaults(), + make_wallet("hi"), + ); + let more_money_payable_params_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new() - .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()) + .more_money_payable_params(more_money_payable_params_arc.clone()) + .more_money_payable_result(Ok(())) + .more_money_payable_result(Ok(())) .more_money_payable_result(Ok(())); - let subject = AccountantBuilder::default() + let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) .build(); - let system = System::new("report_exit_service_consumed_message_is_received"); + subject.message_id_generator = Box::new(MessageIdGeneratorMock::default().id_result(123)); + let system = System::new("report_services_consumed_message_is_received"); let subject_addr: Addr = subject.start(); subject_addr .try_send(BindMessage { peer_actors: peer_actors_builder().build(), }) .unwrap(); + let earning_wallet_exit = make_wallet("exit"); + let earning_wallet_routing_1 = make_wallet("routing 1"); + let earning_wallet_routing_2 = make_wallet("routing 2"); + let timestamp = SystemTime::now(); - let earning_wallet = make_wallet("booga"); subject_addr - .try_send(ReportExitServiceConsumedMessage { - timestamp: now, - earning_wallet: earning_wallet.clone(), - payload_size: 1234, - service_rate: 42, - byte_rate: 24, + .try_send(ReportServicesConsumedMessage { + timestamp, + exit: ExitServiceConsumed { + earning_wallet: earning_wallet_exit.clone(), + payload_size: 1200, + service_rate: 120, + byte_rate: 30, + }, + routing_payload_size: 3456, + routing: vec![ + RoutingServiceConsumed { + earning_wallet: earning_wallet_routing_1.clone(), + service_rate: 42, + byte_rate: 24, + }, + RoutingServiceConsumed { + earning_wallet: earning_wallet_routing_2.clone(), + service_rate: 52, + byte_rate: 33, + }, + ], }) .unwrap(); - System::current().stop_with_code(0); + System::current().stop(); system.run(); - let more_money_payable_parameters = more_money_payable_parameters_arc.lock().unwrap(); + let more_money_payable_params = more_money_payable_params_arc.lock().unwrap(); assert_eq!( - more_money_payable_parameters[0], - (now, make_wallet("booga"), (1 * 42) + (1234 * 24)) + more_money_payable_params + .iter() + .map(|(timestamp, wallet, amount)| (timestamp, wallet, amount)) + .collect::>(), + vec![ + (×tamp, &earning_wallet_exit, &((1 * 120) + (1200 * 30))), + ( + ×tamp, + &earning_wallet_routing_1, + &((1 * 42) + (3456 * 24)) + ), + ( + ×tamp, + &earning_wallet_routing_2, + &((1 * 52) + (3456 * 33)) + ) + ] ); - TestLogHandler::new().exists_log_containing(&format!( - "DEBUG: Accountant: Accruing debt to wallet {} for consuming exit service 1234 bytes", - earning_wallet + let test_log_handler = TestLogHandler::new(); + + test_log_handler.exists_log_containing(&format!( + "DEBUG: Accountant: MsgId 123: Accruing debt to {} for consuming 1200 exited bytes", + earning_wallet_exit + )); + test_log_handler.exists_log_containing(&format!( + "DEBUG: Accountant: MsgId 123: Accruing debt to {} for consuming 3456 routed bytes", + earning_wallet_routing_1 + )); + test_log_handler.exists_log_containing(&format!( + "DEBUG: Accountant: MsgId 123: Accruing debt to {} for consuming 3456 routed bytes", + earning_wallet_routing_2 )); } - #[test] - fn report_exit_service_consumed_message_is_received_for_our_consuming_wallet() { - init_test_logging(); - let consuming_wallet = make_wallet("own consuming wallet"); - let config = bc_from_ac_plus_wallets( - make_accountant_config_null(), - consuming_wallet.clone(), - make_wallet("own earning wallet"), - ); + fn assert_that_we_do_not_charge_our_own_wallet_for_consumed_services( + config: BootstrapperConfig, + message: ReportServicesConsumedMessage, + ) -> Arc>> { let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); let payable_dao_mock = PayableDaoMock::new() .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()); + .more_money_payable_result(Ok(())) + .more_money_payable_params(more_money_payable_parameters_arc.clone()); let subject = AccountantBuilder::default() .bootstrapper_config(config) .payable_dao(payable_dao_mock) .build(); - let system = System::new("report_exit_service_consumed_message_is_received"); + let system = System::new("test"); let subject_addr: Addr = subject.start(); subject_addr .try_send(BindMessage { @@ -3362,64 +3273,165 @@ mod tests { }) .unwrap(); - subject_addr - .try_send(ReportExitServiceConsumedMessage { - timestamp: SystemTime::now(), + subject_addr.try_send(message).unwrap(); + + System::current().stop(); + system.run(); + more_money_payable_parameters_arc + } + + #[test] + fn routing_service_consumed_is_reported_for_our_consuming_wallet() { + init_test_logging(); + let consuming_wallet = make_wallet("the consuming wallet"); + let config = bc_from_ac_plus_wallets( + make_populated_accountant_config_with_defaults(), + consuming_wallet.clone(), + make_wallet("the earning wallet"), + ); + let foreign_wallet = make_wallet("exit wallet"); + let timestamp = SystemTime::now(); + let report_message = ReportServicesConsumedMessage { + timestamp, + exit: ExitServiceConsumed { + earning_wallet: foreign_wallet.clone(), + payload_size: 1234, + service_rate: 45, + byte_rate: 10, + }, + routing_payload_size: 3333, + routing: vec![RoutingServiceConsumed { + earning_wallet: consuming_wallet.clone(), + service_rate: 42, + byte_rate: 6, + }], + }; + + let more_money_payable_params_arc = + assert_that_we_do_not_charge_our_own_wallet_for_consumed_services( + config, + report_message, + ); + + let more_money_payable_params = more_money_payable_params_arc.lock().unwrap(); + assert_eq!( + *more_money_payable_params, + //except processing the exit service there was no change in payables + vec![(timestamp, foreign_wallet, (45 + 10 * 1234))] + ); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: Accountant: Declining to record a payable against our wallet {} for service we provided", + consuming_wallet + )); + } + + #[test] + fn routing_service_consumed_is_reported_for_our_earning_wallet() { + init_test_logging(); + let earning_wallet = + make_wallet("routing_service_consumed_is_reported_for_our_earning_wallet"); + let foreign_wallet = make_wallet("exit wallet"); + let config = bc_from_ac_plus_earning_wallet( + make_populated_accountant_config_with_defaults(), + earning_wallet.clone(), + ); + let timestamp = SystemTime::now(); + let report_message = ReportServicesConsumedMessage { + timestamp, + exit: ExitServiceConsumed { + earning_wallet: foreign_wallet.clone(), + payload_size: 1234, + service_rate: 45, + byte_rate: 10, + }, + routing_payload_size: 3333, + routing: vec![RoutingServiceConsumed { + earning_wallet: earning_wallet.clone(), + service_rate: 42, + byte_rate: 6, + }], + }; + + let more_money_payable_params_arc = + assert_that_we_do_not_charge_our_own_wallet_for_consumed_services( + config, + report_message, + ); + + let more_money_payable_params = more_money_payable_params_arc.lock().unwrap(); + assert_eq!( + *more_money_payable_params, + //except processing the exit service there was no change in payables + vec![(timestamp, foreign_wallet, (45 + 10 * 1234))] + ); + TestLogHandler::new().exists_log_containing(&format!( + "WARN: Accountant: Declining to record a payable against our wallet {} for service we provided", + earning_wallet + )); + } + + #[test] + fn exit_service_consumed_is_reported_for_our_consuming_wallet() { + init_test_logging(); + let consuming_wallet = + make_wallet("exit_service_consumed_is_reported_for_our_consuming_wallet"); + let config = bc_from_ac_plus_wallets( + make_accountant_config_null(), + consuming_wallet.clone(), + make_wallet("own earning wallet"), + ); + let report_message = ReportServicesConsumedMessage { + timestamp: SystemTime::now(), + exit: ExitServiceConsumed { earning_wallet: consuming_wallet.clone(), payload_size: 1234, service_rate: 42, byte_rate: 24, - }) - .unwrap(); + }, + routing_payload_size: 3333, + routing: vec![], + }; - System::current().stop_with_code(0); - system.run(); - assert!(more_money_payable_parameters_arc.lock().unwrap().is_empty()); + let more_money_payable_params_arc = + assert_that_we_do_not_charge_our_own_wallet_for_consumed_services( + config, + report_message, + ); + assert!(more_money_payable_params_arc.lock().unwrap().is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service consumed to our wallet {}", + "WARN: Accountant: Declining to record a payable against our wallet {} for service we provided", consuming_wallet )); } #[test] - fn report_exit_service_consumed_message_is_received_for_our_earning_wallet() { + fn exit_service_consumed_is_reported_for_our_earning_wallet() { init_test_logging(); let earning_wallet = make_wallet("own earning wallet"); let config = bc_from_ac_plus_earning_wallet(make_accountant_config_null(), earning_wallet.clone()); - let more_money_payable_parameters_arc = Arc::new(Mutex::new(vec![])); - let payable_dao_mock = PayableDaoMock::new() - .non_pending_payables_result(vec![]) - .more_money_payable_parameters(more_money_payable_parameters_arc.clone()); - let subject = AccountantBuilder::default() - .bootstrapper_config(config) - .payable_dao(payable_dao_mock) - .build(); - let system = System::new("report_exit_service_consumed_message_is_received"); - let subject_addr: Addr = subject.start(); - subject_addr - .try_send(BindMessage { - peer_actors: peer_actors_builder().build(), - }) - .unwrap(); - - subject_addr - .try_send(ReportExitServiceConsumedMessage { - timestamp: SystemTime::now(), + let report_message = ReportServicesConsumedMessage { + timestamp: SystemTime::now(), + exit: ExitServiceConsumed { earning_wallet: earning_wallet.clone(), payload_size: 1234, service_rate: 42, byte_rate: 24, - }) - .unwrap(); + }, + routing_payload_size: 3333, + routing: vec![], + }; - System::current().stop_with_code(0); - system.run(); - assert!(more_money_payable_parameters_arc.lock().unwrap().is_empty()); + let more_money_payable_params_arc = + assert_that_we_do_not_charge_our_own_wallet_for_consumed_services( + config, + report_message, + ); + assert!(more_money_payable_params_arc.lock().unwrap().is_empty()); TestLogHandler::new().exists_log_containing(&format!( - "INFO: Accountant: Not recording service consumed to our wallet {}", + "WARN: Accountant: Declining to record a payable against our wallet {} for service we provided", earning_wallet )); } @@ -4131,16 +4143,18 @@ mod tests { let notify_later_half_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_later_scan_for_pending_payable_arc_cloned) .permit_to_send_out(); - subject.tools.notify_later_scan_for_pending_payable = - Box::new(notify_later_half_mock); + subject + .confirmation_tools + .notify_later_scan_for_pending_payable = Box::new(notify_later_half_mock); let notify_half_mock = NotifyHandleMock::default() .notify_params(¬ify_cancel_failed_transaction_params_arc_cloned) .permit_to_send_out(); - subject.tools.notify_cancel_failed_transaction = Box::new(notify_half_mock); + subject.confirmation_tools.notify_cancel_failed_transaction = + Box::new(notify_half_mock); let notify_half_mock = NotifyHandleMock::default() .notify_params(¬ify_confirm_transaction_params_arc_cloned) .permit_to_send_out(); - subject.tools.notify_confirm_transaction = Box::new(notify_half_mock); + subject.confirmation_tools.notify_confirm_transaction = Box::new(notify_half_mock); subject }); let mut peer_actors = peer_actors_builder().build(); @@ -4282,7 +4296,7 @@ mod tests { fn accountant_receives_reported_transaction_receipts_and_processes_them_all() { let notify_handle_params_arc = Arc::new(Mutex::new(vec![])); let mut subject = AccountantBuilder::default().build(); - subject.tools.notify_confirm_transaction = + subject.confirmation_tools.notify_confirm_transaction = Box::new(NotifyHandleMock::default().notify_params(¬ify_handle_params_arc)); let subject_addr = subject.start(); let transaction_hash_1 = H256::from_uint(&U256::from(4545)); @@ -4747,6 +4761,28 @@ mod tests { assert_eq!(*more_money_received_params, vec![(now, receivables)]); } + #[test] + #[cfg(not(feature = "no_test_share"))] + fn msg_id_generates_numbers_only_if_debug_log_enabled() { + let mut logger1 = Logger::new("msg_id_generator_off"); + logger1.set_level_for_test(Level::Info); + let mut subject = AccountantBuilder::default().build(); + let msg_id_generator = MessageIdGeneratorMock::default().id_result(789); //we prepared a result just for one call + subject.message_id_generator = Box::new(msg_id_generator); + subject.logger = logger1; + + let id1 = subject.msg_id(); + + let mut logger2 = Logger::new("msg_id_generator_on"); + logger2.set_level_for_test(Level::Debug); + subject.logger = logger2; + + let id2 = subject.msg_id(); + + assert_eq!(id1, 0); + assert_eq!(id2, 789); + } + #[test] fn unsigned_to_signed_handles_zero() { let result = unsigned_to_signed(0); diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index 962f9c606..33255d35b 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -20,7 +20,7 @@ use crate::database::dao_utils; use crate::database::dao_utils::{from_time_t, to_time_t}; use crate::db_config::config_dao::{ConfigDao, ConfigDaoFactory}; use crate::db_config::mocks::ConfigDaoMock; -use crate::sub_lib::accountant::{AccountantConfig, PaymentThresholds}; +use crate::sub_lib::accountant::{AccountantConfig, MessageIdGenerator, PaymentThresholds}; use crate::sub_lib::wallet::Wallet; use crate::test_utils::make_wallet; use crate::test_utils::unshared_test_utils::make_populated_accountant_config_with_defaults; @@ -344,7 +344,7 @@ impl PayableDaoMock { PayableDaoMock::default() } - pub fn more_money_payable_parameters( + pub fn more_money_payable_params( mut self, parameters: Arc>>, ) -> Self { @@ -830,6 +830,24 @@ pub fn make_pending_payable_fingerprint() -> PendingPayableFingerprint { } } +#[derive(Default)] +pub struct MessageIdGeneratorMock { + ids: RefCell>, +} + +impl MessageIdGenerator for MessageIdGeneratorMock { + fn id(&self) -> u32 { + self.ids.borrow_mut().remove(0) + } +} + +impl MessageIdGeneratorMock { + pub fn id_result(self, id: u32) -> Self { + self.ids.borrow_mut().push(id); + self + } +} + //warning: this test function will not tell you anything about the transaction record in the pending_payable table pub fn account_status(conn: &Connection, wallet: &Wallet) -> Option { let mut stmt = conn diff --git a/node/src/accountant/tools.rs b/node/src/accountant/tools.rs index fc873c8e2..399d9e536 100644 --- a/node/src/accountant/tools.rs +++ b/node/src/accountant/tools.rs @@ -42,7 +42,7 @@ pub(in crate::accountant) mod accountant_tools { } fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { let _ = accountant - .tools + .confirmation_tools .notify_later_scan_for_pending_payable .notify_later( ScanForPendingPayables { @@ -67,13 +67,16 @@ pub(in crate::accountant) mod accountant_tools { } fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { - let _ = accountant.tools.notify_later_scan_for_payable.notify_later( - ScanForPayables { - response_skeleton_opt: None, - }, - accountant.config.scan_intervals.payable_scan_interval, - ctx, - ); + let _ = accountant + .confirmation_tools + .notify_later_scan_for_payable + .notify_later( + ScanForPayables { + response_skeleton_opt: None, + }, + accountant.config.scan_intervals.payable_scan_interval, + ctx, + ); } as_any_impl!(); @@ -91,7 +94,7 @@ pub(in crate::accountant) mod accountant_tools { fn notify_later_assertable(&self, accountant: &Accountant, ctx: &mut Context) { let _ = accountant - .tools + .confirmation_tools .notify_later_scan_for_receivable .notify_later( ScanForReceivables { diff --git a/node/src/actor_system_factory.rs b/node/src/actor_system_factory.rs index c9237b38a..a8d1b0b36 100644 --- a/node/src/actor_system_factory.rs +++ b/node/src/actor_system_factory.rs @@ -399,7 +399,7 @@ impl ActorFactory for ActorFactoryReal { ) -> ProxyServerSubs { let is_decentralized = config.neighborhood_config.mode.is_decentralized(); let consuming_wallet_balance = if config.consuming_wallet_opt.is_some() { - Some(0) + Some(0) //TODO this is an old unfinished concept, repair or remove it...never used. } else { None }; diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 304fc380b..fb2892168 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -54,7 +54,7 @@ pub struct BlockchainBridge { } struct TransactionConfirmationTools { - transaction_backup_subs_opt: Option>, + transaction_fingerprint_subs_opt: Option>, report_transaction_receipts_sub_opt: Option>, } @@ -70,7 +70,7 @@ impl Handler for BlockchainBridge { msg.peer_actors.neighborhood.set_consuming_wallet_sub, msg.peer_actors.proxy_server.set_consuming_wallet_sub, ]); - self.payment_confirmation.transaction_backup_subs_opt = + self.payment_confirmation.transaction_fingerprint_subs_opt = Some(msg.peer_actors.accountant.pending_payable_fingerprint); self.payment_confirmation .report_transaction_receipts_sub_opt = @@ -179,7 +179,7 @@ impl BlockchainBridge { crashable, logger: Logger::new("BlockchainBridge"), payment_confirmation: TransactionConfirmationTools { - transaction_backup_subs_opt: None, + transaction_fingerprint_subs_opt: None, report_transaction_receipts_sub_opt: None, }, } @@ -405,7 +405,7 @@ impl BlockchainBridge { .expect("negative balance for qualified payable is nonsense"); let send_tools = self.blockchain_interface.send_transaction_tools( self.payment_confirmation - .transaction_backup_subs_opt + .transaction_fingerprint_subs_opt .as_ref() .expect("Accountant is unbound"), ); @@ -578,8 +578,10 @@ mod tests { response_skeleton_opt: None, }; let (accountant, _, _) = make_recorder(); - let backup_recipient = accountant.start().recipient(); - subject.payment_confirmation.transaction_backup_subs_opt = Some(backup_recipient); + let fingerprint_recipient = accountant.start().recipient(); + subject + .payment_confirmation + .transaction_fingerprint_subs_opt = Some(fingerprint_recipient); let result = subject.handle_report_accounts_payable_inner(&request); diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index a883c6178..ecd6e98a5 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -138,6 +138,8 @@ impl Handler for Neighborhood { } } +//TODO comes across as basically dead code +// I think the idea was to supply the wallet if wallets hadn't been generated until recently during the ongoing Node's run impl Handler for Neighborhood { type Result = (); diff --git a/node/src/proxy_server/client_request_payload_factory.rs b/node/src/proxy_server/client_request_payload_factory.rs index a6dadccfe..1f8e45689 100644 --- a/node/src/proxy_server/client_request_payload_factory.rs +++ b/node/src/proxy_server/client_request_payload_factory.rs @@ -8,22 +8,28 @@ use crate::sub_lib::sequence_buffer::SequencedPacket; use crate::sub_lib::stream_key::StreamKey; use masq_lib::logger::Logger; -#[derive(Default)] -pub struct ClientRequestPayloadFactory {} +pub trait ClientRequestPayloadFactory { + fn make( + &self, + ibcd: &InboundClientData, + stream_key: StreamKey, + cryptde: &dyn CryptDE, + logger: &Logger, + ) -> Option; +} -impl ClientRequestPayloadFactory { - pub fn new() -> Self { - Self::default() - } +#[derive(Default)] +pub struct ClientRequestPayloadFactoryReal {} - pub fn make( +impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { + fn make( &self, ibcd: &InboundClientData, stream_key: StreamKey, cryptde: &dyn CryptDE, logger: &Logger, ) -> Option { - let protocol_pack = from_ibcd(ibcd, logger)?; + let protocol_pack = from_ibcd(ibcd).map_err(|e| error!(logger, "{}", e)).ok()?; let sequence_number = match ibcd.sequence_number { Some(sequence_number) => sequence_number, None => { @@ -59,6 +65,12 @@ impl ClientRequestPayloadFactory { } } +impl ClientRequestPayloadFactoryReal { + pub fn new() -> Self { + Self::default() + } +} + #[cfg(test)] mod tests { use super::*; @@ -85,7 +97,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -120,7 +132,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -174,7 +186,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -222,7 +234,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -257,7 +269,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -281,7 +293,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); @@ -302,8 +314,7 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject .make(&ibcd, make_meaningless_stream_key(), cryptde, &logger) @@ -326,13 +337,11 @@ mod tests { }; let cryptde = main_cryptde(); let logger = Logger::new("test"); - - let subject = ClientRequestPayloadFactory::new(); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); let result = subject.make(&ibcd, make_meaningless_stream_key(), cryptde, &logger); assert_eq!(result, None); - TestLogHandler::new().exists_log_containing( "ERROR: test: internal error: got IBCD with no sequence number and 4 bytes", ); diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 05624e82d..e68cfc4f4 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -6,14 +6,19 @@ pub mod protocol_pack; pub mod server_impersonator_http; pub mod server_impersonator_tls; pub mod tls_protocol_pack; +pub mod utils; -use crate::proxy_server::client_request_payload_factory::ClientRequestPayloadFactory; +use crate::proxy_server::client_request_payload_factory::{ + ClientRequestPayloadFactory, ClientRequestPayloadFactoryReal, +}; use crate::proxy_server::http_protocol_pack::HttpProtocolPack; use crate::proxy_server::protocol_pack::{from_ibcd, from_protocol, ProtocolPack}; +use crate::proxy_server::utils::local::{TTHCommonArgs, TTHLocalArgs, TTHMovableArgs}; +use crate::proxy_server::ExitServiceSearch::{Definite, ZeroHop}; use crate::stream_messages::NonClandestineAttributes; use crate::stream_messages::RemovedStreamType; -use crate::sub_lib::accountant::ReportExitServiceConsumedMessage; -use crate::sub_lib::accountant::ReportRoutingServiceConsumedMessage; +use crate::sub_lib::accountant::RoutingServiceConsumed; +use crate::sub_lib::accountant::{ExitServiceConsumed, ReportServicesConsumedMessage}; use crate::sub_lib::bidi_hashmap::BidiHashMap; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::PublicKey; @@ -23,7 +28,7 @@ use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; use crate::sub_lib::neighborhood::RouteQueryMessage; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::{ExpectedService, NodeRecordMetadataMessage}; -use crate::sub_lib::neighborhood::{ExpectedServices, RatePack, DEFAULT_RATE_PACK}; +use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; @@ -38,13 +43,14 @@ use crate::sub_lib::stream_key::StreamKey; use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::utils::{handle_ui_crash_request, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::wallet::Wallet; -use actix::Actor; use actix::Addr; use actix::Context; use actix::Handler; use actix::Recipient; +use actix::{Actor, MailboxError}; use masq_lib::logger::Logger; use masq_lib::ui_gateway::NodeFromUiMessage; +use masq_lib::utils::{ExpectValue, MutabilityConflictHelper}; use std::collections::HashMap; use std::net::SocketAddr; use std::rc::Rc; @@ -57,8 +63,7 @@ pub const RETURN_ROUTE_TTL: Duration = Duration::from_secs(120); struct ProxyServerOutSubs { dispatcher: Recipient, hopper: Recipient, - accountant_exit: Recipient, - accountant_routing: Recipient, + accountant: Recipient, route_source: Recipient, update_node_record_metadata: Recipient, add_return_route: Recipient, @@ -68,7 +73,7 @@ struct ProxyServerOutSubs { pub struct ProxyServer { subs: Option, - client_request_payload_factory: ClientRequestPayloadFactory, + client_request_payload_factory: Box, stream_key_factory: Box, keys_and_addrs: BidiHashMap, tunneled_hosts: HashMap, @@ -81,6 +86,7 @@ pub struct ProxyServer { logger: Logger, route_ids_to_return_routes: TtlHashMap, browser_proxy_sequence_offset: bool, + inbound_client_data_helper_opt: Option>, } impl Actor for ProxyServer { @@ -95,8 +101,7 @@ impl Handler for ProxyServer { let subs = ProxyServerOutSubs { dispatcher: msg.peer_actors.dispatcher.from_dispatcher_client, hopper: msg.peer_actors.hopper.from_hopper_client, - accountant_exit: msg.peer_actors.accountant.report_exit_service_consumed, - accountant_routing: msg.peer_actors.accountant.report_routing_service_consumed, + accountant: msg.peer_actors.accountant.report_services_consumed, route_source: msg.peer_actors.neighborhood.route_query, update_node_record_metadata: msg.peer_actors.neighborhood.update_node_record_metadata, add_return_route: msg.peer_actors.proxy_server.add_return_route, @@ -107,6 +112,9 @@ impl Handler for ProxyServer { } } +//TODO comes across as basically dead code +// I think the idea was to supply the wallet if wallets hadn't been generated until recently, without the need to kill the Node +// I also found out that there is a test for this, but it changes nothing on it's normally unused impl Handler for ProxyServer { type Result = (); @@ -126,8 +134,10 @@ impl Handler for ProxyServer { if msg.is_connect() { self.tls_connect(&msg); self.browser_proxy_sequence_offset = true; - } else { - self.handle_normal_client_data(msg, false); + } else if let Err(e) = + self.help(|helper, proxy| helper.handle_normal_client_data(proxy, msg, false)) + { + error!(self.logger, "{}", e) } } } @@ -215,7 +225,7 @@ impl ProxyServer { ) -> ProxyServer { ProxyServer { subs: None, - client_request_payload_factory: ClientRequestPayloadFactory::new(), + client_request_payload_factory: Box::new(ClientRequestPayloadFactoryReal::new()), stream_key_factory: Box::new(StreamKeyFactoryReal {}), keys_and_addrs: BidiHashMap::new(), tunneled_hosts: HashMap::new(), @@ -228,6 +238,7 @@ impl ProxyServer { logger: Logger::new("ProxyServer"), route_ids_to_return_routes: TtlHashMap::new(RETURN_ROUTE_TTL), browser_proxy_sequence_offset: false, + inbound_client_data_helper_opt: Some(Box::new(IBCDHelperReal {})), } } @@ -428,141 +439,6 @@ impl ProxyServer { .unwrap_or_else(|| panic!("{} unbound in ProxyServer", actor_name)) } - fn handle_normal_client_data(&mut self, msg: InboundClientData, retire_stream_key: bool) { - let route_source = self.out_subs("Neighborhood").route_source.clone(); - let hopper = self.out_subs("Hopper").hopper.clone(); - let accountant_exit_sub = self.out_subs("Accountant").accountant_exit.clone(); - let accountant_routing_sub = self.out_subs("Accountant").accountant_routing.clone(); - let dispatcher = self.out_subs("Dispatcher").dispatcher.clone(); - let add_return_route_sub = self.out_subs("ProxyServer").add_return_route.clone(); - let add_route_sub = self.out_subs("ProxyServer").add_route.clone(); - let stream_shutdown_sub = self.out_subs("ProxyServer").stream_shutdown_sub.clone(); - let source_addr = msg.peer_addr; - if self.consuming_wallet_balance.is_none() && self.is_decentralized { - let protocol_pack = match from_ibcd(&msg, &self.logger) { - None => return, - Some(pp) => pp, - }; - let data = protocol_pack - .server_impersonator() - .consuming_wallet_absent(); - let msg = TransmitDataMsg { - endpoint: Endpoint::Socket(source_addr), - last_data: true, - sequence_number: Some(0), - data, - }; - dispatcher.try_send(msg).expect("Dispatcher is dead"); - error!( - self.logger, - "Browser request rejected due to missing consuming wallet" - ); - return; - } - let stream_key = self.make_stream_key(&msg); - let timestamp = msg.timestamp; - let payload = match self.make_payload(msg, &stream_key) { - Ok(payload) => payload, - Err(_e) => { - return; - } - }; - let logger = self.logger.clone(); - let minimum_hop_count = if self.is_decentralized { - DEFAULT_MINIMUM_HOP_COUNT - } else { - 0 - }; - let cryptde = self.main_cryptde.dup(); - match self.stream_key_routes.get(&stream_key) { - Some(route_query_response) => { - debug!( - logger, - "Transmitting down existing stream {}: sequence {}, length {}", - stream_key, - payload.sequenced_packet.sequence_number, - payload.sequenced_packet.data.len() - ); - ProxyServer::try_transmit_to_hopper( - cryptde, - &hopper, - timestamp, - route_query_response.clone(), - payload, - logger, - source_addr, - &dispatcher, - &accountant_exit_sub, - &accountant_routing_sub, - &add_return_route_sub, - if retire_stream_key { - Some(&stream_shutdown_sub) - } else { - None - }, - ); - } - None => { - debug!(logger, - "Getting route and opening new stream with key {} to transmit: sequence {}, length {}", - stream_key, payload.sequenced_packet.sequence_number, payload.sequenced_packet.data.len() - ); - tokio::spawn( - route_source - .send(RouteQueryMessage::data_indefinite_route_request( - minimum_hop_count, - payload.sequenced_packet.data.len(), - )) - .then(move |route_result| { - match route_result { - Ok(Some(route_query_response)) => { - add_route_sub - .try_send(AddRouteMessage { - stream_key, - route: route_query_response.clone(), - }) - .expect("ProxyServer is dead"); - ProxyServer::try_transmit_to_hopper( - cryptde, - &hopper, - timestamp, - route_query_response, - payload, - logger, - source_addr, - &dispatcher, - &accountant_exit_sub, - &accountant_routing_sub, - &add_return_route_sub, - if retire_stream_key { - Some(&stream_shutdown_sub) - } else { - None - }, - ); - } - Ok(None) => { - ProxyServer::handle_route_failure( - payload, - &logger, - source_addr, - &dispatcher, - ); - } - Err(e) => { - error!( - logger, - "Neighborhood refused to answer route request: {:?}", e - ); - } - }; - Ok(()) - }), - ); - } - } - } - fn handle_stream_shutdown_msg(&mut self, msg: StreamShutdownMsg) { let nca = match msg.stream_type { RemovedStreamType::Clandestine => { @@ -570,13 +446,12 @@ impl ProxyServer { } RemovedStreamType::NonClandestine(nca) => nca, }; - let msg_peer_addr = msg.peer_addr; let stream_key = match self.keys_and_addrs.b_to_a(&msg.peer_addr) { None => { warning!( self.logger, "Received instruction to shut down nonexistent stream to peer {} - ignoring", - msg_peer_addr + msg.peer_addr ); return; } @@ -588,7 +463,7 @@ impl ProxyServer { "Reporting shutdown of {} to counterpart", &stream_key ); let ibcd = InboundClientData { - timestamp: SystemTime::UNIX_EPOCH, // TODO: Drive this in + timestamp: SystemTime::now(), peer_addr: msg.peer_addr, reception_port: Some(nca.reception_port), last_data: true, @@ -596,11 +471,15 @@ impl ProxyServer { sequence_number: Some(nca.sequence_number), data: vec![], }; - self.handle_normal_client_data(ibcd, true); + if let Err(e) = + self.help(|helper, proxy| helper.handle_normal_client_data(proxy, ibcd, true)) + { + error!(self.logger, "{}", e) + }; } else { debug!( self.logger, - "Retiring stream key {}: StreamShutdownMsg for peer {}", &stream_key, msg_peer_addr + "Retiring stream key {}: StreamShutdownMsg for peer {}", &stream_key, msg.peer_addr ); self.purge_stream_key(&stream_key); } @@ -641,7 +520,7 @@ impl ProxyServer { &mut self, ibcd: InboundClientData, stream_key: &StreamKey, - ) -> Result { + ) -> Result { let tunnelled_host = self.tunneled_hosts.get(stream_key); let new_ibcd = match tunnelled_host { Some(_) => InboundClientData { @@ -656,10 +535,7 @@ impl ProxyServer { self.alias_cryptde, &self.logger, ) { - None => { - error!(self.logger, "Couldn't create ClientRequestPayload"); - Err(()) - } + None => Err("Couldn't create ClientRequestPayload".to_string()), Some(payload) => match tunnelled_host { Some(hostname) => Ok(ClientRequestPayload_0v1 { target_hostname: Some(hostname.clone()), @@ -670,132 +546,106 @@ impl ProxyServer { } } - #[allow(clippy::unnecessary_wraps)] - #[allow(clippy::too_many_arguments)] - fn try_transmit_to_hopper( - cryptde: Box, - hopper: &Recipient, - timestamp: SystemTime, - route_query_response: RouteQueryResponse, - payload: ClientRequestPayload_0v1, - logger: Logger, - source_addr: SocketAddr, - dispatcher: &Recipient, - accountant_exit_sub: &Recipient, - accountant_routing_sub: &Recipient, - add_return_route_sub: &Recipient, - retire_stream_key_via: Option<&Recipient>, - ) { + fn try_transmit_to_hopper(args: TTHLocalArgs, route_query_response: RouteQueryResponse) { match route_query_response.expected_services { ExpectedServices::RoundTrip(over, back, return_route_id) => { let return_route_info = AddReturnRouteMessage { return_route_id, expected_services: back, - protocol: payload.protocol, - server_name: payload.target_hostname.clone(), + protocol: args.common.payload.protocol, + server_name: args.common.payload.target_hostname.clone(), }; debug!( - logger, + args.logger, "Adding expectant return route info: {:?}", return_route_info ); - add_return_route_sub + args.add_return_route_sub .try_send(return_route_info) .expect("ProxyServer is dead"); - ProxyServer::report_exit_service( - accountant_exit_sub, - over.clone(), - timestamp, - &payload, - &logger, - ); ProxyServer::transmit_to_hopper( - cryptde, - hopper, - timestamp, - payload, + args.common.cryptde, + args.hopper_sub, + args.common.timestamp, + args.common.payload, &route_query_response.route, over, - &logger, - source_addr, - dispatcher, - accountant_routing_sub, - retire_stream_key_via, + args.logger, + args.common.source_addr, + args.dispatcher_sub, + args.accountant_sub, + args.retire_stream_key_sub_opt, + args.common.is_decentralized, ); } _ => panic!("Expected RoundTrip ExpectedServices but got OneWay"), } } - fn report_routing_service( - accountant_routing_sub: &Recipient, + fn report_on_routing_services( expected_services: Vec, - timestamp: SystemTime, - payload_size: usize, logger: &Logger, - ) { - let earning_wallets_and_rates: Vec<(&Wallet, &RatePack)> = expected_services - .iter() + ) -> Vec { + let report_of_routing_services: Vec = expected_services + .into_iter() .filter_map(|service| match service { ExpectedService::Routing(_, earning_wallet, rate_pack) => { - Some((earning_wallet, rate_pack)) + Some(RoutingServiceConsumed { + earning_wallet, + service_rate: rate_pack.routing_service_rate, + byte_rate: rate_pack.routing_byte_rate, + }) } _ => None, }) .collect(); - if earning_wallets_and_rates.is_empty() { + if report_of_routing_services.is_empty() { debug!(logger, "No routing services requested."); } - earning_wallets_and_rates - .into_iter() - .for_each(|(earning_wallet, rate_pack)| { - let report_routing_service_consumed = ReportRoutingServiceConsumedMessage { - timestamp, - earning_wallet: earning_wallet.clone(), - payload_size, - service_rate: rate_pack.routing_service_rate, - byte_rate: rate_pack.routing_byte_rate, - }; - accountant_routing_sub - .try_send(report_routing_service_consumed) - .expect("Accountant is dead"); - }); + report_of_routing_services } - fn report_exit_service( - accountant_exit_sub: &Recipient, - expected_services: Vec, - timestamp: SystemTime, - payload: &ClientRequestPayload_0v1, - logger: &Logger, - ) { - match expected_services - .iter() - .find_map(|expected_service| match expected_service { - ExpectedService::Exit(_, earning_wallet, rate_pack) => { - Some((earning_wallet, rate_pack)) + fn report_on_exit_service( + expected_services: &[ExpectedService], + payload_size: usize, + ) -> ExitServiceConsumed { + match expected_services.iter().fold( + None, + |acc: Option<(&Wallet, &RatePack)>, current_service| { + if acc.is_some() && matches!(current_service, ExpectedService::Exit(..)) { + panic!( + "Detected more than one exit service in one-way route: {:?}", + expected_services + ) + } else if acc.is_none() { + match current_service { + ExpectedService::Exit(_, earning_wallet, rate_pack) => { + Some((earning_wallet, rate_pack)) + } + _ => None, + } + } else { + acc } - _ => None, - }) { - Some((earning_wallet, rate_pack)) => { - let payload_size = payload.sequenced_packet.data.len(); - let report_exit_service_consumed_message = ReportExitServiceConsumedMessage { - timestamp, - earning_wallet: earning_wallet.clone(), - payload_size, - service_rate: rate_pack.exit_service_rate, - byte_rate: rate_pack.exit_byte_rate, - }; - accountant_exit_sub - .try_send(report_exit_service_consumed_message) - .expect("Accountant is dead"); + }, + ) { + Some((earning_wallet, rate_pack)) => ExitServiceConsumed { + earning_wallet: earning_wallet.clone(), + payload_size, + service_rate: rate_pack.exit_service_rate, + byte_rate: rate_pack.exit_byte_rate, + }, + None => { + panic!( + "Each route must demand an exit service, but this route has no such demand: {:?}", + expected_services + ) } - None => debug!(logger, "No exit service requested."), - }; + } } #[allow(clippy::too_many_arguments)] fn transmit_to_hopper( - cryptde: Box, + cryptde: &'static dyn CryptDE, hopper: &Recipient, timestamp: SystemTime, payload: ClientRequestPayload_0v1, @@ -804,22 +654,18 @@ impl ProxyServer { logger: &Logger, source_addr: SocketAddr, dispatcher: &Recipient, - accountant_routing_sub: &Recipient, + accountant_sub: &Recipient, retire_stream_key_via: Option<&Recipient>, + is_decentralized: bool, ) { - let destination_key_opt = if !expected_services.is_empty() - && expected_services - .iter() - .all(|expected_service| matches!(expected_service, ExpectedService::Nothing)) - { - Some(payload.originator_public_key.clone()) - } else { + let destination_key_opt = if is_decentralized { expected_services.iter().find_map(|service| match service { ExpectedService::Exit(public_key, _, _) => Some(public_key.clone()), _ => None, }) + } else { + Some(payload.originator_public_key.clone()) }; - match destination_key_opt { None => ProxyServer::handle_route_failure(payload, logger, source_addr, dispatcher), Some(payload_destination_key) => { @@ -827,21 +673,29 @@ impl ProxyServer { logger, "transmit to hopper with destination key {:?}", payload_destination_key ); + let payload_size = payload.sequenced_packet.data.len(); let stream_key = payload.stream_key; let pkg = IncipientCoresPackage::new( - cryptde.as_ref(), + cryptde, route.clone(), payload.into(), &payload_destination_key, ) .expect("Key magically disappeared"); - ProxyServer::report_routing_service( - accountant_routing_sub, - expected_services, - timestamp, - pkg.payload.len(), - logger, - ); + if is_decentralized { + let exit = + ProxyServer::report_on_exit_service(&expected_services, payload_size); + let routing = + ProxyServer::report_on_routing_services(expected_services, logger); + accountant_sub + .try_send(ReportServicesConsumedMessage { + timestamp, + exit, + routing_payload_size: pkg.payload.len(), + routing, + }) + .expect("Accountant is dead"); + } hopper.try_send(pkg).expect("Hopper is dead"); if let Some(shutdown_sub) = retire_stream_key_via { debug!( @@ -928,41 +782,232 @@ impl ProxyServer { exit_size: usize, routing_size: usize, ) { - return_route_info + let exit_service_report: ExitServiceSearch = return_route_info .expected_services .iter() - .for_each(|service| match service { - ExpectedService::Nothing => (), - ExpectedService::Exit(_, wallet, rate_pack) => self - .subs - .as_ref() - .expect("ProxyServer unbound") - .accountant_exit - .try_send(ReportExitServiceConsumedMessage { - timestamp: SystemTime::now(), - earning_wallet: wallet.clone(), - payload_size: exit_size, - service_rate: rate_pack.exit_service_rate, - byte_rate: rate_pack.exit_byte_rate, - }) - .expect("Accountant is dead"), - ExpectedService::Routing(_, wallet, _rate_pack) => self - .subs - .as_ref() - .expect("ProxyServer unbound") - .accountant_routing - .try_send(ReportRoutingServiceConsumedMessage { - timestamp: SystemTime::now(), - earning_wallet: wallet.clone(), - payload_size: routing_size, - service_rate: DEFAULT_RATE_PACK.routing_service_rate, - byte_rate: DEFAULT_RATE_PACK.routing_byte_rate, - }) - .expect("Accountant is dead"), + .filter(|service| !matches!(service, ExpectedService::Nothing)) + .fold(ZeroHop, |acc, service| { + if let Definite(..) = acc { + acc + } else { + match service { + ExpectedService::Exit(_, wallet, rate_pack) => { + Definite(ExitServiceConsumed { + earning_wallet: wallet.clone(), //sadly, the whole data structure is a reference + payload_size: exit_size, + service_rate: rate_pack.exit_service_rate, + byte_rate: rate_pack.exit_byte_rate, + }) + } + _ => unreachable!( + "Return route has to begin with an exit service if not zero hop" + ), + } + } }); + let exit_service_report = match exit_service_report { + ZeroHop => return, + Definite(report) => report, + }; + let routing_service_reports = return_route_info + .expected_services + .iter() + .flat_map(|service| match service { + ExpectedService::Routing(_, wallet, rate_pack) => Some(RoutingServiceConsumed { + earning_wallet: wallet.clone(), + service_rate: rate_pack.routing_service_rate, + byte_rate: rate_pack.routing_byte_rate, + }), + _ => None, + }) + .collect::>(); + let report_message = ReportServicesConsumedMessage { + timestamp: SystemTime::now(), + exit: exit_service_report, + routing_payload_size: routing_size, + routing: routing_service_reports, + }; + self.subs + .as_ref() + .expect("Accountant is unbound") + .accountant + .try_send(report_message) + .expect("Accountant is dead"); + } +} + +impl MutabilityConflictHelper> for ProxyServer { + type Result = Result<(), String>; + + fn helper_access(&mut self) -> &mut Option> { + &mut self.inbound_client_data_helper_opt } } +pub trait IBCDHelper { + fn handle_normal_client_data( + &self, + proxy_s: &mut ProxyServer, + msg: InboundClientData, + retire_stream_key: bool, + ) -> Result<(), String>; +} + +struct IBCDHelperReal {} + +impl IBCDHelper for IBCDHelperReal { + fn handle_normal_client_data( + &self, + proxy: &mut ProxyServer, + msg: InboundClientData, + retire_stream_key: bool, + ) -> Result<(), String> { + let source_addr = msg.peer_addr; + if proxy.consuming_wallet_balance.is_none() && proxy.is_decentralized { + let protocol_pack = match from_ibcd(&msg) { + Err(e) => return Err(e), + Ok(pp) => pp, + }; + let data = protocol_pack + .server_impersonator() + .consuming_wallet_absent(); + let msg = TransmitDataMsg { + endpoint: Endpoint::Socket(source_addr), + last_data: true, + sequence_number: Some(0), + data, + }; + proxy + .out_subs("Dispatcher") + .dispatcher + .try_send(msg) + .expect("Dispatcher is dead"); + return Err("Browser request rejected due to missing consuming wallet".to_string()); + } + let stream_key = proxy.make_stream_key(&msg); + let timestamp = msg.timestamp; + let payload = match proxy.make_payload(msg, &stream_key) { + Ok(payload) => payload, + Err(e) => return Err(e), + }; + let local_args = TTHLocalArgs { + common: TTHCommonArgs { + cryptde: proxy.main_cryptde, + payload, + source_addr, + timestamp, + is_decentralized: proxy.is_decentralized, + }, + hopper_sub: &proxy.out_subs("Hopper").hopper, + logger: &proxy.logger, + dispatcher_sub: &proxy.out_subs("Dispatcher").dispatcher, + accountant_sub: &proxy.out_subs("Accountant").accountant, + add_return_route_sub: &proxy.out_subs("ProxyServer").add_return_route, + retire_stream_key_sub_opt: if retire_stream_key { + Some(&proxy.out_subs("ProxyServer").stream_shutdown_sub) + } else { + None + }, + }; + let pld = &local_args.common.payload; + if let Some(route_query_response) = proxy.stream_key_routes.get(&pld.stream_key) { + debug!( + proxy.logger, + "Transmitting down existing stream {}: sequence {}, length {}", + pld.stream_key, + pld.sequenced_packet.sequence_number, + pld.sequenced_packet.data.len() + ); + let route_query_response = route_query_response.clone(); + ProxyServer::try_transmit_to_hopper(local_args, route_query_response); + Ok(()) + } else { + let movable_args = TTHMovableArgs::from(local_args); + let route_source = proxy.out_subs("Neighborhood").route_source.clone(); + let add_route_sub = proxy.out_subs("ProxyServer").add_route.clone(); + Self::request_route_and_transmit(movable_args, route_source, add_route_sub) + } + } +} + +impl IBCDHelperReal { + fn request_route_and_transmit( + args: TTHMovableArgs, + route_source: Recipient, + add_route_sub: Recipient, + ) -> Result<(), String> { + let common_args = args.common_opt.as_ref().expectv("TTH common"); + let pld = &common_args.payload; + debug!( + args.logger, + "Getting route and opening new stream with key {} to transmit: sequence {}, length {}", + pld.stream_key, + pld.sequenced_packet.sequence_number, + pld.sequenced_packet.data.len() + ); + let payload_size = pld.sequenced_packet.data.len(); + tokio::spawn( + route_source + .send(RouteQueryMessage::data_indefinite_route_request( + if common_args.is_decentralized { + DEFAULT_MINIMUM_HOP_COUNT + } else { + 0 + }, + payload_size, + )) + .then(move |route_result| { + Self::resolve_route_query_response(args, add_route_sub, route_result); + Ok(()) + }), + ); + Ok(()) + } + + fn resolve_route_query_response( + mut args: TTHMovableArgs, + add_route_sub: Recipient, + route_result: Result, MailboxError>, + ) { + match route_result { + Ok(Some(route_query_response)) => { + add_route_sub + .try_send(AddRouteMessage { + stream_key: args + .common_opt + .as_ref() + .expectv("TTH common") + .payload + .stream_key, + route: route_query_response.clone(), + }) + .expect("ProxyServer is dead"); + ProxyServer::try_transmit_to_hopper((&mut args).into(), route_query_response) + } + Ok(None) => { + let tth_common = args.common_opt.take().expectv("tth common"); + ProxyServer::handle_route_failure( + tth_common.payload, + &args.logger, + tth_common.source_addr, + &args.dispatcher_sub, + ) + } + Err(e) => { + error!( + args.logger, + "Neighborhood refused to answer route request: {:?}", e + ); + } + } + } +} + +enum ExitServiceSearch { + Definite(ExitServiceConsumed), + ZeroHop, +} + trait StreamKeyFactory: Send { fn make(&self, public_key: &PublicKey, peer_addr: SocketAddr) -> StreamKey; } @@ -979,20 +1024,19 @@ impl StreamKeyFactory for StreamKeyFactoryReal { #[cfg(test)] mod tests { use super::*; - use crate::node_test_utils::check_timestamp; use crate::proxy_server::protocol_pack::ServerImpersonator; use crate::proxy_server::server_impersonator_http::ServerImpersonatorHttp; use crate::proxy_server::server_impersonator_tls::ServerImpersonatorTls; use crate::stream_messages::{NonClandestineAttributes, RemovedStreamType}; - use crate::sub_lib::accountant::ReportRoutingServiceConsumedMessage; + use crate::sub_lib::accountant::RoutingServiceConsumed; use crate::sub_lib::cryptde::{decodex, CryptData}; use crate::sub_lib::cryptde::{encodex, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::dispatcher::Component; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::neighborhood::ExpectedService; use crate::sub_lib::neighborhood::ExpectedServices; + use crate::sub_lib::neighborhood::{ExpectedService, DEFAULT_RATE_PACK}; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; use crate::sub_lib::proxy_server::ProxyProtocol; @@ -1001,18 +1045,16 @@ mod tests { use crate::sub_lib::sequence_buffer::SequencedPacket; use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::versioned_data::VersionedData; - use crate::sub_lib::wallet::Wallet; - use crate::test_utils::main_cryptde; use crate::test_utils::make_meaningless_stream_key; + use crate::test_utils::make_paying_wallet; use crate::test_utils::make_wallet; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; use crate::test_utils::recorder::Recorder; - use crate::test_utils::recorder::Recording; use crate::test_utils::unshared_test_utils::prove_that_crash_request_handler_is_hooked_up; use crate::test_utils::zero_hop_route_response; use crate::test_utils::{alias_cryptde, rate_pack}; - use crate::test_utils::{make_meaningless_route, make_paying_wallet}; + use crate::test_utils::{main_cryptde, make_meaningless_route}; use actix::System; use crossbeam_channel::unbounded; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; @@ -1022,7 +1064,7 @@ mod tests { use std::cell::RefCell; use std::net::SocketAddr; use std::str::FromStr; - use std::sync::{Arc, Mutex, MutexGuard}; + use std::sync::{Arc, Mutex}; use std::thread; use std::time::SystemTime; @@ -1034,23 +1076,18 @@ mod tests { const STANDARD_CONSUMING_WALLET_BALANCE: i64 = 0; - impl Default for ProxyServerOutSubs { - fn default() -> Self { - let recorder = Recorder::new(); - let addr = recorder.start(); - ProxyServerOutSubs { - dispatcher: addr.clone().recipient::(), - hopper: addr.clone().recipient::(), - accountant_exit: addr.clone().recipient::(), - accountant_routing: addr - .clone() - .recipient::(), - route_source: addr.clone().recipient::(), - update_node_record_metadata: addr.clone().recipient::(), - add_return_route: addr.clone().recipient::(), - add_route: addr.clone().recipient::(), - stream_shutdown_sub: addr.clone().recipient::(), - } + fn make_proxy_server_out_subs() -> ProxyServerOutSubs { + let recorder = Recorder::new(); + let addr = recorder.start(); + ProxyServerOutSubs { + dispatcher: recipient!(addr, TransmitDataMsg), + hopper: recipient!(addr, IncipientCoresPackage), + accountant: recipient!(addr, ReportServicesConsumedMessage), + route_source: recipient!(addr, RouteQueryMessage), + update_node_record_metadata: recipient!(addr, NodeRecordMetadataMessage), + add_return_route: recipient!(addr, AddReturnRouteMessage), + add_route: recipient!(addr, AddRouteMessage), + stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), } } @@ -1117,49 +1154,44 @@ mod tests { .unwrap() } - fn check_exit_report( - accountant_recording: &MutexGuard, - idx: usize, - before: SystemTime, - after: SystemTime, - wallet: &Wallet, - payload_size: usize, - rate_pack: RatePack, - ) { - let record = accountant_recording.get_record::(idx); - check_timestamp(before, record.timestamp, after); - assert_eq!( - record, - &ReportExitServiceConsumedMessage { - timestamp: record.timestamp, - earning_wallet: wallet.clone(), - payload_size, - service_rate: rate_pack.exit_service_rate, - byte_rate: rate_pack.exit_byte_rate, - } - ); + #[derive(Default)] + struct IBCDHelperMock { + handle_normal_client_data_params: Arc>>, + handle_normal_client_data_results: RefCell>>, } - fn check_routing_report( - accountant_recording: &MutexGuard, - idx: usize, - before: SystemTime, - after: SystemTime, - wallet: &Wallet, - payload_size: usize, - ) { - let record = accountant_recording.get_record::(idx); - check_timestamp(before, record.timestamp, after); - assert_eq!( - accountant_recording.get_record::(idx), - &ReportRoutingServiceConsumedMessage { - timestamp: record.timestamp, - earning_wallet: wallet.clone(), - payload_size, - service_rate: DEFAULT_RATE_PACK.routing_service_rate, - byte_rate: DEFAULT_RATE_PACK.routing_byte_rate, - } - ); + impl IBCDHelper for IBCDHelperMock { + fn handle_normal_client_data( + &self, + _proxy_s: &mut ProxyServer, + msg: InboundClientData, + retire_stream_key: bool, + ) -> Result<(), String> { + self.handle_normal_client_data_params + .lock() + .unwrap() + .push((msg, retire_stream_key)); + self.handle_normal_client_data_results + .borrow_mut() + .remove(0) + } + } + + impl IBCDHelperMock { + fn handle_normal_client_data_params( + mut self, + params: &Arc>>, + ) -> Self { + self.handle_normal_client_data_params = params.clone(); + self + } + + fn handle_normal_client_data_result(self, result: Result<(), String>) -> Self { + self.handle_normal_client_data_results + .borrow_mut() + .push(result); + self + } } #[test] @@ -1170,9 +1202,15 @@ mod tests { let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (hopper_mock, hopper_awaiter, hopper_log_arc) = make_recorder(); let (neighborhood_mock, _, neighborhood_recording_arc) = make_recorder(); - let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = neighborhood_mock.route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); let (proxy_server_mock, _, proxy_server_recording_arc) = make_recorder(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); @@ -1187,7 +1225,7 @@ mod tests { data: expected_data.clone(), }; let expected_http_request = PlainData::new(http_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -1204,7 +1242,7 @@ mod tests { main_cryptde, route.clone(), expected_payload.into(), - alias_cryptde.public_key(), + &destination_key, ) .unwrap(); let make_parameters_arc = Arc::new(Mutex::new(vec![])); @@ -1217,7 +1255,7 @@ mod tests { let mut subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1249,7 +1287,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request(0, 47) + &RouteQueryMessage::data_indefinite_route_request(DEFAULT_MINIMUM_HOP_COUNT, 47) ); let recording = proxy_server_recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); @@ -1259,16 +1297,20 @@ mod tests { fn proxy_server_receives_connect_responds_with_ok_and_stores_stream_key_and_hostname() { let main_cryptde = main_cryptde(); let alias_cryptde = alias_cryptde(); - let key = alias_cryptde.public_key(); let http_request = b"CONNECT https://realdomain.nu:443 HTTP/1.1\r\nHost: https://bunkjunk.wrong:443\r\n\r\n"; let (hopper_mock, hopper_awaiter, hopper_recording_arc) = make_recorder(); let (neighborhood_mock, _, neighborhood_recording_arc) = make_recorder(); - let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = neighborhood_mock.route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); + let route = Route { hops: vec![] }; let (dispatcher_mock, _, dispatcher_recording_arc) = make_recorder(); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let request_data = http_request.to_vec(); @@ -1281,7 +1323,6 @@ mod tests { is_clandestine: false, data: request_data.clone(), }; - let tunnelled_msg = InboundClientData { timestamp: SystemTime::now(), peer_addr: socket_addr.clone(), @@ -1291,14 +1332,12 @@ mod tests { is_clandestine: false, data: b"client hello".to_vec(), }; - let expected_tdm = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr.clone()), last_data: false, sequence_number: Some(0), data: b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), }; - let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -1309,12 +1348,15 @@ mod tests { target_hostname: Some(String::from("realdomain.nu")), target_port: 443, protocol: ProxyProtocol::TLS, - originator_public_key: key.clone(), + originator_public_key: alias_cryptde.public_key().clone(), }; - let expected_pkg = - IncipientCoresPackage::new(main_cryptde, route.clone(), expected_payload.into(), &key) - .unwrap(); - + let expected_pkg = IncipientCoresPackage::new( + main_cryptde, + route.clone(), + expected_payload.into(), + &destination_key, + ) + .unwrap(); let make_parameters_arc = Arc::new(Mutex::new(vec![])); let make_parameters_arc_thread = make_parameters_arc.clone(); @@ -1328,7 +1370,7 @@ mod tests { let mut subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1348,7 +1390,6 @@ mod tests { }); hopper_awaiter.await_message_count(1); - let dispatcher_recording = dispatcher_recording_arc.lock().unwrap(); let dispatcher_record = dispatcher_recording.get_record::(0); assert_eq!(dispatcher_record, &expected_tdm); @@ -1366,7 +1407,7 @@ mod tests { let neighborhood_record = neighborhood_recording.get_record::(0); assert_eq!( neighborhood_record, - &RouteQueryMessage::data_indefinite_route_request(0, 12) + &RouteQueryMessage::data_indefinite_route_request(DEFAULT_MINIMUM_HOP_COUNT, 12) ); } @@ -1379,7 +1420,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1486,7 +1527,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1558,7 +1599,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1880,9 +1921,15 @@ mod tests { let hopper_mock = Recorder::new(); let hopper_log_arc = hopper_mock.get_recording(); let hopper_awaiter = hopper_mock.get_awaiter(); - let neighborhood_mock = Recorder::new().route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = Recorder::new().route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let expected_data = http_request.to_vec(); @@ -1896,7 +1943,7 @@ mod tests { data: expected_data.clone(), }; let expected_http_request = PlainData::new(http_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -1913,7 +1960,7 @@ mod tests { main_cryptde, route.clone(), expected_payload.into(), - alias_cryptde.public_key(), + &destination_key, ) .unwrap(); thread::spawn(move || { @@ -1922,7 +1969,7 @@ mod tests { let mut subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -1955,9 +2002,15 @@ mod tests { let hopper_mock = Recorder::new(); let hopper_log_arc = hopper_mock.get_recording(); let hopper_awaiter = hopper_mock.get_awaiter(); - let neighborhood_mock = Recorder::new().route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); + let destination_key = PublicKey::from(&b"our destination"[..]); + let route_query_response = RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + }; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let expected_data = http_request.to_vec(); @@ -1971,7 +2024,7 @@ mod tests { data: expected_data.clone(), }; let expected_http_request = PlainData::new(http_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = route_query_response.route.clone(); let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -1986,22 +2039,22 @@ mod tests { }; let expected_pkg = IncipientCoresPackage::new( main_cryptde, - route.clone(), + route, expected_payload.into(), - alias_cryptde.public_key(), + &destination_key, ) .unwrap(); thread::spawn(move || { let stream_key_factory = StreamKeyFactoryMock::new(); // can't make any stream keys; shouldn't have to let system = System::new("proxy_server_applies_late_wallet_information"); - let mut subject = ProxyServer::new(main_cryptde, alias_cryptde, false, None, false); + let mut subject = ProxyServer::new(main_cryptde, alias_cryptde, true, None, false); subject.stream_key_factory = Box::new(stream_key_factory); subject.keys_and_addrs.insert(stream_key, socket_addr); + subject + .stream_key_routes + .insert(stream_key, route_query_response); let subject_addr: Addr = subject.start(); - let mut peer_actors = peer_actors_builder() - .hopper(hopper_mock) - .neighborhood(neighborhood_mock) - .build(); + let mut peer_actors = peer_actors_builder().hopper(hopper_mock).build(); peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); @@ -2213,14 +2266,15 @@ mod tests { let main_cryptde = main_cryptde(); let alias_cryptde = alias_cryptde(); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let route_query_response = Some(RouteQueryResponse { + let destination_key = PublicKey::from(&b"our destination"[..]); + let route_query_response = RouteQueryResponse { route: Route { hops: vec![] }, expected_services: ExpectedServices::RoundTrip( - vec![ExpectedService::Nothing], + vec![make_exit_service_from_key(destination_key.clone())], vec![], 1234, ), - }); + }; let (hopper_mock, hopper_awaiter, hopper_recording_arc) = make_recorder(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); @@ -2250,7 +2304,7 @@ mod tests { main_cryptde, Route { hops: vec![] }, expected_payload.into(), - &alias_cryptde.public_key().clone(), + &destination_key, ) .unwrap(); @@ -2272,7 +2326,7 @@ mod tests { subject_addr .try_send(AddRouteMessage { stream_key, - route: route_query_response.unwrap(), + route: route_query_response, }) .unwrap(); subject_addr.try_send(msg_from_dispatcher).unwrap(); @@ -2287,7 +2341,7 @@ mod tests { } #[test] - fn proxy_server_sends_message_to_accountant_for_request_routing_service_consumed() { + fn proxy_server_sends_message_to_accountant_about_all_services_consumed_on_the_route_over() { let cryptde = main_cryptde(); let now = SystemTime::now(); let exit_earning_wallet = make_wallet("exit earning wallet"); @@ -2299,6 +2353,7 @@ mod tests { let (proxy_server_mock, _, proxy_server_recording_arc) = make_recorder(); let routing_node_1_rate_pack = rate_pack(101); let routing_node_2_rate_pack = rate_pack(102); + let exit_node_rate_pack = rate_pack(103); let route_query_response = RouteQueryResponse { route: make_meaningless_route(), expected_services: ExpectedServices::RoundTrip( @@ -2317,23 +2372,23 @@ mod tests { ExpectedService::Exit( PublicKey::new(&[3]), exit_earning_wallet.clone(), - rate_pack(103), + exit_node_rate_pack, ), ], vec![ ExpectedService::Exit( PublicKey::new(&[3]), - exit_earning_wallet.clone(), + make_wallet("some wallet 1"), rate_pack(104), ), ExpectedService::Routing( PublicKey::new(&[2]), - route_2_earning_wallet.clone(), + make_wallet("some wallet 2"), rate_pack(105), ), ExpectedService::Routing( PublicKey::new(&[1]), - route_1_earning_wallet.clone(), + make_wallet("some wallet 3"), rate_pack(106), ), ExpectedService::Nothing, @@ -2341,16 +2396,17 @@ mod tests { 0, ), }; - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let expected_data = http_request.to_vec(); let system = - System::new("proxy_server_sends_message_to_accountant_for_routing_service_consumed"); + System::new("proxy_server_sends_message_to_accountant_for_all_services_consumed"); let peer_actors = peer_actors_builder() .accountant(accountant_mock) .hopper(hopper_mock) .proxy_server(proxy_server_mock) .build(); + let exit_payload_size = expected_data.len(); let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, false), @@ -2360,48 +2416,55 @@ mod tests { originator_public_key: PublicKey::new(b"originator_public_key"), }; let logger = Logger::new("test"); + let local_tth_args = TTHLocalArgs { + common: TTHCommonArgs { + cryptde, + payload, + source_addr, + timestamp: now, + is_decentralized: true, + }, + logger: &logger, + hopper_sub: &peer_actors.hopper.from_hopper_client, + dispatcher_sub: &peer_actors.dispatcher.from_dispatcher_client, + accountant_sub: &peer_actors.accountant.report_services_consumed, + add_return_route_sub: &peer_actors.proxy_server.add_return_route, + retire_stream_key_sub_opt: None, + }; - ProxyServer::try_transmit_to_hopper( - cryptde.dup(), - &peer_actors.hopper.from_hopper_client, - now, - route_query_response, - payload.clone(), - logger, - socket_addr, - &peer_actors.dispatcher.from_dispatcher_client, - &peer_actors.accountant.report_exit_service_consumed, - &peer_actors.accountant.report_routing_service_consumed, - &peer_actors.proxy_server.add_return_route, - None, - ); + ProxyServer::try_transmit_to_hopper(local_tth_args, route_query_response); System::current().stop(); system.run(); let recording = hopper_recording_arc.lock().unwrap(); let record = recording.get_record::(0); - let payload_enc = &record.payload; + let payload_enc_length = record.payload.len(); let recording = accountant_recording_arc.lock().unwrap(); - let record = recording.get_record::(1); + let record = recording.get_record::(0); + assert_eq!(recording.len(), 1); assert_eq!( record, - &ReportRoutingServiceConsumedMessage { + &ReportServicesConsumedMessage { timestamp: now, - earning_wallet: route_1_earning_wallet, - payload_size: payload_enc.len(), - service_rate: routing_node_1_rate_pack.routing_service_rate, - byte_rate: routing_node_1_rate_pack.routing_byte_rate, - } - ); - let record = recording.get_record::(2); - assert_eq!( - record, - &ReportRoutingServiceConsumedMessage { - timestamp: now, - earning_wallet: route_2_earning_wallet, - payload_size: payload_enc.len(), - service_rate: routing_node_2_rate_pack.routing_service_rate, - byte_rate: routing_node_2_rate_pack.routing_byte_rate, + exit: ExitServiceConsumed { + earning_wallet: exit_earning_wallet, + payload_size: exit_payload_size, + service_rate: exit_node_rate_pack.exit_service_rate, + byte_rate: exit_node_rate_pack.exit_byte_rate + }, + routing_payload_size: payload_enc_length, + routing: vec![ + RoutingServiceConsumed { + earning_wallet: route_1_earning_wallet, + service_rate: routing_node_1_rate_pack.routing_service_rate, + byte_rate: routing_node_1_rate_pack.routing_byte_rate, + }, + RoutingServiceConsumed { + earning_wallet: route_2_earning_wallet, + service_rate: routing_node_2_rate_pack.routing_service_rate, + byte_rate: routing_node_2_rate_pack.routing_byte_rate, + } + ] } ); let recording = proxy_server_recording_arc.lock().unwrap(); @@ -2422,7 +2485,7 @@ mod tests { 0, ), }; - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let expected_data = http_request.to_vec(); let system = @@ -2439,21 +2502,23 @@ mod tests { originator_public_key: PublicKey::new(b"originator_public_key"), }; let logger = Logger::new("test"); + let local_tth_args = TTHLocalArgs { + common: TTHCommonArgs { + cryptde, + payload, + source_addr, + timestamp: SystemTime::now(), + is_decentralized: false, + }, + logger: &logger, + hopper_sub: &peer_actors.hopper.from_hopper_client, + dispatcher_sub: &peer_actors.dispatcher.from_dispatcher_client, + accountant_sub: &peer_actors.accountant.report_services_consumed, + add_return_route_sub: &peer_actors.proxy_server.add_return_route, + retire_stream_key_sub_opt: Some(&peer_actors.proxy_server.stream_shutdown_sub), + }; - ProxyServer::try_transmit_to_hopper( - cryptde.dup(), - &peer_actors.hopper.from_hopper_client, - SystemTime::now(), - route_query_response, - payload.clone(), - logger, - socket_addr, - &peer_actors.dispatcher.from_dispatcher_client, - &peer_actors.accountant.report_exit_service_consumed, - &peer_actors.accountant.report_routing_service_consumed, - &peer_actors.proxy_server.add_return_route, - Some(&peer_actors.proxy_server.stream_shutdown_sub), - ); + ProxyServer::try_transmit_to_hopper(local_tth_args, route_query_response); System::current().stop(); system.run(); @@ -2472,7 +2537,7 @@ mod tests { assert_eq!( record, &StreamShutdownMsg { - peer_addr: socket_addr, + peer_addr: source_addr, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { reception_port: 0, sequence_number: 0, @@ -2487,11 +2552,20 @@ mod tests { init_test_logging(); let cryptde = main_cryptde(); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let (accountant_mock, _, accountant_log_arc) = make_recorder(); + let (accountant_mock, accountant_awaiter, _) = make_recorder(); let (neighborhood_mock, _, _) = make_recorder(); - let zero_hop_route_response = zero_hop_route_response(&cryptde.public_key(), cryptde); + let mut route_query_response = zero_hop_route_response(&cryptde.public_key(), cryptde); + route_query_response.expected_services = ExpectedServices::RoundTrip( + vec![ExpectedService::Exit( + cryptde.public_key().clone(), + make_wallet("exit wallet"), + rate_pack(3), + )], + vec![], + 0, + ); let neighborhood_mock = - neighborhood_mock.route_query_response(Some(zero_hop_route_response.clone())); + neighborhood_mock.route_query_response(Some(route_query_response.clone())); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = make_meaningless_stream_key(); let expected_data = http_request.to_vec(); @@ -2530,139 +2604,49 @@ mod tests { TestLogHandler::new() .await_log_containing("DEBUG: ProxyServer: No routing services requested.", 1000); - assert_eq!(accountant_log_arc.lock().unwrap().len(), 0); + //report about consumed services is sent anyway, exit service is mandatory ever + accountant_awaiter.await_message_count(1) } #[test] - fn proxy_server_sends_message_to_accountant_for_request_exit_service_consumed() { - let cryptde = main_cryptde(); - let now = SystemTime::now(); - let earning_wallet = make_wallet("earning wallet"); - let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let (accountant_mock, accountant_awaiter, accountant_log_arc) = make_recorder(); - let (neighborhood_mock, _, _) = make_recorder(); - let exit_node_rate_pack = rate_pack(101); - let neighborhood_mock = neighborhood_mock.route_query_response(Some(RouteQueryResponse { - route: make_meaningless_route(), - expected_services: ExpectedServices::RoundTrip( - vec![ - ExpectedService::Nothing, - ExpectedService::Exit( - PublicKey::new(&[3]), - earning_wallet.clone(), - exit_node_rate_pack, - ), - ], - vec![ - ExpectedService::Exit( - PublicKey::new(&[3]), - earning_wallet.clone(), - rate_pack(102), - ), - ExpectedService::Nothing, - ], - 0, - ), - })); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let stream_key = make_meaningless_stream_key(); - let expected_data = http_request.to_vec(); - let msg_from_dispatcher = InboundClientData { - timestamp: now, - peer_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), - last_data: true, - is_clandestine: false, - data: expected_data.clone(), - }; - thread::spawn(move || { - let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key); - let system = - System::new("proxy_server_sends_message_to_accountant_for_exit_service_consumed"); - let mut subject = ProxyServer::new( - cryptde, - alias_cryptde(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - ); - subject.stream_key_factory = Box::new(stream_key_factory); - let subject_addr: Addr = subject.start(); - let mut peer_actors = peer_actors_builder() - .accountant(accountant_mock) - .neighborhood(neighborhood_mock) - .build(); - peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - subject_addr.try_send(msg_from_dispatcher).unwrap(); - system.run(); - }); + #[should_panic( + expected = "Each route must demand an exit service, but this route has no such demand: [Routing(cm91dGluZ19rZXlfMQ, \ + Wallet { kind: Address(0x00000000726f7574696e675f77616c6c65745f31) }, RatePack { routing_byte_rate: 9, \ + routing_service_rate: 10, exit_byte_rate: 11, exit_service_rate: 12 })]" + )] + fn proxy_server_panics_when_exit_services_are_not_requested_in_non_zero_hop_mode() { + let expected_services = vec![ExpectedService::Routing( + PublicKey::from(&b"routing_key_1"[..]), + make_wallet("routing_wallet_1"), + rate_pack(8), + )]; - accountant_awaiter.await_message_count(1); - let recording = accountant_log_arc.lock().unwrap(); - let record = recording.get_record::(0); - assert_eq!( - record, - &ReportExitServiceConsumedMessage { - timestamp: now, - earning_wallet, - payload_size: expected_data.len(), - service_rate: exit_node_rate_pack.exit_service_rate, - byte_rate: exit_node_rate_pack.exit_byte_rate, - } - ); + ProxyServer::report_on_exit_service(&expected_services, 10000); } #[test] - fn proxy_server_logs_message_when_exit_services_are_not_requested() { - init_test_logging(); - let cryptde = main_cryptde(); - let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let (accountant_mock, _, accountant_log_arc) = make_recorder(); - let (neighborhood_mock, _, _) = make_recorder(); - let zero_hop_route_reponse = zero_hop_route_response(&cryptde.public_key(), cryptde); - let neighborhood_mock = - neighborhood_mock.route_query_response(Some(zero_hop_route_reponse.clone())); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let stream_key = make_meaningless_stream_key(); - let expected_data = http_request.to_vec(); - let msg_from_dispatcher = InboundClientData { - timestamp: SystemTime::now(), - peer_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), - last_data: true, - is_clandestine: false, - data: expected_data.clone(), - }; - thread::spawn(move || { - let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key); - let system = - System::new("proxy_server_logs_message_when_exit_services_are_not_consumed"); - let mut subject = ProxyServer::new( - cryptde, - alias_cryptde(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - ); - subject.stream_key_factory = Box::new(stream_key_factory); - let subject_addr: Addr = subject.start(); - let mut peer_actors = peer_actors_builder() - .accountant(accountant_mock) - .neighborhood(neighborhood_mock) - .build(); - peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - subject_addr.try_send(msg_from_dispatcher).unwrap(); - system.run(); - }); - - TestLogHandler::new() - .await_log_containing("DEBUG: ProxyServer: No exit service requested.", 1000); + #[should_panic( + expected = "Detected more than one exit service in one-way route: [Exit(ZXhpdCBrZXkgMQ, Wallet { kind: \ + Address(0x00000000000000657869742077616c6c65742031) }, RatePack { routing_byte_rate: 7, routing_service_rate: \ + 8, exit_byte_rate: 9, exit_service_rate: 10 }), Exit(ZXhpdCBrZXkgMg, Wallet { kind: \ + Address(0x00000000000000657869742077616c6c65742032) }, RatePack { routing_byte_rate: 6, routing_service_rate: \ + 7, exit_byte_rate: 8, exit_service_rate: 9 })]" + )] + fn proxy_server_panics_when_there_are_more_than_one_exit_services_in_the_route() { + let expected_services = vec![ + ExpectedService::Exit( + PublicKey::from(&b"exit key 1"[..]), + make_wallet("exit wallet 1"), + rate_pack(6), + ), + ExpectedService::Exit( + PublicKey::from(&b"exit key 2"[..]), + make_wallet("exit wallet 2"), + rate_pack(5), + ), + ]; - assert_eq!(accountant_log_arc.lock().unwrap().len(), 0); + ProxyServer::report_on_exit_service(&expected_services, 10000); } #[test] @@ -2768,23 +2752,59 @@ mod tests { protocol: ProxyProtocol::TLS, originator_public_key: cryptde.public_key().clone(), }; - let logger = Logger::new("ProxyServer"); - let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let logger = Logger::new("ProxyServer"); + let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let local_tth_args = TTHLocalArgs { + common: TTHCommonArgs { + cryptde, + payload, + source_addr, + timestamp: SystemTime::now(), + is_decentralized: true, + }, + logger: &logger, + hopper_sub: &peer_actors.hopper.from_hopper_client, + dispatcher_sub: &peer_actors.dispatcher.from_dispatcher_client, + accountant_sub: &peer_actors.accountant.report_services_consumed, + add_return_route_sub: &peer_actors.proxy_server.add_return_route, + retire_stream_key_sub_opt: None, + }; + + ProxyServer::try_transmit_to_hopper(local_tth_args, route_result); + } + + #[test] + #[should_panic(expected = "Return route has to begin with an exit service if not zero hop")] + fn report_response_services_consumed_does_not_allow_for_other_order_than_started_at_exit_service( + ) { + let main_cryptde = main_cryptde(); + let alias_cryptde = alias_cryptde(); + let subject = ProxyServer::new( + main_cryptde, + alias_cryptde, + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + ); + let add_return_route_message = AddReturnRouteMessage { + return_route_id: 0, + expected_services: vec![ + ExpectedService::Routing( + PublicKey::from(&b"key"[..]), + make_wallet("some wallet"), + rate_pack(10), + ), + ExpectedService::Exit( + PublicKey::from(&b"exit_key"[..]), + make_wallet("exit"), + rate_pack(11), + ), + ], + protocol: ProxyProtocol::HTTP, + server_name: None, + }; - ProxyServer::try_transmit_to_hopper( - cryptde.dup(), - &peer_actors.hopper.from_hopper_client, - SystemTime::now(), - route_result, - payload, - logger, - source_addr, - &peer_actors.dispatcher.from_dispatcher_client, - &peer_actors.accountant.report_exit_service_consumed, - &peer_actors.accountant.report_routing_service_consumed, - &peer_actors.proxy_server.add_return_route, - None, - ); + subject.report_response_services_consumed(&add_return_route_message, 1234, 3456); } #[test] @@ -2891,9 +2911,15 @@ mod tests { let hopper_mock = Recorder::new(); let hopper_log_arc = hopper_mock.get_recording(); let hopper_awaiter = hopper_mock.get_awaiter(); - let neighborhood_mock = Recorder::new().route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = Recorder::new().route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); let stream_key = make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let expected_data = tls_request.to_vec(); @@ -2907,7 +2933,7 @@ mod tests { data: expected_data.clone(), }; let expected_tls_request = PlainData::new(tls_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -2924,14 +2950,14 @@ mod tests { main_cryptde, route.clone(), expected_payload.into(), - alias_cryptde.public_key(), + &destination_key, ) .unwrap(); thread::spawn(move || { let mut subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -2971,9 +2997,15 @@ mod tests { let hopper_mock = Recorder::new(); let hopper_log_arc = hopper_mock.get_recording(); let hopper_awaiter = hopper_mock.get_awaiter(); - let neighborhood_mock = Recorder::new().route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = Recorder::new().route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); let stream_key = make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let expected_data = tls_request.to_vec(); @@ -2987,7 +3019,7 @@ mod tests { data: expected_data.clone(), }; let expected_tls_request = PlainData::new(tls_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -3004,14 +3036,14 @@ mod tests { main_cryptde, route.clone(), expected_payload.into(), - &alias_cryptde.public_key(), + &destination_key, ) .unwrap(); thread::spawn(move || { let mut subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3049,15 +3081,21 @@ mod tests { let hopper_mock = Recorder::new(); let hopper_log_arc = hopper_mock.get_recording(); let hopper_awaiter = hopper_mock.get_awaiter(); - let neighborhood_mock = Recorder::new().route_query_response(Some( - zero_hop_route_response(&main_cryptde.public_key(), main_cryptde), - )); - let stream_key = make_meaningless_stream_key(); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let destination_key = PublicKey::from(&b"our destination"[..]); + let neighborhood_mock = Recorder::new().route_query_response(Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + 1234, + ), + })); + let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let stream_key = StreamKey::new(main_cryptde.public_key().clone(), source_addr); let expected_data = tls_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), - peer_addr: socket_addr.clone(), + peer_addr: source_addr.clone(), reception_port: Some(TLS_PORT), sequence_number: Some(0), last_data: true, @@ -3065,7 +3103,7 @@ mod tests { data: expected_data.clone(), }; let expected_tls_request = PlainData::new(tls_request); - let route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde).route; + let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -3082,19 +3120,17 @@ mod tests { main_cryptde, route.clone(), expected_payload.into(), - alias_cryptde.public_key(), + &destination_key, ) .unwrap(); thread::spawn(move || { - let mut subject = ProxyServer::new( + let subject = ProxyServer::new( main_cryptde, alias_cryptde, - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); - subject.stream_key_factory = - Box::new(StreamKeyFactoryMock::new().make_result(stream_key.clone())); let system = System::new("proxy_server_receives_tls_client_hello_from_dispatcher_then_sends_cores_package_to_hopper"); let subject_addr: Addr = subject.start(); let mut peer_actors = peer_actors_builder() @@ -3159,7 +3195,7 @@ mod tests { let subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3199,7 +3235,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3244,7 +3280,6 @@ mod tests { System::current().stop(); system.run(); - let dispatcher_recording = dispatcher_recording_arc.lock().unwrap(); let record = dispatcher_recording.get_record::(0); assert_eq!(record.endpoint, Endpoint::Socket(socket_addr)); @@ -3259,11 +3294,11 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); - subject.subs = Some(ProxyServerOutSubs::default()); + subject.subs = Some(make_proxy_server_out_subs()); let stream_key = make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -3289,18 +3324,13 @@ mod tests { server_name: None, }, ); - let client_response_payload = ClientResponsePayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket::new(vec![], 1, true), }; - let (dispatcher_mock, _, _) = make_recorder(); - let peer_actors = peer_actors_builder().dispatcher(dispatcher_mock).build(); - subject.subs.as_mut().unwrap().dispatcher = peer_actors.dispatcher.from_dispatcher_client; - let expired_cores_package: ExpiredCoresPackage = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), @@ -3326,7 +3356,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3339,6 +3369,9 @@ mod tests { let incoming_route_d_wallet = make_wallet("D Earning"); let incoming_route_e_wallet = make_wallet("E Earning"); let incoming_route_f_wallet = make_wallet("F Earning"); + let rate_pack_d = rate_pack(101); + let rate_pack_e = rate_pack(102); + let rate_pack_f = rate_pack(103); subject.route_ids_to_return_routes.insert( 1234, AddReturnRouteMessage { @@ -3347,17 +3380,17 @@ mod tests { ExpectedService::Exit( irrelevant_public_key.clone(), incoming_route_d_wallet.clone(), - rate_pack(101), + rate_pack_d, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_e_wallet.clone(), - rate_pack(102), + rate_pack_e, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_f_wallet.clone(), - rate_pack(103), + rate_pack_f, ), ExpectedService::Nothing, ], @@ -3368,6 +3401,9 @@ mod tests { let incoming_route_g_wallet = make_wallet("G Earning"); let incoming_route_h_wallet = make_wallet("H Earning"); let incoming_route_i_wallet = make_wallet("I Earning"); + let rate_pack_g = rate_pack(104); + let rate_pack_h = rate_pack(105); + let rate_pack_i = rate_pack(106); subject.route_ids_to_return_routes.insert( 1235, AddReturnRouteMessage { @@ -3376,17 +3412,17 @@ mod tests { ExpectedService::Exit( irrelevant_public_key.clone(), incoming_route_g_wallet.clone(), - rate_pack(104), + rate_pack_g, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_h_wallet.clone(), - rate_pack(105), + rate_pack_h, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_i_wallet.clone(), - rate_pack(106), + rate_pack_i, ), ExpectedService::Nothing, ], @@ -3413,7 +3449,6 @@ mod tests { 0, ); let routing_size = first_expired_cores_package.payload_len; - let second_client_response_payload = ClientResponsePayload_0v1 { stream_key, sequenced_packet: SequencedPacket { @@ -3448,7 +3483,6 @@ mod tests { System::current().stop(); system.run(); - let after = SystemTime::now(); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); let record = dispatcher_recording.get_record::(0); @@ -3460,59 +3494,64 @@ mod tests { assert_eq!(record.last_data, false); assert_eq!(record.data, b"other data".to_vec()); let accountant_recording = accountant_recording_arc.lock().unwrap(); - - check_exit_report( - &accountant_recording, - 0, - before, - after, - &incoming_route_d_wallet, - first_exit_size, - rate_pack(101), - ); - check_routing_report( - &accountant_recording, - 1, - before, - after, - &incoming_route_e_wallet, - routing_size, - ); - check_routing_report( - &accountant_recording, - 2, - before, - after, - &incoming_route_f_wallet, - routing_size, + let first_report = accountant_recording.get_record::(0); + let first_report_timestamp = first_report.timestamp; + assert_eq!( + first_report, + &ReportServicesConsumedMessage { + timestamp: first_report_timestamp, + exit: ExitServiceConsumed { + earning_wallet: incoming_route_d_wallet, + payload_size: first_exit_size, + service_rate: rate_pack_d.exit_service_rate, + byte_rate: rate_pack_d.exit_byte_rate + }, + routing_payload_size: routing_size, + routing: vec![ + RoutingServiceConsumed { + earning_wallet: incoming_route_e_wallet, + service_rate: rate_pack_e.routing_service_rate, + byte_rate: rate_pack_e.routing_byte_rate + }, + RoutingServiceConsumed { + earning_wallet: incoming_route_f_wallet, + service_rate: rate_pack_f.routing_service_rate, + byte_rate: rate_pack_f.routing_byte_rate + } + ] + } ); + assert!(before <= first_report_timestamp && first_report_timestamp <= after); + let second_report = accountant_recording.get_record::(1); + let second_report_timestamp = second_report.timestamp; let routing_size = second_expired_cores_package.payload_len; - check_exit_report( - &accountant_recording, - 3, - before, - after, - &incoming_route_g_wallet, - second_exit_size, - rate_pack(104), - ); - check_routing_report( - &accountant_recording, - 4, - before, - after, - &incoming_route_h_wallet, - routing_size, - ); - check_routing_report( - &accountant_recording, - 5, - before, - after, - &incoming_route_i_wallet, - routing_size, + assert_eq!( + second_report, + &ReportServicesConsumedMessage { + timestamp: second_report_timestamp, + exit: ExitServiceConsumed { + earning_wallet: incoming_route_g_wallet, + payload_size: second_exit_size, + service_rate: rate_pack_g.exit_service_rate, + byte_rate: rate_pack_g.exit_byte_rate + }, + routing_payload_size: routing_size, + routing: vec![ + RoutingServiceConsumed { + earning_wallet: incoming_route_h_wallet, + service_rate: rate_pack_h.routing_service_rate, + byte_rate: rate_pack_h.routing_byte_rate + }, + RoutingServiceConsumed { + earning_wallet: incoming_route_i_wallet, + service_rate: rate_pack_i.routing_service_rate, + byte_rate: rate_pack_i.routing_byte_rate + } + ] + } ); - assert_eq!(accountant_recording.len(), 6); + assert!(before <= second_report_timestamp && second_report_timestamp <= after); + assert_eq!(accountant_recording.len(), 2); } #[test] @@ -3525,7 +3564,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3534,6 +3573,8 @@ mod tests { // subject.keys_and_addrs contains no browser stream let incoming_route_d_wallet = make_wallet("D Earning"); let incoming_route_e_wallet = make_wallet("E Earning"); + let rate_pack_d = rate_pack(101); + let rate_pack_e = rate_pack(102); subject.route_ids_to_return_routes.insert( 1234, AddReturnRouteMessage { @@ -3542,12 +3583,12 @@ mod tests { ExpectedService::Exit( irrelevant_public_key.clone(), incoming_route_d_wallet.clone(), - rate_pack(101), + rate_pack_d, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_e_wallet.clone(), - rate_pack(102), + rate_pack_e, ), ], protocol: ProxyProtocol::TLS, @@ -3590,55 +3631,53 @@ mod tests { let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); assert_eq!(dispatcher_recording.len(), 0); let accountant_recording = accountant_recording_arc.lock().unwrap(); - check_exit_report( - &accountant_recording, - 0, - before, - after, - &incoming_route_d_wallet, - exit_size, - rate_pack(101), - ); - check_routing_report( - &accountant_recording, - 1, - before, - after, - &incoming_route_e_wallet, - routing_size, + let services_consumed_report = + accountant_recording.get_record::(0); + let returned_timestamp = services_consumed_report.timestamp; + assert_eq!( + services_consumed_report, + &ReportServicesConsumedMessage { + timestamp: returned_timestamp, + exit: ExitServiceConsumed { + earning_wallet: incoming_route_d_wallet, + payload_size: exit_size, + service_rate: rate_pack_d.exit_service_rate, + byte_rate: rate_pack_d.exit_byte_rate + }, + routing_payload_size: routing_size, + routing: vec![RoutingServiceConsumed { + earning_wallet: incoming_route_e_wallet, + service_rate: rate_pack_e.routing_service_rate, + byte_rate: rate_pack_e.routing_byte_rate + }] + } ); - assert_eq!(accountant_recording.len(), 2); + assert!(before <= returned_timestamp && returned_timestamp <= after); + assert_eq!(accountant_recording.len(), 1); } #[test] fn handle_dns_resolve_failure_sends_message_to_dispatcher() { let system = System::new("proxy_server_receives_response_from_routing_services"); - let (dispatcher_mock, _, dispatcher_log_arc) = make_recorder(); - let cryptde = main_cryptde(); let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); let stream_key = make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - let exit_public_key = PublicKey::from(&b"exit_key"[..]); let exit_wallet = make_wallet("exit wallet"); - let subject_addr: Addr = subject.start(); - let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); - let expired_cores_package: ExpiredCoresPackage = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), @@ -3650,7 +3689,6 @@ mod tests { let mut peer_actors = peer_actors_builder().dispatcher(dispatcher_mock).build(); peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); subject_addr .try_send(AddReturnRouteMessage { @@ -3664,7 +3702,6 @@ mod tests { server_name: Some("server.com".to_string()), }) .unwrap(); - subject_addr.try_send(expired_cores_package).unwrap(); System::current().stop(); @@ -3694,7 +3731,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -3707,6 +3744,9 @@ mod tests { let incoming_route_d_wallet = make_wallet("D Earning"); let incoming_route_e_wallet = make_wallet("E Earning"); let incoming_route_f_wallet = make_wallet("F Earning"); + let rate_pack_d = rate_pack(101); + let rate_pack_e = rate_pack(102); + let rate_pack_f = rate_pack(103); subject.route_ids_to_return_routes.insert( 1234, AddReturnRouteMessage { @@ -3715,17 +3755,17 @@ mod tests { ExpectedService::Exit( irrelevant_public_key.clone(), incoming_route_d_wallet.clone(), - rate_pack(101), + rate_pack_d, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_e_wallet.clone(), - rate_pack(102), + rate_pack_e, ), ExpectedService::Routing( irrelevant_public_key.clone(), incoming_route_f_wallet.clone(), - rate_pack(103), + rate_pack_f, ), ExpectedService::Nothing, ], @@ -3733,10 +3773,8 @@ mod tests { server_name: Some("server.com".to_string()), }, ); - let subject_addr: Addr = subject.start(); let dns_resolve_failure_payload = DnsResolveFailure_0v1::new(stream_key); - let expired_cores_package: ExpiredCoresPackage = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), @@ -3746,7 +3784,6 @@ mod tests { 0, ); let routing_size = expired_cores_package.payload_len; - let mut peer_actors = peer_actors_builder().accountant(accountant).build(); peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); @@ -3758,59 +3795,57 @@ mod tests { System::current().stop(); system.run(); - let after = SystemTime::now(); let accountant_recording = accountant_recording_arc.lock().unwrap(); - check_exit_report( - &accountant_recording, - 0, - before, - after, - &incoming_route_d_wallet, - 0, - rate_pack(101), - ); - check_routing_report( - &accountant_recording, - 1, - before, - after, - &incoming_route_e_wallet, - routing_size, - ); - check_routing_report( - &accountant_recording, - 2, - before, - after, - &incoming_route_f_wallet, - routing_size, + let services_consumed_message = + accountant_recording.get_record::(0); + let returned_timestamp = services_consumed_message.timestamp; + assert_eq!( + services_consumed_message, + &ReportServicesConsumedMessage { + timestamp: returned_timestamp, + exit: ExitServiceConsumed { + earning_wallet: incoming_route_d_wallet, + payload_size: 0, + service_rate: rate_pack_d.exit_service_rate, + byte_rate: rate_pack_d.exit_byte_rate + }, + routing_payload_size: routing_size, + routing: vec![ + RoutingServiceConsumed { + earning_wallet: incoming_route_e_wallet, + service_rate: rate_pack_e.routing_service_rate, + byte_rate: rate_pack_e.routing_byte_rate + }, + RoutingServiceConsumed { + earning_wallet: incoming_route_f_wallet, + service_rate: rate_pack_f.routing_service_rate, + byte_rate: rate_pack_f.routing_byte_rate + } + ] + } ); - assert_eq!(accountant_recording.len(), 3); + assert!(before <= returned_timestamp && returned_timestamp <= after); + assert_eq!(accountant_recording.len(), 1); } #[test] fn handle_dns_resolve_failure_sends_message_to_neighborhood() { let system = System::new("test"); - let (neighborhood_mock, _, neighborhood_log_arc) = make_recorder(); - let cryptde = main_cryptde(); let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); - let stream_key = make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - let exit_public_key = PublicKey::from(&b"exit_key"[..]); let exit_wallet = make_wallet("exit wallet"); subject.route_ids_to_return_routes.insert( @@ -3826,11 +3861,8 @@ mod tests { server_name: Some("server.com".to_string()), }, ); - let subject_addr: Addr = subject.start(); - let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); - let expired_cores_package: ExpiredCoresPackage = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), @@ -3839,19 +3871,16 @@ mod tests { dns_resolve_failure.into(), 0, ); - let mut peer_actors = peer_actors_builder() .neighborhood(neighborhood_mock) .build(); peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - subject_addr.try_send(expired_cores_package).unwrap(); System::current().stop(); system.run(); - let neighborhood_recording = neighborhood_log_arc.lock().unwrap(); let record = neighborhood_recording.get_record::(0); assert_eq!( @@ -3864,26 +3893,21 @@ mod tests { fn handle_dns_resolve_failure_logs_when_stream_key_be_gone_but_server_name_be_not() { init_test_logging(); let system = System::new("test"); - let (neighborhood_mock, _, _) = make_recorder(); - let cryptde = main_cryptde(); let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); - let stream_key = make_meaningless_stream_key(); let return_route_id = 1234; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - let exit_public_key = PublicKey::from(&b"exit_key"[..]); let exit_wallet = make_wallet("exit wallet"); subject.route_ids_to_return_routes.insert( @@ -3899,11 +3923,8 @@ mod tests { server_name: Some("server.com".to_string()), }, ); - let subject_addr: Addr = subject.start(); - let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); - let expired_cores_package: ExpiredCoresPackage = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), @@ -3912,25 +3933,20 @@ mod tests { dns_resolve_failure.into(), 0, ); - let already_used_expired_cores_package = expired_cores_package.clone(); - let mut peer_actors = peer_actors_builder() .neighborhood(neighborhood_mock) .build(); peer_actors.proxy_server = ProxyServer::make_subs_from(&subject_addr); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); subject_addr.try_send(expired_cores_package).unwrap(); - subject_addr .try_send(already_used_expired_cores_package) .unwrap(); System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing( format!("Discarding DnsResolveFailure message for \"server.com\" from an unrecognized stream key {:?}", stream_key).as_str()); } @@ -3946,7 +3962,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -4019,11 +4035,11 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); - subject.subs = Some(ProxyServerOutSubs::default()); + subject.subs = Some(make_proxy_server_out_subs()); let peer_actors = peer_actors_builder() .neighborhood(neighborhood_mock) @@ -4085,7 +4101,7 @@ mod tests { let mut subject = ProxyServer::new( cryptde, alias_cryptde(), - false, + true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, ); @@ -4127,17 +4143,23 @@ mod tests { } #[test] - #[should_panic(expected = "Neighborhood unbound in ProxyServer")] + #[should_panic(expected = "Hopper unbound in ProxyServer")] fn panics_if_hopper_is_unbound() { let system = System::new("panics_if_hopper_is_unbound"); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; - let subject = ProxyServer::new(main_cryptde(), alias_cryptde(), false, None, false); + let subject = ProxyServer::new( + main_cryptde(), + alias_cryptde(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + ); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let expected_data = http_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), peer_addr: socket_addr.clone(), - reception_port: Some(53), + reception_port: Some(80), sequence_number: Some(0), last_data: false, is_clandestine: false, @@ -4661,6 +4683,186 @@ mod tests { assert!(!subject.tunneled_hosts.contains_key(&affected_stream_key)); } + #[test] + fn handle_stream_shutdown_msg_logs_errors_from_handling_normal_client_data() { + init_test_logging(); + let mut subject = ProxyServer::new(main_cryptde(), alias_cryptde(), true, Some(0), false); + let helper = IBCDHelperMock::default() + .handle_normal_client_data_result(Err("Our help is not welcome".to_string())); + subject.inbound_client_data_helper_opt = Some(Box::new(helper)); + let socket_addr = SocketAddr::from_str("3.4.5.6:7777").unwrap(); + let stream_key = StreamKey::new(main_cryptde().public_key().clone(), socket_addr); + subject.keys_and_addrs.insert(stream_key, socket_addr); + let msg = StreamShutdownMsg { + peer_addr: socket_addr, + stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { + reception_port: HTTP_PORT, + sequence_number: 1234, + }), + report_to_counterpart: true, + }; + + subject.handle_stream_shutdown_msg(msg); + + TestLogHandler::new().exists_log_containing("ERROR: ProxyServer: Our help is not welcome"); + } + + #[test] + fn stream_shutdown_msg_populates_correct_inbound_client_data_msg() { + let help_to_handle_normal_client_data_params_arc = Arc::new(Mutex::new(vec![])); + let mut subject = ProxyServer::new(main_cryptde(), alias_cryptde(), true, Some(0), false); + let icd_helper = IBCDHelperMock::default() + .handle_normal_client_data_params(&help_to_handle_normal_client_data_params_arc) + .handle_normal_client_data_result(Ok(())); + subject.inbound_client_data_helper_opt = Some(Box::new(icd_helper)); + let socket_addr = SocketAddr::from_str("3.4.5.6:7890").unwrap(); + let stream_key = StreamKey::new(main_cryptde().public_key().clone(), socket_addr); + subject.keys_and_addrs.insert(stream_key, socket_addr); + subject.stream_key_routes.insert( + stream_key, + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 0), + }, + ); + subject + .tunneled_hosts + .insert(stream_key, "blah".to_string()); + let msg = StreamShutdownMsg { + peer_addr: socket_addr, + stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { + reception_port: HTTP_PORT, + sequence_number: 1234, + }), + report_to_counterpart: true, + }; + let before = SystemTime::now(); + + subject.handle_stream_shutdown_msg(msg); + + let after = SystemTime::now(); + let handle_normal_client_data = + help_to_handle_normal_client_data_params_arc.lock().unwrap(); + let (inbound_client_data_msg, retire_stream_key) = &handle_normal_client_data[0]; + assert_eq!(inbound_client_data_msg.peer_addr, socket_addr); + assert_eq!(inbound_client_data_msg.data, Vec::::new()); + assert_eq!(inbound_client_data_msg.last_data, true); + assert_eq!(inbound_client_data_msg.is_clandestine, false); + let actual_timestamp = inbound_client_data_msg.timestamp; + assert!(before <= actual_timestamp && actual_timestamp <= after); + assert_eq!(*retire_stream_key, true) + } + + #[test] + fn help_to_handle_normal_client_data_missing_consuming_wallet_and_protocol_pack_not_found() { + let mut proxy_server = ProxyServer::new(main_cryptde(), alias_cryptde(), true, None, false); + proxy_server.subs = Some(make_proxy_server_out_subs()); + let inbound_client_data_msg = InboundClientData { + timestamp: SystemTime::now(), + peer_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), + reception_port: None, + last_data: true, + is_clandestine: false, + sequence_number: Some(123), + data: vec![], + }; + + let result = IBCDHelperReal {}.handle_normal_client_data( + &mut proxy_server, + inbound_client_data_msg, + true, + ); + + assert_eq!( + result, + Err("No origin port specified with 0-byte non-clandestine packet: []".to_string()) + ); + } + + #[test] + fn resolve_route_query_response_handles_error() { + init_test_logging(); + let recorder = Recorder::new(); + let addr = recorder.start(); + let add_route_msg_sub = recipient!(&addr, AddRouteMessage); + let logger = Logger::new("resolve_route_query_response_handles_error"); + let movable_tth_args = TTHMovableArgs { + common_opt: None, + logger, + hopper_sub: recipient!(&addr, IncipientCoresPackage), + dispatcher_sub: recipient!(&addr, TransmitDataMsg), + accountant_sub: recipient!(&addr, ReportServicesConsumedMessage), + add_return_route_sub: recipient!(&addr, AddReturnRouteMessage), + retire_stream_key_sub_opt: None, + }; + + IBCDHelperReal::resolve_route_query_response( + movable_tth_args, + add_route_msg_sub, + Err(MailboxError::Timeout), + ); + + TestLogHandler::new().exists_log_containing("ERROR: resolve_route_query_response_handles_error: Neighborhood refused to answer route request: MailboxError(Message delivery timed out)"); + } + + #[derive(Default)] + struct ClientRequestPayloadFactoryMock { + make_results: RefCell>>, + } + + impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryMock { + fn make( + &self, + _ibcd: &InboundClientData, + _stream_key: StreamKey, + _cryptde: &dyn CryptDE, + _logger: &Logger, + ) -> Option { + self.make_results.borrow_mut().remove(0) + } + } + + impl ClientRequestPayloadFactoryMock { + fn make_result(self, result: Option) -> Self { + self.make_results.borrow_mut().push(result); + self + } + } + + #[test] + fn help_to_handle_normal_client_data_make_payload_failed() { + let mut proxy_server = ProxyServer::new( + main_cryptde(), + alias_cryptde(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + ); + proxy_server.subs = Some(make_proxy_server_out_subs()); + proxy_server.client_request_payload_factory = + Box::new(ClientRequestPayloadFactoryMock::default().make_result(None)); + let inbound_client_data_msg = InboundClientData { + timestamp: SystemTime::now(), + peer_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), + reception_port: Some(568), + last_data: true, + is_clandestine: false, + sequence_number: Some(123), + data: vec![], + }; + + let result = IBCDHelperReal {}.handle_normal_client_data( + &mut proxy_server, + inbound_client_data_msg, + true, + ); + + assert_eq!( + result, + Err("Couldn't create ClientRequestPayload".to_string()) + ); + } + #[test] #[should_panic( expected = "ProxyServer should never get ShutdownStreamMsg about clandestine stream" @@ -4691,4 +4893,8 @@ mod tests { prove_that_crash_request_handler_is_hooked_up(proxy_server, CRASH_KEY); } + + fn make_exit_service_from_key(public_key: PublicKey) -> ExpectedService { + ExpectedService::Exit(public_key, make_wallet("exit wallet"), rate_pack(100)) + } } diff --git a/node/src/proxy_server/protocol_pack.rs b/node/src/proxy_server/protocol_pack.rs index 0d6b47ed9..73d2e3b82 100644 --- a/node/src/proxy_server/protocol_pack.rs +++ b/node/src/proxy_server/protocol_pack.rs @@ -5,7 +5,6 @@ use crate::sub_lib::cryptde::{PlainData, PublicKey}; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::proxy_server::ProxyProtocol; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; -use masq_lib::logger::Logger; #[derive(Clone, Debug, PartialEq, Eq)] pub struct Host { @@ -27,39 +26,33 @@ pub fn from_protocol(protocol: ProxyProtocol) -> Box { } } -pub fn from_standard_port(_standard_port: u16) -> Option> { - match _standard_port { +pub fn from_standard_port(standard_port: u16) -> Option> { + match standard_port { HTTP_PORT => Some(Box::new(HttpProtocolPack {})), TLS_PORT => Some(Box::new(TlsProtocolPack {})), _ => None, } } -pub fn from_ibcd(ibcd: &InboundClientData, logger: &Logger) -> Option> { +pub fn from_ibcd(ibcd: &InboundClientData) -> Result, String> { let origin_port = match ibcd.reception_port { None => { - error!( - logger, + return Err(format!( "No origin port specified with {}-byte non-clandestine packet: {:?}", ibcd.data.len(), ibcd.data - ); - return None; + )) } Some(origin_port) => origin_port, }; match from_standard_port(origin_port) { - Some(pp) => Some(pp), - None => { - error!( - logger, - "No protocol associated with origin port {} for {}-byte non-clandestine packet: {:?}", - origin_port, - ibcd.data.len(), - &ibcd.data - ); - None - } + Some(pp) => Ok(pp), + None => Err(format!( + "No protocol associated with origin port {} for {}-byte non-clandestine packet: {:?}", + origin_port, + ibcd.data.len(), + &ibcd.data + )), } } diff --git a/node/src/proxy_server/utils.rs b/node/src/proxy_server/utils.rs new file mode 100644 index 000000000..2beab87e7 --- /dev/null +++ b/node/src/proxy_server/utils.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. + +pub(in crate::proxy_server) mod local { + use crate::sub_lib::accountant::ReportServicesConsumedMessage; + use crate::sub_lib::cryptde::CryptDE; + use crate::sub_lib::dispatcher::StreamShutdownMsg; + use crate::sub_lib::hopper::IncipientCoresPackage; + use crate::sub_lib::proxy_server::{AddReturnRouteMessage, ClientRequestPayload_0v1}; + use crate::sub_lib::stream_handler_pool::TransmitDataMsg; + use actix::Recipient; + use masq_lib::logger::Logger; + use masq_lib::utils::ExpectValue; + use std::net::SocketAddr; + use std::time::SystemTime; + + pub struct TTHCommonArgs { + pub cryptde: &'static dyn CryptDE, + pub payload: ClientRequestPayload_0v1, + pub source_addr: SocketAddr, + pub timestamp: SystemTime, + pub is_decentralized: bool, + } + + pub struct TTHLocalArgs<'a> { + pub common: TTHCommonArgs, + pub logger: &'a Logger, + pub retire_stream_key_sub_opt: Option<&'a Recipient>, + pub hopper_sub: &'a Recipient, + pub dispatcher_sub: &'a Recipient, + pub accountant_sub: &'a Recipient, + pub add_return_route_sub: &'a Recipient, + } + + pub struct TTHMovableArgs { + pub common_opt: Option, + pub logger: Logger, + pub retire_stream_key_sub_opt: Option>, + pub hopper_sub: Recipient, + pub dispatcher_sub: Recipient, + pub accountant_sub: Recipient, + pub add_return_route_sub: Recipient, + } + + impl From> for TTHMovableArgs { + fn from(args: TTHLocalArgs) -> Self { + Self { + common_opt: Some(args.common), + logger: args.logger.clone(), + retire_stream_key_sub_opt: args.retire_stream_key_sub_opt.cloned(), + hopper_sub: (*args.hopper_sub).clone(), + dispatcher_sub: (*args.dispatcher_sub).clone(), + accountant_sub: (*args.accountant_sub).clone(), + add_return_route_sub: (*args.add_return_route_sub).clone(), + } + } + } + + impl<'a> From<&'a mut TTHMovableArgs> for TTHLocalArgs<'a> { + fn from(args: &'a mut TTHMovableArgs) -> TTHLocalArgs<'a> { + Self { + common: args.common_opt.take().expectv("common args"), + logger: &args.logger, + retire_stream_key_sub_opt: args.retire_stream_key_sub_opt.as_ref(), + hopper_sub: &args.hopper_sub, + dispatcher_sub: &args.dispatcher_sub, + accountant_sub: &args.accountant_sub, + add_return_route_sub: &args.add_return_route_sub, + } + } + } +} diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index b80448b62..965a1a828 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -7,8 +7,11 @@ use actix::Message; use actix::Recipient; use lazy_static::lazy_static; use masq_lib::ui_gateway::NodeFromUiMessage; +#[cfg(test)] +use std::any::Any; use std::fmt::{Debug, Formatter}; use std::str::FromStr; +use std::sync::atomic::{AtomicU32, Ordering}; use std::time::{Duration, SystemTime}; lazy_static! { @@ -16,9 +19,7 @@ lazy_static! { // TODO: The consuming wallet should never be defaulted; it should always come in from a // (possibly-complicated) command-line parameter, or the bidirectional GUI. pub static ref TEMPORARY_CONSUMING_WALLET: Wallet = Wallet::from_str("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF").expect("Internal error"); -} - -lazy_static! { + pub static ref MSG_ID_INCREMENTER: AtomicU32 = AtomicU32::default(); pub static ref DEFAULT_PAYMENT_THRESHOLDS: PaymentThresholds = PaymentThresholds { debt_threshold_gwei: 1_000_000_000, maturity_threshold_sec: 1200, @@ -27,9 +28,6 @@ lazy_static! { threshold_interval_sec: 21600, unban_below_gwei: 500_000_000, }; -} - -lazy_static! { pub static ref DEFAULT_SCAN_INTERVALS: ScanIntervals = ScanIntervals { pending_payable_scan_interval: Duration::from_secs(600), payable_scan_interval: Duration::from_secs(600), @@ -80,8 +78,7 @@ pub struct AccountantSubs { pub start: Recipient, pub report_routing_service_provided: Recipient, pub report_exit_service_provided: Recipient, - pub report_routing_service_consumed: Recipient, - pub report_exit_service_consumed: Recipient, + pub report_services_consumed: Recipient, pub report_new_payments: Recipient, pub pending_payable_fingerprint: Recipient, pub report_transaction_receipts: Recipient, @@ -116,17 +113,22 @@ pub struct ReportExitServiceProvidedMessage { } #[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct ReportRoutingServiceConsumedMessage { +pub struct ReportServicesConsumedMessage { pub timestamp: SystemTime, + pub exit: ExitServiceConsumed, + pub routing_payload_size: usize, + pub routing: Vec, +} + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct RoutingServiceConsumed { pub earning_wallet: Wallet, - pub payload_size: usize, pub service_rate: u64, pub byte_rate: u64, } -#[derive(Clone, PartialEq, Eq, Debug, Message)] -pub struct ReportExitServiceConsumedMessage { - pub timestamp: SystemTime, +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct ExitServiceConsumed { pub earning_wallet: Wallet, pub payload_size: usize, pub service_rate: u64, @@ -139,18 +141,38 @@ pub struct FinancialStatistics { pub total_paid_receivable: u64, } +pub trait MessageIdGenerator { + fn id(&self) -> u32; + as_any_dcl!(); +} + +#[derive(Default)] +pub struct MessageIdGeneratorReal {} + +impl MessageIdGenerator for MessageIdGeneratorReal { + fn id(&self) -> u32 { + MSG_ID_INCREMENTER.fetch_add(1, Ordering::Relaxed) + } + as_any_impl!(); +} + #[cfg(test)] mod tests { use crate::sub_lib::accountant::{ - PaymentThresholds, ScanIntervals, DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, - DEFAULT_SCAN_INTERVALS, TEMPORARY_CONSUMING_WALLET, + MessageIdGenerator, MessageIdGeneratorReal, PaymentThresholds, ScanIntervals, + DEFAULT_EARNING_WALLET, DEFAULT_PAYMENT_THRESHOLDS, DEFAULT_SCAN_INTERVALS, + MSG_ID_INCREMENTER, TEMPORARY_CONSUMING_WALLET, }; use crate::sub_lib::wallet::Wallet; use crate::test_utils::recorder::{make_accountant_subs_from_recorder, Recorder}; use actix::Actor; use std::str::FromStr; + use std::sync::atomic::Ordering; + use std::sync::Mutex; use std::time::Duration; + static MSG_ID_GENERATOR_TEST_GUARD: Mutex<()> = Mutex::new(()); + #[test] fn constants_have_correct_values() { let default_earning_wallet_expected: Wallet = @@ -187,4 +209,29 @@ mod tests { assert_eq!(format!("{:?}", subject), "AccountantSubs"); } + + #[test] + fn msg_id_generator_increments_by_one_with_every_call() { + let _guard = MSG_ID_GENERATOR_TEST_GUARD.lock().unwrap(); + let subject = MessageIdGeneratorReal::default(); + + let id1 = subject.id(); + let id2 = subject.id(); + let id3 = subject.id(); + + assert_eq!(id2, id1 + 1); + assert_eq!(id3, id2 + 1) + } + + #[test] + fn msg_id_generator_wraps_around_max_value() { + let _guard = MSG_ID_GENERATOR_TEST_GUARD.lock().unwrap(); + MSG_ID_INCREMENTER.store(u32::MAX, Ordering::Relaxed); + let subject = MessageIdGeneratorReal::default(); + subject.id(); //this returns the previous, not the newly incremented + + let id = subject.id(); + + assert_eq!(id, 0) + } } diff --git a/node/src/sub_lib/utils.rs b/node/src/sub_lib/utils.rs index d0935417d..2d987b880 100644 --- a/node/src/sub_lib/utils.rs +++ b/node/src/sub_lib/utils.rs @@ -328,7 +328,7 @@ mod tests { fn handle_ui_crash_message_does_not_crash_if_not_crashable() { init_test_logging(); let mut logger = Logger::new("handle_ui_crash_message_does_not_crash_if_not_crashable"); - logger.set_level_for_a_test(Level::Info); + logger.set_level_for_test(Level::Info); let msg_body = UiCrashRequest { actor: "CRASHKEY".to_string(), panic_message: "Foiled again!".to_string(), diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index d7fdff3d4..0091ef7f4 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -12,10 +12,9 @@ use crate::daemon::DaemonBindMessage; use crate::neighborhood::gossip::Gossip_0v1; use crate::stream_messages::{AddStreamMsg, PoolBindMessage, RemoveStreamMsg}; use crate::sub_lib::accountant::AccountantSubs; -use crate::sub_lib::accountant::ReportExitServiceConsumedMessage; use crate::sub_lib::accountant::ReportExitServiceProvidedMessage; -use crate::sub_lib::accountant::ReportRoutingServiceConsumedMessage; use crate::sub_lib::accountant::ReportRoutingServiceProvidedMessage; +use crate::sub_lib::accountant::ReportServicesConsumedMessage; use crate::sub_lib::blockchain_bridge::{BlockchainBridgeSubs, SetDbPasswordMsg}; use crate::sub_lib::blockchain_bridge::{ReportAccountsPayable, SetGasPriceMsg}; use crate::sub_lib::configurator::{ConfiguratorSubs, NewPasswordMessage}; @@ -138,9 +137,8 @@ recorder_message_handler!(PoolBindMessage); recorder_message_handler!(ReceivedPayments); recorder_message_handler!(RemoveNeighborMessage); recorder_message_handler!(RemoveStreamMsg); -recorder_message_handler!(ReportExitServiceConsumedMessage); +recorder_message_handler!(ReportServicesConsumedMessage); recorder_message_handler!(ReportExitServiceProvidedMessage); -recorder_message_handler!(ReportRoutingServiceConsumedMessage); recorder_message_handler!(ReportRoutingServiceProvidedMessage); recorder_message_handler!(ScanError); recorder_message_handler!(SentPayable); @@ -415,8 +413,7 @@ pub fn make_accountant_subs_from_recorder(addr: &Addr) -> AccountantSu start: recipient!(addr, StartMessage), report_routing_service_provided: recipient!(addr, ReportRoutingServiceProvidedMessage), report_exit_service_provided: recipient!(addr, ReportExitServiceProvidedMessage), - report_routing_service_consumed: recipient!(addr, ReportRoutingServiceConsumedMessage), - report_exit_service_consumed: recipient!(addr, ReportExitServiceConsumedMessage), + report_services_consumed: recipient!(addr, ReportServicesConsumedMessage), report_new_payments: recipient!(addr, ReceivedPayments), pending_payable_fingerprint: recipient!(addr, PendingPayableFingerprint), report_transaction_receipts: recipient!(addr, ReportTransactionReceipts), diff --git a/port_exposer/Cargo.lock b/port_exposer/Cargo.lock index cc9e1fe56..f48e4922e 100644 --- a/port_exposer/Cargo.lock +++ b/port_exposer/Cargo.lock @@ -20,7 +20,7 @@ checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "port_exposer" -version = "0.6.1" +version = "0.6.2" dependencies = [ "default-net", ] diff --git a/port_exposer/Cargo.toml b/port_exposer/Cargo.toml index 8c056b437..38392d01f 100644 --- a/port_exposer/Cargo.toml +++ b/port_exposer/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "port_exposer" -version = "0.6.1" +version = "0.6.2" authors = ["Dan Wiebe ", "MASQ"] license = "GPL-3.0-only" copyright = "Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved."